Compare commits

..

1 Commits

Author SHA1 Message Date
Ammar Ahmed
10b495cdbe mobile: convert sheets with input to screens or simple modals 2025-06-25 13:45:01 +05:00
213 changed files with 2997 additions and 634030 deletions

View File

@@ -9,13 +9,7 @@ on:
- "packages/core/**"
# re-run workflow if workflow file changes
- ".github/workflows/core.tests.yml"
pull_request_target:
branches:
- "master"
paths:
- "packages/core/**"
# re-run workflow if workflow file changes
- ".github/workflows/core.tests.yml"
pull_request:
types:
- "ready_for_review"
- "opened"
@@ -23,21 +17,11 @@ on:
- "reopened"
jobs:
authorize:
environment: ${{ github.event_name == 'pull_request_target' &&
github.event.pull_request.head.repo.full_name != github.repository &&
'external' || 'internal' }}
runs-on: ubuntu-latest
steps:
- run: echo true
test:
needs: authorize
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache

View File

@@ -6,16 +6,10 @@ on:
branches:
- "master"
paths:
- "apps/desktop/**"
- "app/desktop/**"
# re-run workflow if workflow file changes
- ".github/workflows/desktop.tests.yml"
pull_request:
branches:
- "master"
paths:
- "apps/desktop/**"
# re-run workflow if workflow file changes
- ".github/workflows/desktop.tests.yml"
jobs:
build:

View File

@@ -10,12 +10,6 @@ on:
# re-run workflow if workflow file changes
- ".github/workflows/editor.tests.yml"
pull_request:
branches:
- "master"
paths:
- "packages/editor/**"
# re-run workflow if workflow file changes
- ".github/workflows/editor.tests.yml"
types:
- "ready_for_review"
- "opened"

View File

@@ -45,7 +45,7 @@ jobs:
- name: Docker Setup Buildx
uses: docker/setup-buildx-action@v3
with:
platforms: linux/amd64,linux/arm64
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8
- name: Log in to Docker Hub
uses: docker/login-action@v3
@@ -77,7 +77,7 @@ jobs:
context: apps/monograph
file: apps/monograph/Dockerfile
push: true
platforms: linux/amd64,linux/arm64
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8
tags: streetwriters/monograph:${{ steps.package_metadata.outputs.app_version }},streetwriters/monograph:latest
cache-from: streetwriters/monograph:latest

View File

@@ -9,13 +9,7 @@ on:
- "apps/web/**"
# re-run workflow if workflow file changes
- ".github/workflows/web.tests.yml"
pull_request_target:
branches:
- "master"
paths:
- "apps/web/**"
# re-run workflow if workflow file changes
- ".github/workflows/web.tests.yml"
pull_request:
types:
- "ready_for_review"
- "opened"
@@ -23,23 +17,12 @@ on:
- "reopened"
jobs:
authorize:
environment: ${{ github.event_name == 'pull_request_target' &&
github.event.pull_request.head.repo.full_name != github.repository &&
'external' || 'internal' }}
runs-on: ubuntu-latest
steps:
- run: echo true
build:
needs: authorize
name: Build
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
@@ -71,8 +54,6 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}
- name: Download build
uses: actions/download-artifact@v4

View File

@@ -24,7 +24,7 @@ Requirements:
Before you can do anything, you'll need to [install Node.js](https://nodejs.org/en/download/) v16 or later on your system.
1. `clone` the monorepo:
Once you have completed the setup, the first step is to `clone` the monorepo:
```bash
git clone https://github.com/streetwriters/notesnook.git
@@ -33,28 +33,19 @@ git clone https://github.com/streetwriters/notesnook.git
cd notesnook
```
2. Install dependencies:
Once you are inside the `./notesnook` directory, run the preparation step:
```bash
# this might take a while to complete
npm install
```
3. Run the webapp for desktop environment:
```bash
cd apps/web
npm run start:desktop
```
4. In a separate terminal session, run the desktop app from the root of the project:
Now you can finally start the desktop app for development:
```bash
npm run start:desktop
```
### Release mode
To run the app in release mode:
```bash

View File

@@ -17,226 +17,230 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { testCleanup, test } from "./test-override.js";
import { test } from "vitest";
import { harness } from "./utils.js";
import { writeFile } from "fs/promises";
import { Page } from "playwright";
import { gt, lt } from "semver";
import { describe } from "vitest";
test("update starts downloading if version is outdated", async ({
ctx: { page },
expect,
onTestFinished
}) => {
onTestFinished(testCleanup);
test("update starts downloading if version is outdated", async (t) => {
await harness(
t,
async ({ page }) => {
await page.waitForSelector("#authForm");
await page.waitForSelector("#authForm");
expect(
await page.getByRole("button", { name: "Create account" }).isVisible()
).toBe(true);
t.expect(
await page.getByRole("button", { name: "Create account" }).isVisible()
).toBe(true);
await page
.getByRole("button", { name: "Skip & go directly to the app" })
.click();
await page
.getByRole("button", { name: "Skip & go directly to the app" })
.click();
await skipDialog(page);
await skipDialog(page);
await page.waitForSelector(".ProseMirror");
await page.waitForSelector(".ProseMirror");
await page
.locator(".theme-scope-statusBar")
.getByRole("button", { name: /updating/i })
.waitFor({ state: "attached" });
});
test("update is only shown if version is outdated and auto updates are disabled", async ({
ctx,
expect,
onTestFinished
}) => {
onTestFinished(testCleanup);
await ctx.app.close();
await writeFile(
ctx.configPath,
JSON.stringify({
automaticUpdates: false
})
await page
.locator(".theme-scope-statusBar")
.getByRole("button", { name: /updating/i })
.waitFor({ state: "attached" });
},
{ version: "3.0.0" }
);
await ctx.relaunch();
const { page } = ctx;
await page.waitForSelector("#authForm");
expect(
await page.getByRole("button", { name: "Create account" }).isVisible()
).toBe(true);
await page
.getByRole("button", { name: "Skip & go directly to the app" })
.click();
await skipDialog(page);
await page.waitForSelector(".ProseMirror");
await page
.locator(".theme-scope-statusBar")
.getByRole("button", { name: /available/i })
.waitFor({ state: "attached" });
});
describe("update to stable if it is newer", () => {
test.scoped({ options: { version: "3.0.0-beta.0" } });
test("test", async ({ ctx, expect, onTestFinished }) => {
onTestFinished(testCleanup);
test("update is only shown if version is outdated and auto updates are disabled", async (t) => {
await harness(
t,
async (ctx) => {
await ctx.app.close();
await writeFile(
ctx.configPath,
JSON.stringify({
automaticUpdates: false
})
);
await ctx.app.close();
await writeFile(
ctx.configPath,
JSON.stringify({
automaticUpdates: false,
releaseTrack: "beta"
})
);
await ctx.relaunch();
await ctx.relaunch();
const { page } = ctx;
const { page } = ctx;
await page.waitForSelector("#authForm");
await page.waitForSelector("#authForm");
t.expect(
await page.getByRole("button", { name: "Create account" }).isVisible()
).toBe(true);
expect(
await page.getByRole("button", { name: "Create account" }).isVisible()
).toBe(true);
await page
.getByRole("button", { name: "Skip & go directly to the app" })
.click();
await page
.getByRole("button", { name: "Skip & go directly to the app" })
.click();
await skipDialog(page);
await skipDialog(page);
await page.waitForSelector(".ProseMirror");
await page.waitForSelector(".ProseMirror");
const updateButton = page
.locator(".theme-scope-statusBar")
.getByRole("button", { name: /available/i });
await updateButton.waitFor({ state: "visible" });
const content = await updateButton.textContent();
const version = content?.split(" ")?.[0] || "";
expect(gt(version, "3.0.0-beta.0")).toBe(true);
});
});
describe("update is not available if it latest stable version is older", () => {
test.scoped({ options: { version: "99.0.0-beta.0" } });
test("test", async ({ ctx, expect, onTestFinished }) => {
onTestFinished(testCleanup);
await ctx.app.close();
await writeFile(
ctx.configPath,
JSON.stringify({
automaticUpdates: false,
releaseTrack: "beta"
})
);
await ctx.relaunch();
const { page } = ctx;
await page.waitForSelector("#authForm");
expect(
await page.getByRole("button", { name: "Create account" }).isVisible()
).toBe(true);
await page
.getByRole("button", { name: "Skip & go directly to the app" })
.click();
await skipDialog(page);
await page.waitForSelector(".ProseMirror");
await page
.locator(".theme-scope-statusBar")
.getByRole("button", { name: /checking for updates/i })
.waitFor({ state: "hidden" });
expect(
await page
.locator(".theme-scope-statusBar")
.getByRole("button", { name: /available/i })
.isHidden()
).toBe(true);
});
.waitFor({ state: "attached" });
},
{ version: "3.0.0" }
);
});
describe("downgrade to stable on switching to stable release track", () => {
test.scoped({ options: { version: "99.0.0-beta.0" } });
test("test", async ({ ctx, expect, onTestFinished }) => {
onTestFinished(testCleanup);
test("update to stable if it is newer", async (t) => {
await harness(
t,
async (ctx) => {
await ctx.app.close();
await writeFile(
ctx.configPath,
JSON.stringify({
automaticUpdates: false,
releaseTrack: "beta"
})
);
await ctx.app.close();
await writeFile(
ctx.configPath,
JSON.stringify({
automaticUpdates: false,
releaseTrack: "stable"
})
);
await ctx.relaunch();
await ctx.relaunch();
const { page } = ctx;
const { page } = ctx;
await page.waitForSelector("#authForm");
await page.waitForSelector("#authForm");
t.expect(
await page.getByRole("button", { name: "Create account" }).isVisible()
).toBe(true);
expect(
await page.getByRole("button", { name: "Create account" }).isVisible()
).toBe(true);
await page
.getByRole("button", { name: "Skip & go directly to the app" })
.click();
await page
.getByRole("button", { name: "Skip & go directly to the app" })
.click();
await skipDialog(page);
await skipDialog(page);
await page.waitForSelector(".ProseMirror");
await page.waitForSelector(".ProseMirror");
const updateButton = page
.locator(".theme-scope-statusBar")
.getByRole("button", { name: /available/i });
await updateButton.waitFor({ state: "visible" });
const content = await updateButton.textContent();
const version = content?.split(" ")?.[0] || "";
t.expect(gt(version, "3.0.0-beta.0")).toBe(true);
},
{ version: "3.0.0-beta.0" }
);
});
await page
.locator(".theme-scope-statusBar")
.getByRole("button", { name: /checking for updates/i })
.waitFor({ state: "hidden" });
test("update is not available if it latest stable version is older", async (t) => {
await harness(
t,
async (ctx) => {
await ctx.app.close();
await writeFile(
ctx.configPath,
JSON.stringify({
automaticUpdates: false,
releaseTrack: "beta"
})
);
const updateButton = page
.locator(".theme-scope-statusBar")
.getByRole("button", { name: /available/i });
await updateButton.waitFor({ state: "visible" });
const content = await updateButton.textContent();
const version = content?.split(" ")?.[0] || "";
expect(lt(version, "99.0.0-beta.0")).toBe(true);
});
await ctx.relaunch();
const { page } = ctx;
await page.waitForSelector("#authForm");
t.expect(
await page.getByRole("button", { name: "Create account" }).isVisible()
).toBe(true);
await page
.getByRole("button", { name: "Skip & go directly to the app" })
.click();
await skipDialog(page);
await page.waitForSelector(".ProseMirror");
await page
.locator(".theme-scope-statusBar")
.getByRole("button", { name: /checking for updates/i })
.waitFor({ state: "hidden" });
t.expect(
await page
.locator(".theme-scope-statusBar")
.getByRole("button", { name: /available/i })
.isHidden()
).toBe(true);
},
{ version: "99.0.0-beta.0" }
);
});
test("downgrade to stable on switching to stable release track", async (t) => {
await harness(
t,
async (ctx) => {
await ctx.app.close();
await writeFile(
ctx.configPath,
JSON.stringify({
automaticUpdates: false,
releaseTrack: "stable"
})
);
await ctx.relaunch();
const { page } = ctx;
await page.waitForSelector("#authForm");
t.expect(
await page.getByRole("button", { name: "Create account" }).isVisible()
).toBe(true);
await page
.getByRole("button", { name: "Skip & go directly to the app" })
.click();
await skipDialog(page);
await page.waitForSelector(".ProseMirror");
await page
.locator(".theme-scope-statusBar")
.getByRole("button", { name: /checking for updates/i })
.waitFor({ state: "hidden" });
const updateButton = page
.locator(".theme-scope-statusBar")
.getByRole("button", { name: /available/i });
await updateButton.waitFor({ state: "visible" });
const content = await updateButton.textContent();
const version = content?.split(" ")?.[0] || "";
t.expect(lt(version, "99.0.0-beta.0")).toBe(true);
},
{ version: "99.0.0-beta.0" }
);
});
async function skipDialog(page: Page) {
try {
const dialog = page.locator(".ReactModal__Content");
const positiveButton = dialog.locator(
"button[data-role='positive-button']"
);
const negativeButton = dialog.locator(
"button[data-role='negative-button']"
);
if (await positiveButton.isVisible())
await positiveButton.click({ timeout: 1000 });
else if (await negativeButton.isVisible())
await negativeButton.click({ timeout: 1000 });
} catch (e) {
// ignore error
}
await page
.waitForSelector(".ReactModal__Content", {
timeout: 1000
})
.catch(() => {})
.then(async () => {
const positiveButton = page.locator(
"button[data-role='positive-button']"
);
const negativeButton = page.locator(
"button[data-role='negative-button']"
);
if (await positiveButton.isVisible()) await positiveButton.click();
else if (await negativeButton.isVisible()) await negativeButton.click();
});
}

View File

@@ -1,24 +0,0 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { buildApp } from "./utils";
export default async function setup() {
await buildApp();
}

View File

@@ -17,24 +17,22 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { testCleanup, test } from "./test-override.js";
import { test } from "vitest";
import { harness } from "./utils.js";
import assert from "assert";
test("make sure app loads", async ({
ctx: { page },
expect,
onTestFinished
}) => {
onTestFinished(testCleanup);
test("make sure app loads", async (t) => {
await harness(t, async ({ page }) => {
await page.waitForSelector("#authForm");
await page.waitForSelector("#authForm");
assert.ok(
await page.getByRole("button", { name: "Create account" }).isVisible()
);
expect(
await page.getByRole("button", { name: "Create account" }).isVisible()
).toBe(true);
await page
.getByRole("button", { name: "Skip & go directly to the app" })
.click();
await page
.getByRole("button", { name: "Skip & go directly to the app" })
.click();
await page.waitForSelector(".ProseMirror");
await page.waitForSelector(".ProseMirror");
});
});

View File

@@ -1,64 +0,0 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { test as vitestTest, TestContext } from "vitest";
import { buildAndLaunchApp, Fixtures, TestOptions } from "./utils";
import { mkdir, rm } from "fs/promises";
import path from "path";
import slugify from "slugify";
export const test = vitestTest.extend<Fixtures>({
options: { version: "3.0.0" } as TestOptions,
ctx: async ({ options }, use) => {
const ctx = await buildAndLaunchApp(options);
await use(ctx);
}
});
export async function testCleanup(context: TestContext) {
const ctx = (context.task.context as unknown as Fixtures).ctx;
if (context.task.result?.state === "fail") {
await mkdir("test-results", { recursive: true });
await ctx.page.screenshot({
path: path.join(
"test-results",
`${slugify(context.task.name)}-${process.platform}-${
process.arch
}-error.png`
)
});
}
await ctx.app.close();
await rm(ctx.userDataDir, {
force: true,
recursive: true,
maxRetries: 3,
retryDelay: 5000
}).catch(() => {
/*ignore */
});
await rm(ctx.outputDir, {
force: true,
recursive: true,
maxRetries: 3,
retryDelay: 5000
}).catch(() => {
/*ignore */
});
}

View File

@@ -18,233 +18,144 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { execSync } from "child_process";
import { cp, readFile, writeFile } from "fs/promises";
import { mkdir } from "fs/promises";
import { fileURLToPath } from "node:url";
import path, { join, resolve } from "path";
import path from "path";
import { _electron as electron } from "playwright";
import { existsSync } from "fs";
import slugify from "slugify";
import { TaskContext } from "vitest";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const IS_DEBUG = process.env.NN_DEBUG === "true" || process.env.CI === "true";
const productName = `NotesnookTestHarness`;
const SOURCE_DIR = resolve("output", productName);
export interface AppContext {
interface AppContext {
app: import("playwright").ElectronApplication;
page: import("playwright").Page;
configPath: string;
userDataDir: string;
outputDir: string;
relaunch: () => Promise<void>;
}
export interface TestOptions {
interface TestOptions {
version: string;
}
export interface Fixtures {
options: TestOptions;
ctx: AppContext;
export async function harness(
t: TaskContext,
cb: (ctx: AppContext) => Promise<void>,
options?: TestOptions
) {
const ctx = await buildAndLaunchApp(options);
t.onTestFinished(async (result) => {
if (result.state === "fail") {
await mkdir("test-results", { recursive: true });
await ctx.page.screenshot({
path: path.join(
"test-results",
`${slugify(t.task.name)}-${process.platform}-${
process.arch
}-error.png`
)
});
}
await ctx.app.close();
});
await cb(ctx);
}
export async function buildAndLaunchApp(
options?: TestOptions
): Promise<AppContext> {
const productName = `notesnooktest${makeid(10)}`;
const outputDir = path.join("test-artifacts", `${productName}-output`);
const executablePath = await copyBuild({
...options,
outputDir
});
const { app, page, configPath, userDataDir } = await launchApp(
executablePath,
productName
);
async function buildAndLaunchApp(options?: TestOptions): Promise<AppContext> {
const productName = makeid(10);
const executablePath = await buildApp({ ...options, productName });
const { app, page, configPath } = await launchApp(executablePath);
const ctx: AppContext = {
app,
page,
configPath,
userDataDir,
outputDir,
relaunch: async () => {
const { app, page, configPath, userDataDir } = await launchApp(
executablePath,
productName
);
const { app, page, configPath } = await launchApp(executablePath);
ctx.app = app;
ctx.page = page;
ctx.userDataDir = userDataDir;
ctx.configPath = configPath;
}
};
return ctx;
}
async function launchApp(executablePath: string, packageName: string) {
const userDataDir = resolve(
__dirname,
"..",
"test-artifacts",
"user_data_dirs",
packageName
);
async function launchApp(executablePath: string) {
const app = await electron.launch({
executablePath,
args: IS_DEBUG ? [] : ["--hidden"],
env: {
...(process.platform === "linux"
env:
process.platform === "linux"
? {
...(process.env as Record<string, string>),
APPIMAGE: "true"
}
: (process.env as Record<string, string>)),
CUSTOM_USER_DATA_DIR: userDataDir
}
: (process.env as Record<string, string>)
});
const page = await app.firstWindow();
const configPath = path.join(userDataDir, "UserData", "config.json");
const userDataDirectory = await app.evaluate((a) => {
return a.app.getPath("userData");
});
const configPath = path.join(userDataDirectory, "config.json");
return {
app,
page,
configPath,
userDataDir
configPath
};
}
let MAX_RETRIES = 3;
export async function buildApp(version?: string) {
if (!existsSync(SOURCE_DIR)) {
const args = [
"electron-builder",
"--dir",
`--${process.arch}`,
`--config electron-builder.config.js`,
`--c.extraMetadata.productName=${productName}`,
`--c.compression=store`,
"--publish=never"
];
if (version) args.push(`--c.extraMetadata.version=${version}`);
try {
execSync(`npx ${args.join(" ")}`, {
stdio: IS_DEBUG ? "inherit" : "ignore",
env: {
...process.env,
NOTESNOOK_STAGING: "true",
NN_PRODUCT_NAME: productName,
NN_APP_ID: `com.notesnook.test.${productName}`,
NN_OUTPUT_DIR: SOURCE_DIR
}
});
} catch (e) {
if (--MAX_RETRIES) {
console.log("retrying...");
return await buildApp(version);
} else throw e;
}
}
}
async function copyBuild({
async function buildApp({
version,
outputDir
productName
}: {
version?: string;
outputDir: string;
productName: string;
}) {
return process.platform === "win32"
? await makeBuildCopyWindows(outputDir, productName, version)
: process.platform === "darwin"
? await makeBuildCopyMacOS(outputDir, productName, version)
: await makeBuildCopyLinux(outputDir, productName, version);
}
async function makeBuildCopyLinux(
outputDir: string,
productName: string,
version?: string
) {
const platformDir =
process.arch === "arm64" ? "linux-arm64-unpacked" : "linux-unpacked";
const appDir = await makeBuildCopy(
outputDir,
platformDir,
"resources",
version
);
return resolve(
__dirname,
"..",
appDir,
productName.toLowerCase().replace(/\s+/g, "-")
);
}
async function makeBuildCopyWindows(
outputDir: string,
productName: string,
version?: string
) {
const platformDir =
process.arch === "arm64" ? "win-arm64-unpacked" : "win-unpacked";
const appDir = await makeBuildCopy(
outputDir,
platformDir,
"resources",
version
);
return resolve(__dirname, "..", appDir, `${productName}.exe`);
}
async function makeBuildCopyMacOS(
outputDir: string,
productName: string,
version?: string
) {
const platformDir = process.arch === "arm64" ? "mac-arm64" : "mac";
const appDir = await makeBuildCopy(
outputDir,
platformDir,
join(`${productName}.app`, "Contents", "Resources"),
version
);
return resolve(
__dirname,
"..",
appDir,
`${productName}.app`,
"Contents",
"MacOS",
productName
);
}
async function makeBuildCopy(
outputDir: string,
platformDir: string,
resourcesDir: string,
version?: string
) {
const appDir = outputDir;
await cp(join(SOURCE_DIR, platformDir), outputDir, {
recursive: true,
preserveTimestamps: true,
verbatimSymlinks: true,
dereference: false,
force: true
const buildRoot = path.join("test-artifacts", `${productName}-build`);
const output = path.join("test-artifacts", `${productName}-output`);
execSync(`npm run release -- --root ${buildRoot} --skip-tsc-build`, {
stdio: IS_DEBUG ? "inherit" : "ignore"
});
const packageJsonPath = join(appDir, resourcesDir, "app", "package.json");
const args = [
`--config electron-builder.config.js`,
`--c.extraMetadata.productName=${productName}`,
"--publish=never"
];
if (version) args.push(`--c.extraMetadata.version=${version}`);
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf-8"));
if (version) {
packageJson.version = version;
await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
}
execSync(`npx electron-builder --dir --${process.arch} ${args.join(" ")}`, {
stdio: IS_DEBUG ? "inherit" : "ignore",
env: {
...process.env,
NOTESNOOK_STAGING: "true",
NN_BUILD_ROOT: buildRoot,
NN_PRODUCT_NAME: productName,
NN_APP_ID: `com.notesnook.test.${productName}`,
NN_OUTPUT_DIR: output
}
});
return appDir;
return path.join(
__dirname,
"..",
output,
process.platform === "linux"
? process.arch === "arm64"
? "linux-arm64-unpacked"
: "linux-unpacked"
: process.platform === "darwin"
? process.arch === "arm64"
? `mac-arm64/${productName}.app/Contents/MacOS/`
: `mac/${productName}.app/Contents/MacOS/`
: "win-unpacked",
process.platform === "win32" ? `${productName}.exe` : productName
);
}
function makeid(length: number) {

File diff suppressed because it is too large Load Diff

View File

@@ -2,15 +2,13 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "3.2.4",
"version": "3.2.2",
"appAppleId": "1544027013",
"private": true,
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/types/index.d.ts",
"sideEffects": [
"src/overrides.ts"
],
"sideEffects": false,
"exports": {
".": {
"require": {
@@ -37,7 +35,7 @@
"electron-updater": "^6.6.2",
"icojs": "^0.19.5",
"sqlite-better-trigram": "0.0.3",
"sqlite3-fts5-html": "^0.0.4",
"sqlite3-fts5-html": "^0.0.3",
"typed-emitter": "^2.1.0",
"yargs": "^17.7.2",
"zod": "3.24.3"
@@ -57,7 +55,7 @@
"slugify": "1.6.6",
"tree-kill": "^1.2.2",
"undici": "^7.8.0",
"vitest": "^3.2.4"
"vitest": "2.1.8"
},
"optionalDependencies": {
"dmg-license": "^1.0.11"

View File

@@ -17,7 +17,6 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import "./overrides";
import { app, BrowserWindow, nativeTheme, shell } from "electron";
import { isDevelopment } from "./utils";
import { registerProtocol, PROTOCOL_URL } from "./utils/protocol";

View File

@@ -1,35 +0,0 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { app } from "electron";
import path from "path";
if (process.env.CUSTOM_USER_DATA_DIR) {
app.setPath(
"appData",
path.join(process.env.CUSTOM_USER_DATA_DIR, "AppData")
);
app.setPath(
"userData",
path.join(process.env.CUSTOM_USER_DATA_DIR, "UserData")
);
app.setPath(
"documents",
path.join(process.env.CUSTOM_USER_DATA_DIR, "Documents")
);
}

View File

@@ -21,13 +21,11 @@ import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
testTimeout: 120 * 1000,
hookTimeout: 120 * 1000,
testTimeout: process.env.CI ? 120 * 1000 : 120 * 1000,
sequence: {
concurrent: true,
shuffle: true
},
globalSetup: "./__tests__/global-setup.ts",
dir: "./__tests__/",
exclude: [
"**/node_modules/**",

View File

@@ -137,7 +137,6 @@ export async function deleteFile(
export async function clearFileStorage() {
try {
await createCacheDir();
const files = await RNFetchBlob.fs.ls(cacheDir);
const oldCache = await RNFetchBlob.fs.ls(cacheDirOld);
@@ -222,7 +221,6 @@ export async function deleteCacheFileByName(name: string) {
}
export async function deleteDCacheFiles() {
await createCacheDir();
const files = await RNFetchBlob.fs.ls(cacheDir);
for (const file of files) {
if (file.includes("_dcache") || file.startsWith("NN_")) {

View File

@@ -33,9 +33,6 @@ export function hideAuth() {
eSendEvent(eCloseLoginDialog);
if (initialAuthMode.current === AuthMode.welcomeSignup) {
Navigation.replace("FluidPanelsView");
setTimeout(() => {
Navigation.resetRootState();
}, 1000);
} else {
Navigation.goBack();
}

View File

@@ -89,9 +89,6 @@ const Auth = ({ navigation, route }) => {
onPress={() => {
if (initialAuthMode.current === 2) {
Navigation.replace("FluidPanelsView");
setTimeout(() => {
Navigation.resetRootState();
}, 1000);
} else {
Navigation.goBack();
}

View File

@@ -36,7 +36,6 @@ import { getElevationStyle } from "../../utils/elevation";
import { AppFontSize, normalize } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import { hexToRGBA, RGB_Linear_Shade } from "../../utils/colors";
import useKeyboard from "../../hooks/use-keyboard";
interface FloatingButtonProps {
onPress: () => void;

View File

@@ -21,7 +21,7 @@ import React from "react";
import { Text, View, ViewStyle } from "react-native";
import { useThemeColors } from "@notesnook/theme";
import { AppFontSize } from "../../utils/size";
import { Button, ButtonProps } from "../ui/button";
import { Button } from "../ui/button";
import { PressableProps } from "../ui/pressable";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
@@ -31,7 +31,13 @@ type DialogHeaderProps = {
icon?: string;
title?: string;
paragraph?: string;
button?: ButtonProps;
button?: {
onPress?: () => void;
loading?: boolean;
title?: string;
type?: PressableProps["type"];
icon?: string;
};
paragraphColor?: string;
padding?: number;
centered?: boolean;
@@ -89,14 +95,17 @@ const DialogHeader = ({
{button ? (
<Button
onPress={button.onPress}
style={{
borderRadius: 100,
paddingHorizontal: DefaultAppStyles.GAP
}}
loading={button.loading}
fontSize={13}
title={button.title}
icon={button.icon}
type={button.type || "secondary"}
height={30}
{...button}
/>
) : null}
</View>

View File

@@ -17,15 +17,12 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { Dimensions, TextInput, View } from "react-native";
import Orientation from "react-native-orientation-locker";
import Pdf from "react-native-pdf";
import { MMKV } from "../../../common/database/mmkv";
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
import downloadAttachment from "../../../common/filesystem/download-attachment";
import { deleteCacheFileByPath, exists } from "../../../common/filesystem/io";
import { cacheDir } from "../../../common/filesystem/utils";
import { useAttachmentProgress } from "../../../hooks/use-attachment-progress";
import useGlobalSafeAreaInsets from "../../../hooks/use-global-safe-area-insets";
@@ -33,9 +30,8 @@ import {
eSubscribeEvent,
eUnSubscribeEvent
} from "../../../services/event-manager";
import { useThemeColors } from "@notesnook/theme";
import { AppFontSize } from "../../../utils/size";
import { DefaultAppStyles } from "../../../utils/styles";
import { sleep } from "../../../utils/time";
import { Dialog } from "../../dialog";
import BaseDialog from "../../dialog/base-dialog";
import { presentDialog } from "../../dialog/functions";
@@ -43,6 +39,11 @@ import SheetProvider from "../../sheet-provider";
import { IconButton } from "../../ui/icon-button";
import { ProgressBarComponent } from "../../ui/svg/lazy";
import Paragraph from "../../ui/typography/paragraph";
import { sleep } from "../../../utils/time";
import { MMKV } from "../../../common/database/mmkv";
import { deleteCacheFileByPath } from "../../../common/filesystem/io";
import { strings } from "@notesnook/intl";
import { DefaultAppStyles } from "../../../utils/styles";
const WIN_WIDTH = Dimensions.get("window").width;
const WIN_HEIGHT = Dimensions.get("window").height;
@@ -104,19 +105,15 @@ const PDFPreview = () => {
const open = useCallback(
async (attachment) => {
setVisible(true);
setLoading(true);
setTimeout(async () => {
setAttachment(attachment);
let hash = attachment.hash;
if (!hash) return;
if (!(await exists(hash))) setLoading(true);
const uri = await downloadAttachment(hash, false, {
silent: true,
cache: true
});
if (!(await exists(hash))) {
setVisible(false);
return;
}
const path = `${cacheDir}/${uri}`;
snapshotValue.current = snapshot.current;
setPDFSource("file://" + path);
@@ -169,7 +166,8 @@ const PDFPreview = () => {
}}
>
{loading ? (
<View
<Animated.View
exiting={FadeOut}
style={{
flex: 1,
justifyContent: "center",
@@ -190,7 +188,7 @@ const PDFPreview = () => {
>
{strings.loadingWithProgress(progress?.percent)}
</Paragraph>
</View>
</Animated.View>
) : (
<>
<View
@@ -272,42 +270,49 @@ const PDFPreview = () => {
</View>
</View>
{pdfSource ? (
<Pdf
source={{
uri: pdfSource
}}
ref={pdfRef}
onLoadComplete={(numberOfPages) => {
setNumPages(numberOfPages);
}}
onPageChanged={(page) => {
setCurrentPage(page);
inputRef.current?.setNativeProps({
text: page + ""
});
saveSnapshot({
currentPage: page,
scale: snapshot?.current?.scale
});
}}
// scale={snapshotValue.current?.scale}
// onScaleChanged={(scale) => {
// saveSnapshot({
// currentPage: snapshot?.current?.currentPage,
// scale: scale
// });
// }}
page={snapshotValue?.current?.currentPage}
password={password}
maxScale={6}
onError={onError}
onPressLink={(uri) => {}}
<Animated.View
style={{
flex: 1,
width: width,
height: Dimensions.get("window").height
flex: 1
}}
/>
entering={FadeIn}
>
<Pdf
source={{
uri: pdfSource
}}
ref={pdfRef}
onLoadComplete={(numberOfPages) => {
setNumPages(numberOfPages);
}}
onPageChanged={(page) => {
setCurrentPage(page);
inputRef.current?.setNativeProps({
text: page + ""
});
saveSnapshot({
currentPage: page,
scale: snapshot?.current?.scale
});
}}
// scale={snapshotValue.current?.scale}
// onScaleChanged={(scale) => {
// saveSnapshot({
// currentPage: snapshot?.current?.currentPage,
// scale: scale
// });
// }}
page={snapshotValue?.current?.currentPage}
password={password}
maxScale={6}
onError={onError}
onPressLink={(uri) => {}}
style={{
flex: 1,
width: width,
height: Dimensions.get("window").height
}}
/>
</Animated.View>
) : null}
</>
)}

View File

@@ -30,7 +30,6 @@ import { BackHandler, Platform, ViewProps } from "react-native";
import { Gesture, GestureDetector } from "react-native-gesture-handler";
import Animated, {
runOnJS,
SharedValue,
useAnimatedReaction,
useAnimatedStyle,
useSharedValue,
@@ -68,7 +67,6 @@ export interface TabsRef {
setScrollEnabled: () => true;
isDrawerOpen: () => boolean;
node: RefObject<Animated.View>;
tabChangedFromSwipeAction: SharedValue<boolean>;
}
export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
@@ -118,7 +116,6 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
const isLoaded = useRef(false);
const prevWidths = useRef(widths);
const isIPhone = Platform.OS === "ios";
const tabChangedFromSwipeAction = useSharedValue(false);
useEffect(() => {
if (deviceMode === "tablet" || fullscreen) {
@@ -194,7 +191,6 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
: withTiming(editorPosition);
currentTab.value = 2;
}
tabChangedFromSwipeAction.value = false;
},
goToIndex: (index: number, animated = true) => {
if (deviceMode === "tablet") {
@@ -214,7 +210,6 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
: editorPosition;
currentTab.value = 2;
}
tabChangedFromSwipeAction.value = false;
},
unlock: () => {
forcedLock.value = false;
@@ -257,8 +252,7 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
},
page: () => (currentTab.value === 1 ? "home" : "editor"),
setScrollEnabled: () => true,
node: node,
tabChangedFromSwipeAction: tabChangedFromSwipeAction
node: node
}),
[
currentTab,
@@ -268,8 +262,7 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
homePosition,
editorPosition,
forcedLock,
isDrawerOpen,
tabChangedFromSwipeAction
isDrawerOpen
]
);
@@ -369,20 +362,18 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
isDrawerOpen.value = true;
currentTab.value = 1;
runOnJS(onDrawerStateChange)(true);
tabChangedFromSwipeAction.value = true;
return;
} else if (!isSwipeLeft && finalValue > 100) {
translateX.value = withSpring(homePosition, animationConfig);
isDrawerOpen.value = false;
currentTab.value = 1;
runOnJS(onDrawerStateChange)(false);
tabChangedFromSwipeAction.value = true;
return;
} else if (!isSwipeLeft && finalValue < 100) {
translateX.value = withSpring(0, animationConfig);
isDrawerOpen.value = true;
currentTab.value = 1;
tabChangedFromSwipeAction.value = true;
return;
}
}
@@ -393,8 +384,6 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
translateX.value = withSpring(editorPosition, animationConfig);
currentTab.value = 2;
isDrawerOpen.value = false;
tabChangedFromSwipeAction.value = true;
return;
}
}
@@ -409,8 +398,6 @@ export const FluidPanels = forwardRef<TabsRef, TabProps>(function FluidTabs(
translateX.value = withSpring(editorPosition, animationConfig);
currentTab.value = 2;
}
tabChangedFromSwipeAction.value = true;
return;
}

View File

@@ -17,26 +17,24 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Reminder } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { notesnook } from "../../../../e2e/test.ids";
import useIsSelected from "../../../hooks/use-selected";
import AddReminder from "../../../screens/add-reminder";
import { eSendEvent } from "../../../services/event-manager";
import { useSelectionStore } from "../../../stores/use-selection-store";
import { eCloseSheet } from "../../../utils/events";
import { AppFontSize, defaultBorderRadius } from "../../../utils/size";
import { DefaultAppStyles } from "../../../utils/styles";
import { defaultBorderRadius, AppFontSize } from "../../../utils/size";
import { Properties } from "../../properties";
import AppIcon from "../../ui/AppIcon";
import ReminderSheet from "../../sheets/reminder";
import { IconButton } from "../../ui/icon-button";
import { ReminderTime } from "../../ui/reminder-time";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import SelectionWrapper, { selectItem } from "../selection-wrapper";
import { strings } from "@notesnook/intl";
import { useSelectionStore } from "../../../stores/use-selection-store";
import useIsSelected from "../../../hooks/use-selected";
import AppIcon from "../../ui/AppIcon";
import { DefaultAppStyles } from "../../../utils/styles";
const ReminderItem = React.memo(
({
@@ -51,10 +49,8 @@ const ReminderItem = React.memo(
const { colors } = useThemeColors();
const openReminder = () => {
if (selectItem(item)) return;
AddReminder.present(item, undefined);
if (isSheet) {
eSendEvent(eCloseSheet);
}
ReminderSheet.present(item, undefined, isSheet);
};
const selectionMode = useSelectionStore((state) => state.selectionMode);
const [selected] = useIsSelected(item);

View File

@@ -153,6 +153,7 @@ export const SearchResult = (props: SearchResultProps) => {
for (let i = 0; i <= index; i++) {
activeIndex += props.item.content[i].length;
}
console.log(activeIndex);
openNote(activeIndex);
}}
>

View File

@@ -73,6 +73,9 @@ const ColorItem = ({ item, note }: { item: Color; note: Note }) => {
testID={notesnook.ids.dialogs.actionsheet.color(item.colorCode)}
key={item.id}
onPress={toggleColor}
onLongPress={(event) => {
NativeTooltip.show(event, item.title, NativeTooltip.POSITIONS.TOP);
}}
style={{
width: 35,
height: 35,

View File

@@ -21,14 +21,13 @@ import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { View } from "react-native";
import { FlatList } from "react-native-actions-sheet";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../common/database";
import { DDS } from "../../services/device-detection";
import { eSendEvent, presentSheet } from "../../services/event-manager";
import { ColorValues } from "../../utils/colors";
import { eOnLoadNote } from "../../utils/events";
import { fluidTabsRef } from "../../utils/global-refs";
import { AppFontSize } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import SheetProvider from "../sheet-provider";
import { IconButton } from "../ui/icon-button";
import { Pressable } from "../ui/pressable";
@@ -39,6 +38,8 @@ import { DateMeta } from "./date-meta";
import { Items } from "./items";
import Notebooks from "./notebooks";
import { TagStrip, Tags } from "./tags";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { DefaultAppStyles } from "../../utils/styles";
const Line = ({ top = 6, bottom = 6 }) => {
const { colors } = useThemeColors();
@@ -150,8 +151,7 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
) : null}
</View>
{(item.type === "notebook" || item.type === "reminder") &&
item.description ? (
{item.type === "notebook" && item.description ? (
<Paragraph>{item.description}</Paragraph>
) : null}

View File

@@ -65,9 +65,7 @@ const COLUMN_BAR_ITEMS: ActionId[] = [
"edit-notebook",
"move-notes",
"move-notebook",
"edit-reminder",
"pin",
"disable-reminder",
"default-notebook",
"default-tag",
"default-homepage",

View File

@@ -29,10 +29,6 @@ import { ColorTags } from "./color-tags";
import { strings } from "@notesnook/intl";
import { DefaultAppStyles } from "../../utils/styles";
import ManageTags from "../../screens/manage-tags";
import {
paddingTop,
paddingVertical
} from "deprecated-react-native-prop-types/DeprecatedLayoutPropTypes";
export const Tags = ({ item, close }) => {
const { colors } = useThemeColors();
@@ -90,6 +86,7 @@ export const TagStrip = ({ item, close }) => {
flexDirection: "row",
flexWrap: "wrap",
alignItems: "center",
marginTop: DefaultAppStyles.GAP_VERTICAL,
gap: 5
}}
>
@@ -110,13 +107,16 @@ const TagItem = ({ tag, close }) => {
const style = {
paddingHorizontal: 0,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
borderRadius: 100,
marginTop: 0,
backgroundColor: "transparent"
};
return (
<Button
onPress={onPress}
title={"#" + tag.title}
type="plain"
height={20}
fontSize={AppFontSize.xs}
style={style}
textStyle={{

View File

@@ -197,6 +197,16 @@ export const SelectionHeader = React.memo(
}
]
: [
{
title: strings.move(),
onPress: async () => {
const ids = selectedItemsList;
const notebooks = await db.notebooks.all.items(ids);
MoveNotebook.present(notebooks);
},
visible: renderedInRoute === "Notebooks",
icon: "arrow-right-bold-box-outline"
},
{
title: strings.manageTags(),
onPress: async () => {

View File

@@ -95,6 +95,8 @@ const SheetProvider = ({ context = "global" }) => {
[context]
);
console.log(data?.keyboardHandlerDisabled);
return !visible || !data ? null : (
<SheetWrapper
fwdRef={actionSheetRef}

View File

@@ -310,7 +310,6 @@ const ExportNotesSheet = ({
ToastManager.error(e as Error);
});
} else {
await sleep(500);
FileViewer.open(result?.filePath, {
showOpenWithDialog: true,
showAppsSuggestions: true
@@ -339,7 +338,6 @@ const ExportNotesSheet = ({
.getState()
.setAppDidEnterBackgroundForAction(true);
if (Platform.OS === "ios") {
await sleep(500);
Share.open({
url: result?.fileDir + result.fileName
}).catch(() => {

View File

@@ -38,9 +38,10 @@ import { eCloseSheet, eOnNotebookUpdated } from "../../../utils/events";
import { AppFontSize } from "../../../utils/size";
import { DefaultAppStyles } from "../../../utils/styles";
import { sleep } from "../../../utils/time";
import { Dialog } from "../../dialog";
import { Properties } from "../../properties";
import SheetProvider from "../../sheet-provider";
import { NotebookItem } from "../../side-menu/notebook-item";
import { useSideMenuNotebookTreeStore } from "../../side-menu/stores";
import { IconButton } from "../../ui/icon-button";
import Paragraph from "../../ui/typography/paragraph";
import { AddNotebookSheet } from "../add-notebook";
@@ -59,8 +60,13 @@ export const Notebooks = (props: {
close?: (ctx?: string) => void;
}) => {
const tree = useNotebookTreeStore((state) => state.tree);
const [isLoading, setIsLoading] = useState(true);
const { colors } = useThemeColors();
const [notebooks, setNotebooks] = useState<Notebook[]>();
const [filteredNotebooks, setFilteredNotebooks] =
React.useState<VirtualizedGrouping<Notebook>>();
const searchTimer = React.useRef<NodeJS.Timeout>();
const lastQuery = React.useRef<string>();
const loadRootNotebooks = React.useCallback(async () => {
const notebooks = await db.relations
.from(
@@ -89,6 +95,7 @@ export const Notebooks = (props: {
useEffect(() => {
(async () => {
loadRootNotebooks();
setIsLoading(false);
})();
}, [loadRootNotebooks]);
@@ -144,7 +151,7 @@ export const Notebooks = (props: {
height: 400
}}
>
<Dialog context="local" />
<SheetProvider context="local" />
<View
style={{
@@ -168,7 +175,7 @@ export const Notebooks = (props: {
}}
name="plus"
onPress={() => {
AddNotebookSheet.present(undefined, props.rootNotebook, "local");
AddNotebookSheet.present(props.rootNotebook, undefined, "local");
}}
/>
</View>
@@ -237,18 +244,20 @@ const NotebookItemWrapper = React.memo(
const onItemUpdate = React.useCallback(async () => {
const notebook = await db.notebooks.notebook(item.notebook.id);
if (notebook) {
useNotebookTreeStore.getState().updateItem(item.notebook.id, notebook);
useSideMenuNotebookTreeStore
.getState()
.updateItem(item.notebook.id, notebook);
if (expanded) {
useNotebookTreeStore
useSideMenuNotebookTreeStore
.getState()
.setTree(
await useNotebookTreeStore
await useSideMenuNotebookTreeStore
.getState()
.fetchAndAdd(item.notebook.id, item.depth + 1)
);
}
} else {
useNotebookTreeStore.getState().removeItem(item.notebook.id);
useSideMenuNotebookTreeStore.getState().removeItem(item.notebook.id);
}
}, [expanded, item.depth, item.notebook.id]);
@@ -265,15 +274,17 @@ const NotebookItemWrapper = React.memo(
onToggleExpanded={async () => {
useNotebookExpandedStore.getState().setExpanded(item.notebook.id);
if (!expanded) {
useNotebookTreeStore
useSideMenuNotebookTreeStore
.getState()
.setTree(
await useNotebookTreeStore
await useSideMenuNotebookTreeStore
.getState()
.fetchAndAdd(item.notebook.id, item.depth + 1)
);
} else {
useNotebookTreeStore.getState().removeChildren(item.notebook.id);
useSideMenuNotebookTreeStore
.getState()
.removeChildren(item.notebook.id);
}
}}
selected={selected}

View File

@@ -34,7 +34,7 @@ import { AppFontSize } from "../../../utils/size";
import DialogHeader from "../../dialog/dialog-header";
import List from "../../list";
import SheetProvider from "../../sheet-provider";
import { Button, ButtonProps } from "../../ui/button";
import { Button } from "../../ui/button";
import { PressableProps } from "../../ui/pressable";
import Paragraph from "../../ui/typography/paragraph";
import { DefaultAppStyles } from "../../../utils/styles";
@@ -47,10 +47,18 @@ type RelationsListProps = {
referenceType: string;
relationType: "to" | "from";
title: string;
button?: ButtonProps;
button?: Button;
onAdd: () => void;
};
type Button = {
onPress?: (() => void) | undefined;
loading?: boolean | undefined;
title?: string | undefined;
type?: PressableProps["type"];
icon?: string;
};
const IconsByType = {
reminder: "bell"
};
@@ -146,7 +154,7 @@ RelationsList.present = ({
referenceType: string;
relationType: "to" | "from";
title: string;
button?: ButtonProps;
button?: Button;
onAdd: () => void;
}) => {
presentSheet({

View File

@@ -0,0 +1,630 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import React, { RefObject, useRef, useState } from "react";
import {
Platform,
TextInput,
View,
ScrollView as RNScrollView
} from "react-native";
import { ActionSheetRef, ScrollView } from "react-native-actions-sheet";
import DateTimePickerModal from "react-native-modal-datetime-picker";
import {
PresentSheetOptions,
ToastManager,
presentSheet
} from "../../../services/event-manager";
import { defaultBorderRadius, AppFontSize } from "../../../utils/size";
import { Button } from "../../ui/button";
import Input from "../../ui/input";
import dayjs from "dayjs";
import DatePicker from "react-native-date-picker";
import { db } from "../../../common/database";
import { DDS } from "../../../services/device-detection";
import Navigation from "../../../services/navigation";
import Notifications from "../../../services/notifications";
import PremiumService from "../../../services/premium";
import SettingsService from "../../../services/settings";
import { useRelationStore } from "../../../stores/use-relation-store";
import { Dialog } from "../../dialog";
import { ReminderTime } from "../../ui/reminder-time";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { Note, Reminder } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { DefaultAppStyles } from "../../../utils/styles";
type ReminderSheetProps = {
actionSheetRef: RefObject<ActionSheetRef>;
close?: (ctx?: string) => void;
update?: (options: PresentSheetOptions) => void;
reminder?: Reminder;
reference?: Note;
};
const ReminderModes =
Platform.OS === "ios"
? {
Once: "once",
Repeat: "repeat"
}
: {
Once: "once",
Repeat: "repeat",
Permanent: "permanent"
};
const RecurringModes = {
Daily: "day",
Week: "week",
Month: "month",
Year: "year"
};
const WeekDays = new Array(7).fill(true);
const MonthDays = new Array(31).fill(true);
const WeekDayNames = {
0: "Sunday",
1: "Monday",
2: "Tuesday",
3: "Wednesday",
4: "Thursday",
5: "Friday",
6: "Saturday"
};
const ReminderNotificationModes = {
Silent: "silent",
Vibrate: "vibrate",
Urgent: "urgent"
};
export default function ReminderSheet({
actionSheetRef,
close,
update,
reminder,
reference
}: ReminderSheetProps) {
const { colors, isDark } = useThemeColors();
const [reminderMode, setReminderMode] = useState<Reminder["mode"]>(
reminder?.mode || "once"
);
const [recurringMode, setRecurringMode] = useState<Reminder["recurringMode"]>(
reminder?.recurringMode || "week"
);
const [selectedDays, setSelectedDays] = useState<number[]>(
reminder?.selectedDays || []
);
const [date, setDate] = useState<Date>(
new Date(reminder?.date || Date.now())
);
const [reminderNotificationMode, setReminderNotificatioMode] = useState<
Reminder["priority"]
>(reminder?.priority || SettingsService.get().reminderNotificationMode);
const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
const [repeatFrequency, setRepeatFrequency] = useState(1);
const referencedItem = reference ? (reference as Note) : null;
const title = useRef<string | undefined>(
!reminder ? referencedItem?.title : reminder?.title
);
const details = useRef<string | undefined>(
!reminder ? referencedItem?.headline : reminder?.description
);
const titleRef = useRef<TextInput>(null);
const timer = useRef<NodeJS.Timeout>();
const showDatePicker = () => {
setDatePickerVisibility(true);
};
const hideDatePicker = () => {
setDatePickerVisibility(false);
};
const handleConfirm = (date: Date) => {
timer.current = setTimeout(() => {
hideDatePicker();
setDate(date);
}, 50);
};
function nth(n: number) {
return (
["st", "nd", "rd"][(((((n < 0 ? -n : n) + 90) % 100) - 10) % 10) - 1] ||
"th"
);
}
function getSelectedDaysText(selectedDays: number[]) {
const text = selectedDays
.sort((a, b) => a - b)
.map((day, index) => {
const isLast = index === selectedDays.length - 1;
const isSecondLast = index === selectedDays.length - 2;
const joinWith = isSecondLast ? " & " : isLast ? "" : ", ";
return recurringMode === RecurringModes.Week
? WeekDayNames[day as keyof typeof WeekDayNames] + joinWith
: `${day}${nth(day)} ${joinWith}`;
})
.join("");
return text;
}
async function saveReminder() {
try {
if (!(await Notifications.checkAndRequestPermissions(true)))
throw new Error(strings.noNotificationPermission());
if (!date && reminderMode !== ReminderModes.Permanent) return;
if (
reminderMode === ReminderModes.Repeat &&
recurringMode !== "day" &&
recurringMode !== "year" &&
selectedDays.length === 0
)
throw new Error(strings.selectDayError());
if (!title.current) throw new Error(strings.setTitleError());
if (date.getTime() < Date.now() && reminderMode === "once") {
titleRef?.current?.focus();
throw new Error(strings.dateError());
}
date.setSeconds(0, 0);
const reminderId = await db.reminders?.add({
id: reminder?.id,
date: date?.getTime(),
priority: reminderNotificationMode,
title: title.current,
description: details.current,
recurringMode: recurringMode,
selectedDays: selectedDays,
mode: reminderMode,
localOnly: reminderMode === "permanent",
snoozeUntil:
date?.getTime() > Date.now() ? undefined : reminder?.snoozeUntil,
disabled: false
});
if (!reminderId) return;
const _reminder = await db.reminders?.reminder(reminderId);
if (reference && _reminder) {
await db.relations?.add(reference, {
id: _reminder?.id as string,
type: _reminder?.type
});
}
Notifications.scheduleNotification(_reminder as Reminder);
Navigation.queueRoutesForUpdate();
useRelationStore.getState().update();
close?.();
} catch (e) {
ToastManager.error(e as Error, undefined, "local");
}
}
return (
<View
style={{
paddingHorizontal: DefaultAppStyles.GAP,
maxHeight: "100%"
}}
>
<Heading size={AppFontSize.lg}>
{reminder ? strings.editReminder() : strings.newReminder()}
</Heading>
<Dialog context="local" />
<ScrollView
bounces={false}
style={{
marginBottom: DDS.isTab ? 25 : undefined
}}
>
<Input
fwdRef={titleRef}
defaultValue={reminder?.title || referencedItem?.title}
placeholder={strings.remindeMeOf()}
onChangeText={(text) => (title.current = text)}
wrapperStyle={{
marginTop: DefaultAppStyles.GAP_VERTICAL
}}
/>
<Input
defaultValue={
reminder ? reminder?.description : referencedItem?.headline
}
placeholder={strings.addShortNote()}
onChangeText={(text) => (details.current = text)}
containerStyle={{
maxHeight: 80
}}
multiline
textAlignVertical="top"
inputStyle={{
minHeight: 80,
paddingVertical: DefaultAppStyles.GAP_VERTICAL
}}
height={80}
wrapperStyle={{
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
/>
<ScrollView
style={{
flexDirection: "row",
marginBottom: DefaultAppStyles.GAP_VERTICAL,
height: 50
}}
horizontal
>
{Object.keys(ReminderModes).map((mode) => (
<Button
key={mode}
title={strings.reminderModes(
ReminderModes[mode as keyof typeof ReminderModes] as string
)}
style={{
marginRight: 12,
borderRadius: 100,
minWidth: 70,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
}}
proTag={mode === "Repeat"}
height={35}
type={
reminderMode ===
ReminderModes[mode as keyof typeof ReminderModes]
? "selected"
: "plain"
}
onPress={() => {
if (mode === "Repeat" && !PremiumService.get()) return;
setReminderMode(
ReminderModes[
mode as keyof typeof ReminderModes
] as Reminder["mode"]
);
if (mode === "Repeat") {
setSelectedDays((days) => {
if (days.length > 0) return days;
if (days.indexOf(date.getDay()) > -1) {
return days;
}
days.push(date.getDay());
return [...days];
});
}
}}
/>
))}
</ScrollView>
{reminderMode === ReminderModes.Repeat ? (
<View
style={{
backgroundColor: colors.secondary.background,
padding: DefaultAppStyles.GAP,
borderRadius: defaultBorderRadius,
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
>
<View
style={{
flexDirection: "row",
marginBottom:
recurringMode === "day" || recurringMode === "year" ? 0 : 12,
alignItems: "center"
}}
>
{Object.keys(RecurringModes).map((mode) => (
<Button
key={mode}
title={strings.recurringModes(
RecurringModes[mode as keyof typeof RecurringModes]
)}
style={{
marginRight: 6,
borderRadius: 100,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
}}
type={
recurringMode ===
RecurringModes[mode as keyof typeof RecurringModes]
? "selected"
: "plain"
}
onPress={() => {
setRecurringMode(
RecurringModes[
mode as keyof typeof RecurringModes
] as Reminder["recurringMode"]
);
setSelectedDays([]);
setRepeatFrequency(1);
}}
/>
))}
</View>
<RNScrollView showsHorizontalScrollIndicator={false} horizontal>
{recurringMode === RecurringModes.Daily ||
recurringMode === RecurringModes.Year
? null
: recurringMode === RecurringModes.Week
? WeekDays.map((item, index) => (
<Button
key={strings.weekDayNamesShort[
index as keyof typeof strings.weekDayNamesShort
]()}
title={strings.weekDayNamesShort[
index as keyof typeof strings.weekDayNamesShort
]()}
type={
selectedDays.indexOf(index) > -1 ? "selected" : "plain"
}
fontSize={AppFontSize.xs}
style={{
height: 40,
borderRadius: 100,
marginRight: 10
}}
onPress={() => {
setSelectedDays((days) => {
if (days.indexOf(index) > -1) {
days.splice(days.indexOf(index), 1);
return [...days];
}
days.push(index);
return [...days];
});
}}
/>
))
: MonthDays.map((item, index) => (
<Button
key={index + "monthday"}
title={index + 1 + ""}
type={
selectedDays.indexOf(index + 1) > -1
? "selected"
: "plain"
}
fontSize={AppFontSize.xs}
style={{
height: 40,
borderRadius: 100,
marginRight: 10
}}
onPress={() => {
setSelectedDays((days) => {
if (days.indexOf(index + 1) > -1) {
days.splice(days.indexOf(index + 1), 1);
return [...days];
}
days.push(index + 1);
return [...days];
});
}}
/>
))}
</RNScrollView>
</View>
) : null}
{reminderMode === ReminderModes.Permanent ? null : (
<View
style={{
width: "100%",
flexDirection: "column",
justifyContent: "center",
marginBottom: DefaultAppStyles.GAP_VERTICAL,
alignItems: "center"
}}
>
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="date"
onConfirm={handleConfirm}
onCancel={hideDatePicker}
is24Hour={db.settings.getTimeFormat() === "24-hour"}
date={date || new Date(Date.now())}
/>
<DatePicker
date={date}
maximumDate={dayjs(date).add(3, "months").toDate()}
onDateChange={handleConfirm}
textColor={isDark ? colors.static.white : colors.static.black}
fadeToColor={colors.primary.background}
theme={isDark ? "dark" : "light"}
androidVariant="nativeAndroid"
is24hourSource="locale"
locale={
db.settings?.getTimeFormat() === "24-hour" ? "en_GB" : "en_US"
}
mode={
reminderMode === ReminderModes.Repeat &&
recurringMode !== "year"
? "time"
: "datetime"
}
/>
{reminderMode === ReminderModes.Repeat ? null : (
<Button
style={{
width: "100%"
}}
title={date ? date.toLocaleDateString() : strings.selectDate()}
type={date ? "secondaryAccented" : "secondary"}
icon="calendar"
fontSize={AppFontSize.sm}
onPress={() => {
showDatePicker();
}}
/>
)}
</View>
)}
{reminderMode === ReminderModes.Once ||
reminderMode === ReminderModes.Permanent ? null : (
<View
style={{
borderRadius: defaultBorderRadius,
flexDirection: "row",
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
alignItems: "center",
justifyContent: "flex-start",
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
>
<>
<Paragraph
size={AppFontSize.xxs}
color={colors.secondary.paragraph}
>
{recurringMode === RecurringModes.Daily
? strings.reminderRepeatStrings.day(
dayjs(date).format("hh:mm A")
)
: recurringMode === RecurringModes.Year
? strings.reminderRepeatStrings.year(
dayjs(date).format("dddd, MMMM D, h:mm A")
)
: selectedDays.length === 7 &&
recurringMode === RecurringModes.Week
? strings.reminderRepeatStrings.week.daily(
dayjs(date).format("hh:mm A")
)
: selectedDays.length === 0
? strings.reminderRepeatStrings[
recurringMode as "week" | "month"
].selectDays()
: strings.reminderRepeatStrings.repeats(
repeatFrequency,
recurringMode as string,
getSelectedDaysText(selectedDays),
dayjs(date).format("hh:mm A")
)}
</Paragraph>
</>
</View>
)}
<ReminderTime
reminder={reminder}
style={{
width: "100%",
justifyContent: "flex-start",
borderWidth: 0,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
alignSelf: "flex-start"
}}
/>
{reminderMode === ReminderModes.Permanent ? null : (
<RNScrollView
style={{
flexDirection: "row",
marginTop: DefaultAppStyles.GAP_VERTICAL,
height: 50
}}
horizontal
>
{Object.keys(ReminderNotificationModes).map((mode) => (
<Button
key={mode}
title={strings.reminderNotificationModes(
mode as keyof typeof ReminderNotificationModes
)}
style={{
marginRight: 12,
borderRadius: 100,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
}}
icon={
mode === "Silent"
? "minus-circle"
: mode === "Vibrate"
? "vibrate"
: "volume-high"
}
height={35}
type={
reminderNotificationMode ===
ReminderNotificationModes[
mode as keyof typeof ReminderNotificationModes
]
? "selected"
: "plain"
}
onPress={() => {
const _mode = ReminderNotificationModes[
mode as keyof typeof ReminderNotificationModes
] as Reminder["priority"];
SettingsService.set({
reminderNotificationMode: _mode
});
setReminderNotificatioMode(_mode);
}}
/>
))}
</RNScrollView>
)}
</ScrollView>
<Button
title={strings.save()}
type="accent"
style={{
paddingHorizontal: DefaultAppStyles.GAP * 2,
marginTop: DefaultAppStyles.GAP_VERTICAL,
width: "100%"
}}
onPress={saveReminder}
/>
</View>
);
}
ReminderSheet.present = (
reminder?: Reminder,
reference?: Note,
isSheet?: boolean
) => {
presentSheet({
context: isSheet ? "local" : undefined,
component: (ref, close, update) => (
<ReminderSheet
actionSheetRef={ref}
close={close}
update={update}
reminder={reminder}
reference={reference}
/>
)
});
};

View File

@@ -40,7 +40,6 @@ import Sync from "../../../services/sync";
import Clipboard from "@react-native-clipboard/clipboard";
import { logoutUser } from "../../../screens/settings/logout";
import { sleep } from "../../../utils/time";
export const UserSheet = () => {
const ref = useSheetRef();
const { colors } = useThemeColors();
@@ -311,10 +310,10 @@ export const UserSheet = () => {
{
icon: "logout",
title: strings.logout(),
onPress: async () => {
ref.current?.hide();
await sleep(300);
onPress: () => {
console.log("logout");
logoutUser();
ref.current?.hide();
},
hidden: !user
}

View File

@@ -211,14 +211,6 @@ const TabBar = (
const ids = useSideMenuNotebookSelectionStore
.getState()
.getSelectedItemIds();
if (!ids.length) {
ToastManager.show({
context: "local",
type: "error",
message: strings.noNotebooksSelectedToMove()
});
return;
}
const notebooks = await db.notebooks.all.items(ids);
Navigation.navigate("MoveNotebook", {
selectedNotebooks: notebooks

View File

@@ -49,8 +49,7 @@ export const NotebookItem = ({
onPress,
onLongPress,
onAddNotebook,
canDisableSelectionMode,
disableExpand
canDisableSelectionMode
}: {
index: number;
item: TreeItem;
@@ -65,7 +64,6 @@ export const NotebookItem = ({
onLongPress?: () => void;
onAddNotebook?: () => void;
canDisableSelectionMode?: boolean;
disableExpand?: boolean;
}) => {
const notebook = item.notebook;
const isFocused = focused;
@@ -168,7 +166,7 @@ export const NotebookItem = ({
}
testID={item.hasChildren ? `expand-notebook-${index}` : ""}
onPress={() => {
if (item.hasChildren && !disableExpand) {
if (item.hasChildren) {
onToggleExpanded?.();
} else {
onPress?.();
@@ -184,7 +182,7 @@ export const NotebookItem = ({
borderRadius: defaultBorderRadius
}}
name={
!item.hasChildren || disableExpand
!item.hasChildren
? "book-outline"
: expanded
? "chevron-down"

View File

@@ -20,9 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { Notebook } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import { FlashList } from "@shopify/flash-list";
import React, { useEffect, useState } from "react";
import { TextInput, View } from "react-native";
import { FlatList, TextInput, View } from "react-native";
import { db } from "../../common/database";
import NotebookScreen from "../../screens/notebook";
import Navigation from "../../services/navigation";
@@ -43,7 +42,6 @@ import {
useSideMenuNotebookSelectionStore.setState({
multiSelect: true
});
export const SideMenuNotebooks = () => {
const tree = useSideMenuNotebookTreeStore((state) => state.tree);
const [notebooks, loading] = useNotebooks();
@@ -66,19 +64,13 @@ export const SideMenuNotebooks = () => {
const updateNotebooks = React.useCallback(() => {
if (lastQuery.current) {
// useSideMenuNotebookTreeStore.setState({
// isSearching: true
// });
db.lookup
.notebooks(lastQuery.current)
.sorted(db.settings.getGroupOptions("notebooks"))
.sorted()
.then((filtered) => {
setFilteredNotebooks(filtered);
});
} else {
// useSideMenuNotebookTreeStore.setState({
// isSearching: false
// });
setFilteredNotebooks(notebooks);
}
}, [notebooks]);
@@ -145,13 +137,13 @@ export const SideMenuNotebooks = () => {
/>
) : (
<>
<FlashList
<FlatList
data={tree}
bounces={false}
bouncesZoom={false}
overScrollMode="never"
// keyExtractor={(item,) => item.notebook.id}
estimatedItemSize={30}
keyExtractor={(item) => item.notebook.id}
windowSize={3}
ListHeaderComponent={
<View
style={{
@@ -204,9 +196,7 @@ const NotebookItemWrapper = React.memo(
const expanded = useSideMenuNotebookExpandedStore(
(state) => state.expanded[item.notebook.id]
);
const disableExpand = useSideMenuNotebookTreeStore(
(state) => state.isSearching
);
const selectionEnabled = useSideMenuNotebookSelectionStore(
(state) => state.enabled
);
@@ -266,7 +256,6 @@ const NotebookItemWrapper = React.memo(
.removeChildren(item.notebook.id);
}
}}
disableExpand={disableExpand}
selected={selected}
selectionEnabled={selectionEnabled}
selectionStore={useSideMenuNotebookSelectionStore}

View File

@@ -220,7 +220,7 @@ export const SideMenuTags = () => {
if (lastQuery.current) {
db.lookup
.tags(lastQuery.current.trim())
.sorted(db.settings.getGroupOptions("tags"))
.sorted()
.then(async (filtered) => {
setFilteredTags(filtered);
});

View File

@@ -154,7 +154,7 @@ const Input = ({
isError = customValidator && value === customValidator();
break;
case "url":
isError = isURL(value, { allow_underscores: true });
isError = isURL(value);
break;
case "phonenumber": {
const result = phone(value, {

View File

@@ -54,7 +54,6 @@ type ButtonTypes =
| "accent"
| "shade"
| "secondary"
| "selectedAccent"
| "secondaryAccented"
| "inverted"
| "white"
@@ -127,18 +126,6 @@ const buttonTypes = (
isDark
)
},
selectedAccent: {
primary: colors.selected.accent,
text: colors.selected.accentForeground,
selected: colors.selected.accent,
borderWidth: 0.8,
borderColor: getColorLinearShade(colors.selected.accent, 0.05, isDark),
borderSelectedColor: getColorLinearShade(
colors.selected.accent,
0.05,
isDark
)
},
secondaryAccented: {
primary: colors.secondary.background,
text: colors.primary.accent,

View File

@@ -23,11 +23,13 @@ import { Platform, View } from "react-native";
import ActionSheet from "react-native-actions-sheet";
import useGlobalSafeAreaInsets from "../../../hooks/use-global-safe-area-insets";
import { useSettingStore } from "../../../stores/use-setting-store";
import { useUserStore } from "../../../stores/use-user-store";
import { getContainerBorder } from "../../../utils/colors";
import { PremiumToast } from "../../premium/premium-toast";
import { Toast } from "../../toast";
import { NotesnookModule } from "../../../utils/notesnook-module";
import { useAppState } from "../../../hooks/use-app-state";
import SettingsService from "../../../services/settings";
import { useUserStore } from "../../../stores/use-user-store";
import { getContainerBorder } from "../../../utils/colors";
/**
*
* @param {any} param0
@@ -61,8 +63,6 @@ const SheetWrapper = ({
const locked = useUserStore((state) => state.appLocked);
let width = dimensions.width > 600 ? 600 : 500;
const isGestureNavigationEnabled =
NotesnookModule.isGestureNavigationEnabled();
const style = React.useMemo(() => {
return {
@@ -77,18 +77,14 @@ const SheetWrapper = ({
borderBottomRightRadius: 0,
borderBottomLeftRadius: 0,
...getContainerBorder(colors.primary.border, 0.5),
borderBottomWidth: 0,
paddingBottom: isGestureNavigationEnabled
? insets.bottom
: insets.bottom || 48
borderBottomWidth: 0
};
}, [
colors.primary.background,
colors.primary.border,
largeTablet,
smallTablet,
width,
insets.bottom
width
]);
const _onOpen = () => {

View File

@@ -336,7 +336,7 @@ const emailconfirmed: { id: string; steps: TStep[] } = {
steps: [
{
title: strings.emailConfirmed(),
text: strings.emailConfirmedDesc(),
text: strings.emailNotConfirmedDesc(),
walkthroughItem: (colors) => (
<SvgView src={WELCOME_SVG(colors.primary.paragraph)} />
),

View File

@@ -19,4 +19,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { FeatureType } from "./components/sheets/new-feature";
export const features: FeatureType[] = [];
export const features: FeatureType[] = [
{
title: "Improved Search",
body: "Improved search results with better relevance and highlighting."
}
];

View File

@@ -41,6 +41,7 @@ import ExportNotesSheet from "../components/sheets/export-notes";
import PublishNoteSheet from "../components/sheets/publish-note";
import { ReferencesList } from "../components/sheets/references";
import { RelationsList } from "../components/sheets/relations-list/index";
import ReminderSheet from "../components/sheets/reminder";
import { useSideBarDraggingStore } from "../components/side-menu/dragging-store";
import { ButtonProps } from "../components/ui/button";
import { useTabStore } from "../screens/editor/tiptap/use-tab-store";
@@ -53,20 +54,19 @@ import {
} from "../services/event-manager";
import Navigation from "../services/navigation";
import Notifications from "../services/notifications";
import SettingsService from "../services/settings";
import { useArchivedStore } from "../stores/use-archived-store";
import { useMenuStore } from "../stores/use-menu-store";
import useNavigationStore from "../stores/use-navigation-store";
import { useRelationStore } from "../stores/use-relation-store";
import { useSelectionStore } from "../stores/use-selection-store";
import { useSettingStore } from "../stores/use-setting-store";
import { useTagStore } from "../stores/use-tag-store";
import { useUserStore } from "../stores/use-user-store";
import { eUpdateNoteInEditor } from "../utils/events";
import { deleteItems } from "../utils/functions";
import { convertNoteToText } from "../utils/note-to-text";
import { sleep } from "../utils/time";
import AddReminder from "../screens/add-reminder";
import SettingsService from "../services/settings";
import { useSettingStore } from "../stores/use-setting-store";
import { useArchivedStore } from "../stores/use-archived-store";
export type ActionId =
| "select"
@@ -448,8 +448,7 @@ export const useActions = ({
title: strings.editReminder(),
icon: "pencil",
onPress: async () => {
AddReminder.present(item);
close();
ReminderSheet.present(item);
}
}
);
@@ -515,8 +514,6 @@ export const useActions = ({
title: strings.addNotebook(),
icon: "plus",
onPress: async () => {
close();
await sleep(300);
AddNotebookSheet.present(undefined, item);
}
},
@@ -525,8 +522,6 @@ export const useActions = ({
title: strings.editNotebook(),
icon: "square-edit-outline",
onPress: async () => {
close();
await sleep(300);
AddNotebookSheet.present(item);
}
},
@@ -913,18 +908,12 @@ export const useActions = ({
referenceType: "reminder",
relationType: "from",
title: strings.dataTypesPluralCamelCase.reminder(),
onAdd: () => {
AddReminder.present(undefined, item);
close();
},
onAdd: () => ReminderSheet.present(undefined, item, true),
button: {
type: "plain",
onPress: () => {
AddReminder.present(undefined, item);
close();
},
icon: "plus",
iconSize: 20
title: strings.add(),
type: "accent",
onPress: () => ReminderSheet.present(undefined, item, true),
icon: "plus"
}
});
}
@@ -968,8 +957,7 @@ export const useActions = ({
title: strings.remindMe(),
icon: "clock-plus-outline",
onPress: () => {
close();
AddReminder.present(undefined, item);
ReminderSheet.present(undefined, item);
}
},
{

View File

@@ -49,6 +49,7 @@ import { MMKV } from "../common/database/mmkv";
import { endProgress, startProgress } from "../components/dialogs/progress";
import Migrate from "../components/sheets/migrate";
import NewFeature from "../components/sheets/new-feature";
import ReminderSheet from "../components/sheets/reminder";
import { Walkthrough } from "../components/walkthroughs";
import {
resetTabStore,
@@ -102,7 +103,6 @@ import { getGithubVersion } from "../utils/github-version";
import { fluidTabsRef } from "../utils/global-refs";
import { NotesnookModule } from "../utils/notesnook-module";
import { sleep } from "../utils/time";
import AddReminder from "../screens/add-reminder";
const onCheckSyncStatus = async (type: SyncStatusEvent) => {
const { disableSync, disableAutoSync } = SettingsService.get();
@@ -182,10 +182,10 @@ const onAppOpenedFromURL = async (event: { url: string }) => {
const id = new URL(url).searchParams.get("id");
if (id) {
const reminder = await db.reminders.reminder(id);
if (reminder) AddReminder.present(reminder);
if (reminder) ReminderSheet.present(reminder);
}
} else if (url.startsWith("https://notesnook.com/new_reminder")) {
AddReminder.present();
ReminderSheet.present();
}
} catch (e) {
console.error(e);

View File

@@ -26,14 +26,12 @@ import { useCallback } from "react";
* @returns Is keyboard floating or not
*/
const useIsFloatingKeyboard = () => {
const { width, height } = useWindowDimensions();
const { width } = useWindowDimensions();
const [floating, setFloating] = useState<boolean>(false);
const onKeyboardWillChangeFrame = useCallback(
(event: KeyboardEvent) => {
setFloating(
event.endCoordinates.width === 0 || event.endCoordinates.width < width
);
setFloating(event.endCoordinates.width < width);
},
[width]
);
@@ -43,13 +41,8 @@ const useIsFloatingKeyboard = () => {
"keyboardWillChangeFrame",
onKeyboardWillChangeFrame
);
const sub2 = Keyboard.addListener(
"keyboardWillShow",
onKeyboardWillChangeFrame
);
return () => {
sub1?.remove();
sub2?.remove();
};
}, [onKeyboardWillChangeFrame, width]);

View File

@@ -30,10 +30,6 @@ import React, {
useState
} from "react";
import { Dimensions, LayoutChangeEvent, Platform, View } from "react-native";
import Orientation, {
OrientationType,
useDeviceOrientationChange
} from "react-native-orientation-locker";
import Animated, {
useAnimatedStyle,
useSharedValue,
@@ -71,6 +67,10 @@ import {
} from "../utils/events";
import { editorRef, fluidTabsRef } from "../utils/global-refs";
import { AppNavigationStack } from "./navigation-stack";
import Orientation, {
OrientationType,
useDeviceOrientationChange
} from "react-native-orientation-locker";
const MOBILE_SIDEBAR_SIZE = 0.85;
@@ -421,6 +421,7 @@ export const FluidPanelsView = React.memo(
height: "100%",
width: "100%",
backgroundColor: colors.primary.background,
paddingBottom: Platform.OS === "android" ? insets?.bottom : 0,
marginRight:
orientation === "LANDSCAPE-RIGHT" && Platform.OS === "ios"
? insets.right
@@ -529,20 +530,17 @@ const onChangeTab = async (event: { i: number; from: number }) => {
activateKeepAwake();
eSendEvent(eOnEnterEditor);
if (
useTabStore.getState().getTab(useTabStore.getState().currentTab)?.session
?.locked
) {
eSendEvent(eUnlockNote);
}
if (
fluidTabsRef.current?.tabChangedFromSwipeAction.value &&
!useTabStore.getState().getNoteIdForTab(useTabStore.getState().currentTab)
) {
editorController?.current?.commands?.focus(
useTabStore.getState().currentTab
);
if (!useTabStore.getState().getCurrentNoteId()) {
eSendEvent(eOnLoadNote, {
newNote: true
});
} else {
if (
useTabStore.getState().getTab(useTabStore.getState().currentTab)
?.session?.locked
) {
eSendEvent(eUnlockNote);
}
}
} else {
if (event.from === 2) {

View File

@@ -21,7 +21,6 @@ import { useThemeColors } from "@notesnook/theme";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import * as React from "react";
import { db } from "../common/database";
import { hideAllTooltips } from "../hooks/use-tooltip";
import SettingsService from "../services/settings";
import useNavigationStore, {
@@ -30,7 +29,7 @@ import useNavigationStore, {
import { useSelectionStore } from "../stores/use-selection-store";
import { useSettingStore } from "../stores/use-setting-store";
import { rootNavigatorRef } from "../utils/global-refs";
import Navigation from "../services/navigation";
import { db } from "../common/database";
const RootStack = createNativeStackNavigator();
const AppStack = createNativeStackNavigator();
@@ -249,25 +248,18 @@ let MoveNotebook: any = null;
let MoveNotes: any = null;
let Settings: any = null;
let ManageTags: any = null;
let AddReminder: any = null;
export const RootNavigation = () => {
const introCompleted = useSettingStore(
(state) => state.settings.introCompleted
);
const clearSelection = useSelectionStore((state) => state.clearSelection);
const onStateChange = React.useCallback(
(state: any) => {
if (useSelectionStore.getState().selectionMode) {
clearSelection();
}
setTimeout(() => {
Navigation.resetRootState(state);
}, 1000);
hideAllTooltips();
},
[clearSelection]
);
const onStateChange = React.useCallback(() => {
if (useSelectionStore.getState().selectionMode) {
clearSelection();
}
hideAllTooltips();
}, [clearSelection]);
return (
<NavigationContainer onStateChange={onStateChange} ref={rootNavigatorRef}>
@@ -344,15 +336,6 @@ export const RootNavigation = () => {
return ManageTags;
}}
/>
<RootStack.Screen
name="AddReminder"
getComponent={() => {
AddReminder =
AddReminder || require("../screens/add-reminder").default;
return AddReminder;
}}
/>
</RootStack.Navigator>
</NavigationContainer>
);

View File

@@ -1,621 +0,0 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Note, Reminder } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import dayjs from "dayjs";
import React, { useRef, useState } from "react";
import {
KeyboardAvoidingView,
Platform,
ScrollView,
TextInput,
View
} from "react-native";
import DatePicker from "react-native-date-picker";
import DateTimePickerModal from "react-native-modal-datetime-picker";
import { SafeAreaView } from "react-native-safe-area-context";
import { db } from "../../common/database";
import { Dialog } from "../../components/dialog";
import { Header } from "../../components/header";
import { Button } from "../../components/ui/button";
import Input from "../../components/ui/input";
import { ReminderTime } from "../../components/ui/reminder-time";
import Paragraph from "../../components/ui/typography/paragraph";
import { DDS } from "../../services/device-detection";
import { ToastManager } from "../../services/event-manager";
import Navigation, { NavigationProps } from "../../services/navigation";
import Notifications from "../../services/notifications";
import PremiumService from "../../services/premium";
import SettingsService from "../../services/settings";
import { useRelationStore } from "../../stores/use-relation-store";
import { AppFontSize, defaultBorderRadius } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import { getFormattedDate } from "@notesnook/common";
const ReminderModes =
Platform.OS === "ios"
? {
Once: "once",
Repeat: "repeat"
}
: {
Once: "once",
Repeat: "repeat",
Permanent: "permanent"
};
const RecurringModes = {
Daily: "day",
Week: "week",
Month: "month",
Year: "year"
};
const WeekDays = new Array(7).fill(true);
const MonthDays = new Array(31).fill(true);
const WeekDayNames = {
0: "Sunday",
1: "Monday",
2: "Tuesday",
3: "Wednesday",
4: "Thursday",
5: "Friday",
6: "Saturday"
};
const ReminderNotificationModes = {
Silent: "silent",
Vibrate: "vibrate",
Urgent: "urgent"
};
export default function AddReminder(props: NavigationProps<"AddReminder">) {
const { reminder, reference } = props.route.params;
const { colors, isDark } = useThemeColors();
const [reminderMode, setReminderMode] = useState<Reminder["mode"]>(
reminder?.mode || "once"
);
const [recurringMode, setRecurringMode] = useState<Reminder["recurringMode"]>(
reminder?.recurringMode || "week"
);
const [selectedDays, setSelectedDays] = useState<number[]>(
reminder?.selectedDays || []
);
const [date, setDate] = useState<Date>(
new Date(reminder?.date || Date.now())
);
const [reminderNotificationMode, setReminderNotificatioMode] = useState<
Reminder["priority"]
>(reminder?.priority || SettingsService.get().reminderNotificationMode);
const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
const [repeatFrequency, setRepeatFrequency] = useState(1);
const referencedItem = reference ? (reference as Note) : null;
const title = useRef<string | undefined>(
!reminder ? referencedItem?.title : reminder?.title
);
const details = useRef<string | undefined>(
!reminder ? referencedItem?.headline : reminder?.description
);
const titleRef = useRef<TextInput>(null);
const descriptionRef = useRef<TextInput>(null);
const timer = useRef<NodeJS.Timeout>();
const showDatePicker = () => {
setDatePickerVisibility(true);
};
const hideDatePicker = () => {
setDatePickerVisibility(false);
};
const handleConfirm = (date: Date) => {
timer.current = setTimeout(() => {
hideDatePicker();
setDate(date);
}, 10);
};
function nth(n: number) {
return (
["st", "nd", "rd"][(((((n < 0 ? -n : n) + 90) % 100) - 10) % 10) - 1] ||
"th"
);
}
function getSelectedDaysText(selectedDays: number[]) {
const text = selectedDays
.sort((a, b) => a - b)
.map((day, index) => {
const isLast = index === selectedDays.length - 1;
const isSecondLast = index === selectedDays.length - 2;
const joinWith = isSecondLast ? " & " : isLast ? "" : ", ";
return recurringMode === RecurringModes.Week
? WeekDayNames[day as keyof typeof WeekDayNames] + joinWith
: `${day}${nth(day)} ${joinWith}`;
})
.join("");
return text;
}
async function saveReminder() {
try {
if (!(await Notifications.checkAndRequestPermissions(true)))
throw new Error(strings.noNotificationPermission());
if (!date && reminderMode !== ReminderModes.Permanent) return;
if (
reminderMode === ReminderModes.Repeat &&
recurringMode !== "day" &&
recurringMode !== "year" &&
selectedDays.length === 0
)
throw new Error(strings.selectDayError());
if (!title.current) throw new Error(strings.setTitleError());
if (
date.getTime() < Date.now() &&
reminderMode === "once" &&
!props.route.params.reminder
) {
throw new Error(strings.dateError());
}
date.setSeconds(0, 0);
const reminderId = await db.reminders?.add({
id: reminder?.id,
date: date?.getTime(),
priority: reminderNotificationMode,
title: title.current,
description: details.current,
recurringMode: recurringMode,
selectedDays: selectedDays,
mode: reminderMode,
localOnly: reminderMode === "permanent",
snoozeUntil:
date?.getTime() > Date.now() ? undefined : reminder?.snoozeUntil,
disabled: false
});
if (!reminderId) return;
const _reminder = await db.reminders?.reminder(reminderId);
if (reference && _reminder) {
await db.relations?.add(reference, {
id: _reminder?.id as string,
type: _reminder?.type
});
}
Notifications.scheduleNotification(_reminder as Reminder);
Navigation.queueRoutesForUpdate();
useRelationStore.getState().update();
Navigation.goBack();
} catch (e) {
ToastManager.error(e as Error, undefined);
}
}
const KeyboardViewIOS = Platform.OS === "ios" ? KeyboardAvoidingView : View;
return (
<SafeAreaView
style={{
backgroundColor: colors.primary.background,
flex: 1
}}
>
<KeyboardViewIOS
behavior="padding"
style={{
flex: 1
}}
>
<Header
title={reminder ? strings.editReminder() : strings.newReminder()}
canGoBack
rightButton={{
name: "check",
onPress: saveReminder
}}
/>
<Dialog context="local" />
<ScrollView
style={{
marginBottom: DDS.isTab ? 25 : undefined,
paddingHorizontal: DefaultAppStyles.GAP
}}
keyboardDismissMode="interactive"
keyboardShouldPersistTaps="handled"
>
<Input
fwdRef={titleRef}
defaultValue={reminder?.title || referencedItem?.title}
placeholder={strings.remindeMeOf()}
onChangeText={(text) => (title.current = text)}
autoFocus
wrapperStyle={{
marginTop: DefaultAppStyles.GAP_VERTICAL
}}
onSubmit={() => {
descriptionRef.current?.focus();
}}
/>
<Input
defaultValue={
reminder ? reminder?.description : referencedItem?.headline
}
fwdRef={descriptionRef}
placeholder={strings.addShortNote()}
onChangeText={(text) => (details.current = text)}
containerStyle={{
maxHeight: 80
}}
multiline
textAlignVertical="top"
inputStyle={{
minHeight: 80,
paddingVertical: DefaultAppStyles.GAP_VERTICAL
}}
height={80}
wrapperStyle={{
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
/>
<ScrollView
style={{
flexDirection: "row",
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
horizontal
>
{Object.keys(ReminderModes).map((mode) => (
<Button
key={mode}
title={strings.reminderModes(
ReminderModes[mode as keyof typeof ReminderModes] as string
)}
style={{
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
marginRight: DefaultAppStyles.GAP_SMALL
}}
proTag={mode === "Repeat"}
height={35}
type={
reminderMode ===
ReminderModes[mode as keyof typeof ReminderModes]
? "selectedAccent"
: "plain"
}
onPress={() => {
if (mode === "Repeat" && !PremiumService.get()) return;
setReminderMode(
ReminderModes[
mode as keyof typeof ReminderModes
] as Reminder["mode"]
);
if (mode === "Repeat") {
setSelectedDays((days) => {
if (days.length > 0) return days;
if (days.indexOf(date.getDay()) > -1) {
return days;
}
days.push(date.getDay());
return [...days];
});
}
}}
/>
))}
</ScrollView>
{reminderMode === ReminderModes.Repeat ? (
<View
style={{
backgroundColor: colors.secondary.background,
padding: DefaultAppStyles.GAP,
borderRadius: defaultBorderRadius,
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
>
<View
style={{
flexDirection: "row",
marginBottom:
recurringMode === "day" || recurringMode === "year"
? 0
: 12,
alignItems: "center"
}}
>
{Object.keys(RecurringModes).map((mode) => (
<Button
key={mode}
title={strings.recurringModes(
RecurringModes[mode as keyof typeof RecurringModes]
)}
style={{
marginRight: 6,
borderRadius: 100,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
}}
type={
recurringMode ===
RecurringModes[mode as keyof typeof RecurringModes]
? "selected"
: "plain"
}
onPress={() => {
setRecurringMode(
RecurringModes[
mode as keyof typeof RecurringModes
] as Reminder["recurringMode"]
);
setSelectedDays([]);
setRepeatFrequency(1);
}}
/>
))}
</View>
<ScrollView showsHorizontalScrollIndicator={false} horizontal>
{recurringMode === RecurringModes.Daily ||
recurringMode === RecurringModes.Year
? null
: recurringMode === RecurringModes.Week
? WeekDays.map((item, index) => (
<Button
key={strings.weekDayNamesShort[
index as keyof typeof strings.weekDayNamesShort
]()}
title={strings.weekDayNamesShort[
index as keyof typeof strings.weekDayNamesShort
]()}
type={
selectedDays.indexOf(index) > -1
? "selected"
: "plain"
}
fontSize={AppFontSize.xs}
style={{
height: 40,
borderRadius: 100,
marginRight: 10
}}
onPress={() => {
setSelectedDays((days) => {
if (days.indexOf(index) > -1) {
days.splice(days.indexOf(index), 1);
return [...days];
}
days.push(index);
return [...days];
});
}}
/>
))
: MonthDays.map((item, index) => (
<Button
key={index + "monthday"}
title={index + 1 + ""}
type={
selectedDays.indexOf(index + 1) > -1
? "selected"
: "plain"
}
fontSize={AppFontSize.xs}
style={{
height: 40,
borderRadius: 100,
marginRight: 10
}}
onPress={() => {
setSelectedDays((days) => {
if (days.indexOf(index + 1) > -1) {
days.splice(days.indexOf(index + 1), 1);
return [...days];
}
days.push(index + 1);
return [...days];
});
}}
/>
))}
</ScrollView>
</View>
) : null}
{reminderMode === ReminderModes.Permanent ? null : (
<View
style={{
width: "100%",
flexDirection: "column",
justifyContent: "center",
marginBottom: DefaultAppStyles.GAP_VERTICAL,
alignItems: "center"
}}
>
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="datetime"
onConfirm={handleConfirm}
onCancel={hideDatePicker}
isDarkModeEnabled={isDark}
is24Hour={db.settings.getTimeFormat() === "24-hour"}
date={date || new Date(Date.now())}
/>
<DatePicker
date={date}
maximumDate={dayjs(date).add(3, "months").toDate()}
onDateChange={handleConfirm}
textColor={isDark ? colors.static.white : colors.static.black}
fadeToColor={colors.primary.background}
theme={isDark ? "dark" : "light"}
androidVariant="nativeAndroid"
is24hourSource="locale"
locale={
db.settings?.getTimeFormat() === "24-hour" ? "en_GB" : "en_US"
}
mode={
reminderMode === ReminderModes.Repeat &&
recurringMode !== "year"
? "time"
: "datetime"
}
/>
{reminderMode === ReminderModes.Repeat ? null : (
<Button
style={{
width: "100%"
}}
title={
date
? getFormattedDate(date, "date-time")
: strings.selectDate()
}
type={date ? "secondaryAccented" : "secondary"}
icon="calendar"
fontSize={AppFontSize.sm}
onPress={() => {
showDatePicker();
}}
/>
)}
</View>
)}
{reminderMode === ReminderModes.Once ||
reminderMode === ReminderModes.Permanent ? null : (
<View
style={{
borderRadius: defaultBorderRadius,
flexDirection: "row",
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
alignItems: "center",
justifyContent: "flex-start",
marginBottom: DefaultAppStyles.GAP_VERTICAL
}}
>
<>
<Paragraph
size={AppFontSize.xxs}
color={colors.secondary.paragraph}
>
{recurringMode === RecurringModes.Daily
? strings.reminderRepeatStrings.day(
dayjs(date).format("hh:mm A")
)
: recurringMode === RecurringModes.Year
? strings.reminderRepeatStrings.year(
dayjs(date).format("dddd, MMMM D, h:mm A")
)
: selectedDays.length === 7 &&
recurringMode === RecurringModes.Week
? strings.reminderRepeatStrings.week.daily(
dayjs(date).format("hh:mm A")
)
: selectedDays.length === 0
? strings.reminderRepeatStrings[
recurringMode as "week" | "month"
].selectDays()
: strings.reminderRepeatStrings.repeats(
repeatFrequency,
recurringMode as string,
getSelectedDaysText(selectedDays),
dayjs(date).format("hh:mm A")
)}
</Paragraph>
</>
</View>
)}
<ReminderTime
reminder={reminder}
style={{
width: "100%",
justifyContent: "flex-start",
borderWidth: 0,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL,
alignSelf: "flex-start"
}}
/>
{reminderMode === ReminderModes.Permanent ? null : (
<ScrollView
style={{
flexDirection: "row",
marginTop: DefaultAppStyles.GAP_VERTICAL,
height: 50
}}
horizontal
>
{Object.keys(ReminderNotificationModes).map((mode) => (
<Button
key={mode}
title={strings.reminderNotificationModes(
mode as keyof typeof ReminderNotificationModes
)}
style={{
marginRight: 12,
paddingVertical: DefaultAppStyles.GAP_VERTICAL_SMALL
}}
icon={
mode === "Silent"
? "minus-circle"
: mode === "Vibrate"
? "vibrate"
: "volume-high"
}
fontSize={AppFontSize.xs}
height={35}
type={
reminderNotificationMode ===
ReminderNotificationModes[
mode as keyof typeof ReminderNotificationModes
]
? "selectedAccent"
: "plain"
}
onPress={() => {
const _mode = ReminderNotificationModes[
mode as keyof typeof ReminderNotificationModes
] as Reminder["priority"];
SettingsService.set({
reminderNotificationMode: _mode
});
setReminderNotificatioMode(_mode);
}}
/>
))}
</ScrollView>
)}
</ScrollView>
</KeyboardViewIOS>
</SafeAreaView>
);
}
AddReminder.present = (reminder?: Reminder, reference?: Note) => {
Navigation.navigate("AddReminder", {
reminder,
reference
});
};

View File

@@ -38,11 +38,11 @@ import { WebViewMessageEvent } from "react-native-webview";
import { DatabaseLogger, db } from "../../../common/database";
import downloadAttachment from "../../../common/filesystem/download-attachment";
import { AuthMode } from "../../../components/auth/common";
import { Properties } from "../../../components/properties";
import EditorTabs from "../../../components/sheets/editor-tabs";
import { Issue } from "../../../components/sheets/github/issue";
import LinkNote from "../../../components/sheets/link-note";
import { RelationsList } from "../../../components/sheets/relations-list";
import ReminderSheet from "../../../components/sheets/reminder";
import TableOfContents from "../../../components/sheets/toc";
import { DDS } from "../../../services/device-detection";
import {
@@ -73,13 +73,13 @@ import {
} from "../../../utils/events";
import { openLinkInBrowser } from "../../../utils/functions";
import { fluidTabsRef } from "../../../utils/global-refs";
import { sleep } from "../../../utils/time";
import ManageTags from "../../manage-tags";
import { useDragState } from "../../settings/editor/state";
import { EditorMessage, EditorProps, useEditorType } from "./types";
import { useTabStore } from "./use-tab-store";
import { editorState, openInternalLink } from "./utils";
import AddReminder from "../../add-reminder";
import { Properties } from "../../../components/properties";
import { sleep } from "../../../utils/time";
import ManageTags from "../../manage-tags";
const publishNote = async () => {
const user = useUserStore.getState().user;
@@ -131,6 +131,7 @@ const showActionsheet = async () => {
.getState()
.getNoteIdForTab(useTabStore.getState().currentTab!);
if (noteId) {
console.log("OPEN NOTE");
const note = await db.notes?.note(noteId);
Properties.present(note, false);
} else {
@@ -437,7 +438,7 @@ export const useEditorEvents = (
referenceType: "reminder",
relationType: "from",
title: strings.dataTypesPluralCamelCase.reminder(),
onAdd: () => AddReminder.present(undefined, note)
onAdd: () => ReminderSheet.present(undefined, note, true)
});
break;
case EditorEvents.newtag:

View File

@@ -755,8 +755,6 @@ export const useEditor = (
? (data as ContentItem).noteId
: data.id;
if (!useTabStore.getState().hasTabForNote(noteId)) return;
const note = data.type === "note" ? data : await db.notes?.note(noteId);
lock.current = true;
// Handle this case where note was locked on another device and synced.
@@ -875,6 +873,7 @@ export const useEditor = (
}
lastContentChangeTime.current[note.id] = note.dateEdited;
console.log(tab.session?.selection);
await postMessage(
NativeEvents.updatehtml,
{

View File

@@ -98,7 +98,7 @@ class TabSessionStorage {
static get(id: string): TabSessionItem | null {
if (!id) return null;
return TabSessionStorage.storage.getMap(id) || null;
return TabSessionStorage.storage.getMap(id);
}
static set(id: string, session: TabSessionItem): void {

View File

@@ -96,16 +96,14 @@ export const EditorWrapper = ({ widths }: { widths: any }) => {
minHeight: "100%",
backgroundColor: toolBarColors.primary.background,
borderLeftWidth: DDS.isTab ? 1 : 0,
borderLeftColor: DDS.isTab
? colors.secondary.background
: "transparent",
paddingBottom: insets.bottom
borderLeftColor: DDS.isTab ? colors.secondary.background : "transparent"
}}
>
{loading || !introCompleted ? null : (
<KeyboardAvoidingViewIOS
behavior="padding"
style={{
marginBottom: getMarginBottom(),
backgroundColor: colors.primary.background,
flex: 1
}}

View File

@@ -300,8 +300,6 @@ const NotebookItemWrapper = React.memo(
const expanded = useNotebookExpandedStore(
(state) => state.expanded[item.notebook.id]
);
const disableExpand = useNotebookTreeStore((state) => state.isSearching);
const selectionEnabled = useNotebookSelectionStore(
(state) => state.enabled
);
@@ -373,7 +371,6 @@ const NotebookItemWrapper = React.memo(
selectionEnabled={selectionEnabled}
selectionStore={useNotebookSelectionStore}
onItemUpdate={onItemUpdate}
disableExpand={disableExpand}
focused={false}
onPress={onPress}
onAddNotebook={() => {

View File

@@ -112,7 +112,7 @@ const ManageTags = (props: NavigationProps<"ManageTags">) => {
if (query && query.trim() !== "") {
db.lookup
.tags(query)
.sorted(db.settings.getGroupOptions("tags"))
.sorted()
.then((items) => {
setTags(items);
});
@@ -249,7 +249,6 @@ const ManageTags = (props: NavigationProps<"ManageTags">) => {
}}
>
<Header title={strings.manageTags()} canGoBack />
<View
style={{
paddingHorizontal: DefaultAppStyles.GAP

View File

@@ -328,8 +328,6 @@ const NotebookItemWrapper = React.memo(
const expanded = useNotebookExpandedStore(
(state) => state.expanded[item.notebook.id]
);
const disableExpand = useNotebookTreeStore((state) => state.isSearching);
const selectionEnabled = useNotebookSelectionStore(
(state) => state.enabled
);
@@ -380,7 +378,6 @@ const NotebookItemWrapper = React.memo(
useNotebookTreeStore.getState().removeChildren(item.notebook.id);
}
}}
disableExpand={disableExpand}
selected={selected}
selectionEnabled={selectionEnabled}
selectionStore={useNotebookSelectionStore}

View File

@@ -17,19 +17,19 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { strings } from "@notesnook/intl";
import React from "react";
import { FloatingButton } from "../../components/container/floating-button";
import DelayLayout from "../../components/delay-layout";
import { Header } from "../../components/header";
import List from "../../components/list";
import SelectionHeader from "../../components/selection-header";
import ReminderSheet from "../../components/sheets/reminder";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import Navigation, { NavigationProps } from "../../services/navigation";
import SettingsService from "../../services/settings";
import useNavigationStore from "../../stores/use-navigation-store";
import { useReminders } from "../../stores/use-reminder-store";
import AddReminder from "../add-reminder";
import { strings } from "@notesnook/intl";
export const Reminders = ({
navigation,
@@ -68,7 +68,7 @@ export const Reminders = ({
}}
id={route.name}
onPressDefaultRightButton={() => {
AddReminder.present();
ReminderSheet.present();
}}
/>
@@ -84,7 +84,7 @@ export const Reminders = ({
paragraph: strings.remindersEmpty(),
button: strings.setReminder(),
action: () => {
AddReminder.present();
ReminderSheet.present();
},
loading: strings.loadingReminders()
}}
@@ -92,7 +92,7 @@ export const Reminders = ({
<FloatingButton
onPress={() => {
AddReminder.present();
ReminderSheet.present();
}}
alwaysVisible
/>

View File

@@ -113,10 +113,7 @@ async function onBackgroundSyncStarted() {
useUserStore.getState().setSyncing(false);
}
await Notifications.setupReminders();
if (SettingsService.get().notifNotes) {
Notifications.pinQuickNote(false);
}
Notifications.restorePinnedNotes();
NotePreviewWidget.updateNotes();
deleteDCacheFiles();
DatabaseLogger.info("BACKGROUND SYNC COMPLETE");
@@ -145,7 +142,6 @@ const onBoot = async () => {
Notifications.pinQuickNote(false);
}
Notifications.restorePinnedNotes();
NotePreviewWidget.updateNotes();
DatabaseLogger.info("BOOT TASK COMPLETE");
} catch (e) {
DatabaseLogger.error(e as Error);

View File

@@ -18,12 +18,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Platform } from "react-native";
import { db } from "../common/database";
import ReminderSheet from "../components/sheets/reminder";
import { setAppState } from "../screens/editor/tiptap/utils";
import { eOnLoadNote } from "../utils/events";
import { NotesnookModule } from "../utils/notesnook-module";
import { eSendEvent } from "./event-manager";
import { fluidTabsRef } from "../utils/global-refs";
import AddReminder from "../screens/add-reminder";
const launchIntent = Platform.OS === "ios" ? {} : NotesnookModule.getIntent();
let used = false;
@@ -65,9 +65,9 @@ export const IntentService = {
const reminder = await db.reminders.reminder(
intent["com.streetwriters.notesnook.OpenReminderId"]
);
if (reminder) AddReminder.present(reminder);
if (reminder) ReminderSheet.present(reminder);
} else if (intent["com.streetwriters.notesnook.NewReminder"]) {
AddReminder.present();
ReminderSheet.present();
}
} catch (e) {
/* empty */

View File

@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { NavigationHelpers, StackActions } from "@react-navigation/native";
import { StackActions } from "@react-navigation/native";
import { NativeStackScreenProps } from "@react-navigation/native-stack";
import { useFavoriteStore } from "../stores/use-favorite-store";
import useNavigationStore, {
@@ -69,8 +69,7 @@ const routeNames = {
LinkNotebooks: "LinkNotebooks",
MoveNotes: "MoveNotes",
Archive: "Archive",
ManageTags: "ManageTags",
AddReminder: "AddReminder"
ManageTags: "ManageTags"
};
export type NavigationProps<T extends RouteName> = NativeStackScreenProps<
@@ -166,32 +165,6 @@ function closeDrawer() {
fluidTabsRef.current?.closeDrawer();
}
function resetRootState(
_state?: ReturnType<NavigationHelpers<any, any>["getState"]>
) {
const state = _state || rootNavigatorRef.getState();
const focusedRoute = state.routes[state.index];
if (state.routes.length < 2) return;
const routes = state.routes.filter(
(route) =>
(route.name !== "Auth" && route.name !== "Welcome") ||
route.key === focusedRoute.key
);
if (routes.length === state.routes.length) return;
if (routes.length === 0) {
routes.push(focusedRoute);
}
const newIndex = routes.findIndex((route) => route.key === focusedRoute.key);
rootNavigatorRef.reset({
...state,
routes: routes,
index: newIndex
});
}
const Navigation = {
navigate,
goBack,
@@ -203,8 +176,7 @@ const Navigation = {
queueRoutesForUpdate,
routeNeedsUpdate,
routeNames,
routeUpdateFunctions,
resetRootState
routeUpdateFunctions
};
export default Navigation;

View File

@@ -17,7 +17,6 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { getFormattedReminderTime } from "@notesnook/common";
import { isReminderActive, Reminder } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import notifee, {
@@ -37,7 +36,6 @@ import dayjs, { Dayjs } from "dayjs";
import { encodeNonAsciiHTML } from "entities";
import { Platform } from "react-native";
import { db, setupDatabase } from "../common/database";
import { MMKV } from "../common/database/mmkv";
import { presentDialog } from "../components/dialog/functions";
import { useTabStore } from "../screens/editor/tiptap/use-tab-store";
import { editorState } from "../screens/editor/tiptap/utils";
@@ -54,6 +52,8 @@ import { DDS } from "./device-detection";
import { eSendEvent } from "./event-manager";
import Navigation from "./navigation";
import SettingsService from "./settings";
import { getFormattedReminderTime } from "@notesnook/common";
import { MMKV } from "../common/database/mmkv";
let pinned: DisplayedNotification[] = [];
@@ -348,7 +348,7 @@ async function scheduleNotification(
title: title,
message: description || "",
ongoing: true,
// subtitle: description || "",
subtitle: description || "",
actions: [strings.unpin()]
});
}
@@ -396,7 +396,7 @@ async function scheduleNotification(
payload: payload || "",
dateModified: reminder.dateModified + ""
},
// subtitle: !description ? undefined : description,
subtitle: !description ? undefined : description,
android: {
channelId: await getChannelId(priority),
smallIcon: "ic_stat_name",
@@ -454,7 +454,7 @@ async function loadNote(id: string, jump: boolean) {
const tab = useTabStore.getState().getTabForNote(id);
if (useTabStore.getState().currentTab !== tab) {
eSendEvent(eOnLoadNote, {
item: note
note: note
});
}
}
@@ -916,21 +916,8 @@ async function pinQuickNote(launch: boolean) {
* A function that checks if reminders need to be reconfigured &
* reschedules them if anything has changed.
*/
async function setupReminders(checkNeedsScheduling = false) {
const reminders = ((await db.reminders?.all.items()) as Reminder[]) || [];
let notificationsCancelled = false;
if (Platform.OS === "android") {
// If the API level has changed, cancel all notifications.
// This is to ensure that the app does not crash on Android 14+.
const API_LEVEL = MMKV.getInt("android_apiLevel");
if (API_LEVEL !== (Platform.Version as number)) {
await notifee.cancelAllNotifications();
notificationsCancelled = true;
MMKV.setInt("android_apiLevel", Platform.Version as number);
}
}
const triggers = await notifee.getTriggerNotifications();
for (const reminder of reminders) {
if (reminder.mode === "permanent") {
@@ -940,22 +927,20 @@ async function setupReminders(checkNeedsScheduling = false) {
// Skip reminders that are not repeating and their trigger date is in past.
if (reminder.mode === "once" && dayjs().isAfter(reminder.date)) continue;
if (!notificationsCancelled) {
const pending = triggers.filter((t) =>
t.notification.id?.startsWith(reminder.id)
);
const pending = triggers.filter((t) =>
t.notification.id?.startsWith(reminder.id)
);
let needsReschedule = pending.length === 0 ? true : false;
if (!needsReschedule) {
needsReschedule = pending[0].notification.data?.dateModified
? parseInt(pending[0].notification.data?.dateModified as string) <
reminder.dateModified
: true;
}
if (!needsReschedule && checkNeedsScheduling) continue;
let needsReschedule = pending.length === 0 ? true : false;
if (!needsReschedule) {
needsReschedule = pending[0].notification.data?.dateModified
? parseInt(pending[0].notification.data?.dateModified as string) <
reminder.dateModified
: true;
}
if (!needsReschedule && checkNeedsScheduling) continue;
await scheduleNotification(reminder);
}
// Check for any triggers whose notifications

View File

@@ -49,7 +49,6 @@ export function createNotebookTreeStores(
) {
const useNotebookTreeStore = create<{
tree: TreeItem[];
isSearching?: boolean;
setTree: (tree: TreeItem[]) => void;
removeItem: (id: string) => void;
addNotebooks: (
@@ -114,32 +113,16 @@ export function createNotebookTreeStores(
}
}
const rootTreeItems = newTree.filter((item) => item.parentId === "root");
const newTreeItems = notebooks.reduce((acc, notebook) => {
if (!rootTreeItems.find((item) => item.notebook.id === notebook.id)) {
acc.push({
parentId,
notebook,
depth: depth,
hasChildren: false
});
}
return acc;
}, [] as TreeItem[]);
if (parentId === "root") {
rootTreeItems.splice(0, 0, ...newTreeItems);
}
for (const treeItem of newTreeItems) {
treeItem.hasChildren = items.some((item) => {
return (
rootTreeItems.findIndex(
(treeItem) => treeItem.notebook.id === item.toId
) === -1 && item.fromId === treeItem.notebook.id
);
});
}
const newTreeItems = notebooks.map((notebook) => {
return {
parentId,
notebook,
depth: depth,
hasChildren: items.some((item) => {
return item.fromId === notebook.id;
})
};
});
newTree.splice(parentIndex + 1, 0, ...newTreeItems);
@@ -147,7 +130,7 @@ export function createNotebookTreeStores(
const expanded =
useNotebookExpandedStore.getState().expanded[item.notebook.id] &&
item.hasChildren;
if (expanded && !get().isSearching) {
if (expanded) {
newTree = await get().fetchAndAdd(
item.notebook.id,
depth + 1,

View File

@@ -45,10 +45,10 @@ export function initAfterSync(type: "full" | "send" = "send") {
useUserStore.setState({
profile: db.settings.getProfile()
});
eSendEvent(eAfterSync);
}
Notifications.setupReminders(true);
NotePreviewWidget.updateNotes();
eSendEvent(eAfterSync);
}
export async function initialize() {}

View File

@@ -94,10 +94,6 @@ export interface RouteParams extends ParamListBase {
ManageTags: {
ids?: string[];
};
AddReminder: {
reminder?: Reminder;
reference?: Note;
};
}
export type RouteName = keyof RouteParams;

View File

@@ -43,7 +43,7 @@ export function changeSystemBarColors() {
const isDark = useThemeStore.getState().colorScheme === "dark";
changeNavigationBarColor(
currTheme.scopes.base.primary.background,
!isDark,
isDark,
false
);
StatusBar.setBackgroundColor("transparent" as any);

View File

@@ -40,7 +40,6 @@ interface NotesnookModuleInterface {
hasWidgetNote: (noteId: string) => Promise<boolean>;
updateWidgetNote: (noteId: string, data: string) => void;
updateReminderWidget: () => void;
isGestureNavigationEnabled: () => boolean;
}
export const NotesnookModule: NotesnookModuleInterface = Platform.select({
@@ -60,8 +59,7 @@ export const NotesnookModule: NotesnookModuleInterface = Platform.select({
getWidgetNotes: () => {},
hasWidgetNote: () => {},
updateWidgetNote: () => {},
updateReminderWidget: () => {},
isGestureNavigationEnabled: () => true
updateReminderWidget: () => {}
},
android: NativeModules.NNativeModule
});

View File

@@ -89,10 +89,10 @@ function getSize() {
xs: normalize(13.5) * scale.fontScale,
sm: normalize(14.5) * scale.fontScale,
md: normalize(16.5) * scale.fontScale,
lg: normalize(20) * scale.fontScale,
xl: normalize(22) * scale.fontScale,
xxl: normalize(25) * scale.fontScale,
xxxl: normalize(30) * scale.fontScale
lg: normalize(22) * scale.fontScale,
xl: normalize(24) * scale.fontScale,
xxl: normalize(28) * scale.fontScale,
xxxl: normalize(32) * scale.fontScale
};
}

View File

@@ -124,7 +124,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled true
versionCode 3068
versionCode 3058
versionName getNpmVersion()
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

View File

@@ -11,69 +11,57 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:node="remove" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
tools:node="remove" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission
android:name="android.permission.USE_FULL_SCREEN_INTENT"
tools:node="remove" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" tools:node="remove" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="application/pdf" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="text/*" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="image/*" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="application/pdf" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="text/*" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="image/*" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="video/*" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="audio/*" />
</intent>
</queries>
<data android:mimeType="video/*" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
<data android:mimeType="audio/*" />
</intent>
</queries>
<application
android:name=".MainApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:networkSecurityConfig="@xml/network_security_config"
android:requestLegacyExternalStorage="true"
android:theme="@style/BootTheme"
android:largeHeap="true"
android:supportsRtl="true"
android:theme="@style/BootTheme">
<receiver
android:name=".NoteWidget"
android:exported="false"
android:label="@string/quick_note">
android:networkSecurityConfig="@xml/network_security_config">
<receiver android:exported="false" android:label="@string/quick_note" android:name=".NoteWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
@@ -83,10 +71,7 @@
android:resource="@xml/new_note_widget_info" />
</receiver>
<receiver
android:name=".NotePreviewWidget"
android:exported="false"
android:label="@string/note">
<receiver android:exported="false" android:label="@string/note" android:name=".NotePreviewWidget">
<intent-filter>"
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
@@ -96,10 +81,7 @@
android:resource="@xml/note_widget_info" />
</receiver>
<receiver
android:name=".ReminderWidgetProvider"
android:exported="false"
android:label="@string/reminders_title">
<receiver android:exported="false" android:label="@string/reminders_title" android:name=".ReminderWidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
@@ -108,37 +90,33 @@
android:resource="@xml/widget_reminders_info" />
</receiver>
<activity
android:name=".NotePreviewConfigureActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:exported="true"
<activity android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:label="NotePreviewConfigure"
android:launchMode="singleTask"
android:exported="true"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize">
android:windowSoftInputMode="adjustResize" android:name=".NotePreviewConfigureActivity">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:exported="true"
android:label="@string/app_name"
android:launchMode="singleTask"
android:resizeableActivity="true"
android:exported="true"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:label="Notesnook">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
@@ -149,7 +127,6 @@
<intent-filter android:label="Notesnook">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
@@ -157,25 +134,22 @@
</intent-filter>
</activity>
<activity
android:name="com.facebook.react.devsupport.DevSettingsActivity"
android:exported="false" />
<activity android:exported="false" android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<activity
android:name=".ShareActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:excludeFromRecents="true"
android:exported="true"
android:label="@string/title_activity_share"
android:noHistory="true"
android:screenOrientation="unspecified"
android:exported="true"
android:taskAffinity=""
android:theme="@style/AppThemeB"
android:windowSoftInputMode="adjustResize">
android:excludeFromRecents="true"
android:windowSoftInputMode="adjustResize"
android:theme="@style/AppThemeB">
<intent-filter android:label="Make Note">
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/*" />
<data android:mimeType="image/*" />
<data android:mimeType="video/*" />
@@ -186,7 +160,6 @@
<intent-filter android:label="Make Note">
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/*" />
<data android:mimeType="image/*" />
<data android:mimeType="video/*" />
@@ -206,41 +179,35 @@
android:enabled="true"
android:exported="true"
android:stopWithTask="false" />
<service android:name="com.asterinet.react.bgactions.RNBackgroundActionsTask" />
<service android:name="com.asterinet.react.bgactions.RNBackgroundActionsTask" />
<service
android:name="com.streetwriters.notesnook.BootTaskService"
android:foregroundServiceType="specialUse">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="The service is required by the app to restore notifications of pinned notes, restore notification with reply input for creating notes and restore data in note preview widgets on device reboot." />
</service>
<service android:name="com.streetwriters.notesnook.BootTaskService" android:foregroundServiceType="dataSync" />
<service
android:name=".NotesnookTileService"
android:exported="true"
android:icon="@drawable/add_note"
android:label="New note"
android:exported="true"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
<action android:name="android.service.quicksettings.action.QS_TILE"/>
</intent-filter>
</service>
<service
android:name=".ReminderViewsService"
android:exported="true"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<service
android:name=".ReminderViewsService"
android:exported="true"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_viewer_provider_paths" />
</provider>
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_viewer_provider_paths" />
</provider>
<provider
android:name="com.vinzscam.reactnativefileviewer.FileProvider"
@@ -252,14 +219,12 @@
android:resource="@xml/file_viewer_provider_paths" />
</provider>
<receiver
android:name=".BootRecieverService"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<receiver android:exported="true" android:name=".BootRecieverService">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
</application>

View File

@@ -39,6 +39,7 @@ public class BootTaskService extends HeadlessJsTaskService {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("com.streetwriters.notesnook",
"Default",
@@ -54,7 +55,7 @@ public class BootTaskService extends HeadlessJsTaskService {
this.startForeground(
1,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE);
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
} else {
this.startForeground(
1,

View File

@@ -3,16 +3,8 @@ package com.streetwriters.notesnook;
import com.facebook.react.ReactActivity;
import android.content.Intent;
import android.content.res.Configuration;
import androidx.core.graphics.Insets;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebView;
import androidx.core.view.OnApplyWindowInsetsListener;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactActivityDelegate;
@@ -30,20 +22,6 @@ public class MainActivity extends ReactActivity {
try {
startService(new Intent(getBaseContext(), OnClearFromRecentService.class));
} catch (Exception ignored) {}
if (Build.VERSION.SDK_INT >= 35) {
final View rootView = findViewById(android.R.id.content);
ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, insets) -> {
Insets innerPadding = insets.getInsets(WindowInsetsCompat.Type.ime());
rootView.setPadding(
innerPadding.left,
innerPadding.top,
innerPadding.right,
innerPadding.bottom
);
return insets;
});
}
}
/**

View File

@@ -7,9 +7,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.WindowManager;
import android.widget.RemoteViews;
@@ -219,24 +217,4 @@ public class RCTNNativeModule extends ReactContextBaseJavaModule {
wm.notifyAppWidgetViewDataChanged(id, R.id.widget_list_view);
}
}
@ReactMethod(isBlockingSynchronousMethod = true)
public boolean isGestureNavigationEnabled() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
try {
String navBarMode = Settings.Secure.getString(
mContext.getContentResolver(),
"navigation_mode"
);
return "2".equals(navBarMode);
} catch (Exception e) {
return false;
}
} else {
return false;
}
}
}

View File

@@ -1,5 +1,5 @@
- Improved editor UI for better usability
- Improved UX for creating and editing reminders
- Minor bug fixes and improvements
- New and improved search with highlighting
- Fixed app crashing on android 16 devices
- Many bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -5,5 +5,5 @@ rootProject.name = 'Notesnook'
include ':app'
includeBuild('../../node_modules/@react-native/gradle-plugin')
include ":lazysodium-android"
project(':lazysodium-android').projectDir = new File(rootProject.projectDir, '../../node_modules/@ammarahmed/react-native-sodium/android/lazysodium-android/app')
include ":lazysodium"
project(':lazysodium').projectDir = new File(rootProject.projectDir, '../../node_modules/@ammarahmed/react-native-sodium/android/lazysodium')

View File

@@ -124,6 +124,7 @@
6529D2B0257B4A2900B49BC3 /* NotesnookDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = NotesnookDebug.entitlements; path = Notesnook/NotesnookDebug.entitlements; sourceTree = "<group>"; };
656835802BB29A8300144BAB /* OpenSans-Italic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-Italic.ttf"; path = "../../../../packages/editor-mobile/public/fonts/OpenSans-Italic.ttf"; sourceTree = "<group>"; };
6593E4A2281C345400492C50 /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = Notesnook/AppDelegate.mm; sourceTree = "<group>"; };
6594DDF22CD8BFE8007F5EC2 /* BetterTrigram.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = BetterTrigram.xcframework; sourceTree = "<group>"; };
659BE46625E11A5100E05671 /* notesnook-text.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "notesnook-text.png"; path = "Notesnook/Images.xcassets/notesnook-text.png"; sourceTree = "<group>"; };
65A7F34F255689AD00699170 /* Notesnook.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Notesnook.entitlements; path = Notesnook/Notesnook.entitlements; sourceTree = "<group>"; };
65AA857725E6DDEC00772A01 /* NotesWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotesWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -255,6 +256,7 @@
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
isa = PBXGroup;
children = (
6594DDF22CD8BFE8007F5EC2 /* BetterTrigram.xcframework */,
6515C42E2580AA2F00E83E39 /* StoreKit.framework */,
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
@@ -1089,7 +1091,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2147;
CURRENT_PROJECT_VERSION = 2136;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1163,7 +1165,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.2.12;
MARKETING_VERSION = 3.2.2;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1194,7 +1196,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2147;
CURRENT_PROJECT_VERSION = 2136;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1268,7 +1270,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.2.12;
MARKETING_VERSION = 3.2.2;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -1427,7 +1429,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2147;
CURRENT_PROJECT_VERSION = 2136;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1439,7 +1441,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.2.12;
MARKETING_VERSION = 3.2.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1470,7 +1472,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2147;
CURRENT_PROJECT_VERSION = 2136;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1483,7 +1485,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.2.12;
MARKETING_VERSION = 3.2.2;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1513,16 +1515,10 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2147;
CURRENT_PROJECT_VERSION = 2136;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"${PODS_ROOT}/hermes-engine/destroot/Library/Frameworks/universal\"",
"\"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built\"",
"\"${PROJECT_DIR}\"",
);
GCC_C_LANGUAGE_STANDARD = gnu11;
HEADER_SEARCH_PATHS = (
"$(inherited)",
@@ -1593,8 +1589,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
MARKETING_VERSION = 3.2.12;
MARKETING_VERSION = 3.2.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1625,17 +1620,11 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2147;
CURRENT_PROJECT_VERSION = 2136;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"${PODS_ROOT}/hermes-engine/destroot/Library/Frameworks/universal\"",
"\"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built\"",
"\"${PROJECT_DIR}\"",
);
GCC_C_LANGUAGE_STANDARD = gnu11;
HEADER_SEARCH_PATHS = (
"$(inherited)",
@@ -1706,8 +1695,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
MARKETING_VERSION = 3.2.12;
MARKETING_VERSION = 3.2.2;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -1325,28 +1325,6 @@ PODS:
- react-native-pdf (6.7.7):
- React-Core
- react-native-quick-sqlite (8.2.7):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2024.11.18.00)
- RCTRequired
- RCTTypeSafety
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- react-native-quick-sqlite/lexbor (= 8.2.7)
- React-NativeModulesApple
- React-RCTFabric
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-quick-sqlite/lexbor (8.2.7):
- DoubleConversion
- glog
- hermes-engine
@@ -1374,7 +1352,7 @@ PODS:
- SDWebImage (~> 5.11.1)
- react-native-share-extension (2.9.0):
- React
- react-native-sodium (1.6.5):
- react-native-sodium (1.6.3):
- React
- react-native-theme-switch-animation (0.6.0):
- DoubleConversion
@@ -2431,11 +2409,11 @@ SPEC CHECKSUMS:
react-native-orientation-locker: cc6f357b289a2e0dd2210fea0c52cb8e0727fdaa
react-native-pager-view: 5aaf51a9338f7997b8acee0e0febfddd1ade5c0a
react-native-pdf: 6a51a22ccefb23eb93298771e4bf090913e86d70
react-native-quick-sqlite: 1bfc7f1e9acbe9a5aa5c4cc81712e9bde3ab7672
react-native-quick-sqlite: 6aebe0e1c264c48450059e271e5d2404013cea93
react-native-safe-area-context: 9d72abf6d8473da73033b597090a80b709c0b2f1
react-native-screenguard: 82437eeb0086a90b5e5d7e54130bb04fb406373e
react-native-share-extension: bcb7e466390a9e50c742f4b1019d6f181aedd7ad
react-native-sodium: 285eec063e4232cb67347ef6a434b85e588d38cb
react-native-sodium: aa26d9f46dcfdebd92d3af793b74fbb234a4d75d
react-native-theme-switch-animation: d90fe2de0d9e87a63cd6235d98cba6e7054e9a10
react-native-webview: 079eca50edf657503318b66687dadfb903731aa8
React-nativeconfig: ecf4dc92c40b97e2b3f0c619938f78bfd6507b08

View File

@@ -66,7 +66,7 @@
"react-native-screenguard": "^1.0.0",
"@formatjs/intl-locale": "4.0.0",
"@formatjs/intl-pluralrules": "5.2.14",
"@ammarahmed/react-native-sodium": "^1.6.5",
"@ammarahmed/react-native-sodium": "^1.6.4",
"@react-native-community/datetimepicker": "^8.2.0",
"react-native-exit-app": "github:ammarahm-ed/react-native-exit-app",
"react-native-begin-background-task": "github:blockfirm/react-native-begin-background-task",

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/mobile",
"version": "3.2.10",
"version": "3.2.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/mobile",
"version": "3.2.10",
"version": "3.2.2",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -1098,7 +1098,7 @@
"refractor": "^4.8.1",
"sqlite-better-trigram": "^0.0.3",
"sqlite-regex": "^0.2.4-alpha.1",
"sqlite3-fts5-html": "^0.0.4",
"sqlite3-fts5-html": "^0.0.3",
"vitest": "2.1.8",
"vitest-fetch-mock": "^0.2.2",
"ws": "^8.13.0"
@@ -28324,7 +28324,7 @@
"@ammarahmed/react-native-eventsource": "1.1.0",
"@ammarahmed/react-native-fingerprint-scanner": "^5.0.0",
"@ammarahmed/react-native-share-extension": "^2.9.0",
"@ammarahmed/react-native-sodium": "^1.6.5",
"@ammarahmed/react-native-sodium": "^1.6.4",
"@bam.tech/react-native-image-resizer": "3.0.11",
"@callstack/repack": "~5.1.2",
"@formatjs/intl-locale": "4.0.0",
@@ -28478,9 +28478,9 @@
}
},
"node_modules/@ammarahmed/react-native-sodium": {
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-sodium/-/react-native-sodium-1.6.5.tgz",
"integrity": "sha512-x8YkHWHaHiB4QomjTAXyw/aLjrsFMWumJxSRrEeUOVeArVszbrwZ88B+2zAq+sPZ+HSMi/YrnrvgzbwvOCLnNA==",
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-sodium/-/react-native-sodium-1.6.4.tgz",
"integrity": "sha512-+OIBABV/8IPXVQPRxiub0WW86X/bs14GHnQ1VqtxWGv7phwfyOVzi9c1/LDeRqjCDKBAYHOv2a5PLHZb2RzeGw==",
"license": "ISC"
},
"node_modules/@ampproject/remapping": {

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "3.2.12",
"version": "3.2.2",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [

View File

@@ -1,39 +0,0 @@
diff --git a/node_modules/@react-navigation/core/lib/commonjs/useNavigationCache.js b/node_modules/@react-navigation/core/lib/commonjs/useNavigationCache.js
index c7f1df6..0141f28 100644
--- a/node_modules/@react-navigation/core/lib/commonjs/useNavigationCache.js
+++ b/node_modules/@react-navigation/core/lib/commonjs/useNavigationCache.js
@@ -109,7 +109,7 @@ function useNavigationCache(_ref) {
})),
isFocused: () => {
const state = getState();
- if (state.routes[state.index].key !== route.key) {
+ if (state.routes[state.index]?.key !== route.key) {
return false;
}
diff --git a/node_modules/@react-navigation/core/lib/module/useNavigationCache.js b/node_modules/@react-navigation/core/lib/module/useNavigationCache.js
index c4d65c6..89abe63 100644
--- a/node_modules/@react-navigation/core/lib/module/useNavigationCache.js
+++ b/node_modules/@react-navigation/core/lib/module/useNavigationCache.js
@@ -100,7 +100,7 @@ export default function useNavigationCache(_ref) {
})),
isFocused: () => {
const state = getState();
- if (state.routes[state.index].key !== route.key) {
+ if (state.routes[state.index]?.key !== route.key) {
return false;
}
diff --git a/node_modules/@react-navigation/core/src/useNavigationCache.tsx b/node_modules/@react-navigation/core/src/useNavigationCache.tsx
index 390120a..1b0f2d1 100644
--- a/node_modules/@react-navigation/core/src/useNavigationCache.tsx
+++ b/node_modules/@react-navigation/core/src/useNavigationCache.tsx
@@ -157,7 +157,7 @@ export default function useNavigationCache<
isFocused: () => {
const state = getState();
- if (state.routes[state.index].key !== route.key) {
+ if (state.routes[state.index]?.key !== route.key) {
return false;
}

View File

@@ -1,24 +1,3 @@
diff --git a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
index 77c81f0..be34558 100644
--- a/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
+++ b/node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js
@@ -156,7 +156,8 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
if (
Platform.OS === 'ios' &&
- this._windowWidth !== this._keyboardEvent.endCoordinates.width
+ this._windowWidth !== this._keyboardEvent.endCoordinates.width &&
+ this._windowWidth > this._keyboardEvent.endCoordinates.width
) {
// The keyboard is not the standard bottom-of-the-screen keyboard. For example, floating keyboard on iPadOS.
this._setBottom(0);
diff --git a/node_modules/react-native/scripts/.packager.env b/node_modules/react-native/scripts/.packager.env
new file mode 100644
index 0000000..361f5fb
--- /dev/null
+++ b/node_modules/react-native/scripts/.packager.env
@@ -0,0 +1 @@
+export RCT_METRO_PORT=8081
diff --git a/node_modules/react-native/scripts/packager.sh b/node_modules/react-native/scripts/packager.sh
index 00d6ebc..6846832 100755
--- a/node_modules/react-native/scripts/packager.sh

View File

@@ -1,12 +0,0 @@
diff --git a/node_modules/react-native-actions-sheet/dist/src/index.js b/node_modules/react-native-actions-sheet/dist/src/index.js
index 0e124da..3703153 100644
--- a/node_modules/react-native-actions-sheet/dist/src/index.js
+++ b/node_modules/react-native-actions-sheet/dist/src/index.js
@@ -1088,6 +1088,7 @@ export default forwardRef(function ActionSheet(_a, ref) {
* Always true, it causes issue with keyboard handling.
*/
statusBarTranslucent: true,
+ navigationBarTranslucent: true
}
: {
testID: ((_b = props.testIDs) === null || _b === void 0 ? void 0 : _b.root) || props.testID,

View File

@@ -36,37 +36,3 @@ index 3dfe1dc..70ef9da 100644
+ implementation 'io.legere:pdfiumandroid:1.0.32'
implementation 'com.google.code.gson:gson:2.8.5'
}
diff --git a/node_modules/react-native-pdf/android/src/main/java/org/wonday/pdf/PdfView.java b/node_modules/react-native-pdf/android/src/main/java/org/wonday/pdf/PdfView.java
index 7b7a125..361924b 100644
--- a/node_modules/react-native-pdf/android/src/main/java/org/wonday/pdf/PdfView.java
+++ b/node_modules/react-native-pdf/android/src/main/java/org/wonday/pdf/PdfView.java
@@ -12,6 +12,8 @@ import java.io.File;
import android.content.ContentResolver;
import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
import android.util.SizeF;
import android.view.View;
import android.view.ViewGroup;
@@ -105,7 +107,7 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
TopChangeEvent tce = new TopChangeEvent(surfaceId, getId(), event);
if (dispatcher != null) {
- dispatcher.dispatchEvent(tce);
+ new Handler(Looper.getMainLooper()).postDelayed(() -> dispatcher.dispatchEvent(tce), 10);
}
// ReactContext reactContext = (ReactContext)this.getContext();
diff --git a/node_modules/react-native-pdf/index.js b/node_modules/react-native-pdf/index.js
index 56df005..dd14412 100644
--- a/node_modules/react-native-pdf/index.js
+++ b/node_modules/react-native-pdf/index.js
@@ -364,7 +364,6 @@ export default class Pdf extends Component {
}
_onChange = (event) => {
-
let message = event.nativeEvent.message.split('|');
//__DEV__ && console.log("onChange: " + message);
if (message.length > 0) {

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
FROM oven/bun:1.2.21-slim
FROM oven/bun:1.2.2-alpine
RUN mkdir -p /home/bun/app && chown -R bun:bun /home/bun/app

View File

@@ -107,12 +107,10 @@ function generateTableOfContents() {
export const MonographPage = ({
monograph,
encodedKey,
apiHost
encodedKey
}: {
monograph: Monograph;
encodedKey?: string;
apiHost: string;
}) => {
const [reportDialogVisible, setReportDialogVisible] = useState(false);
const [tableOfContents, setTableOfContents] = useState<TableOfContent[]>([]);
@@ -229,7 +227,7 @@ export const MonographPage = ({
)}
<Image
sx={{ display: "none" }}
src={`${apiHost}/monographs/${monograph.id}/view`}
src={`https://api.notesnook.com/monographs/${monograph.id}/view`}
/>
</Flex>
</Box>

View File

@@ -90,7 +90,7 @@ function getContentSecurityPolicy(nonce?: string) {
const connect_src =
process.env.NODE_ENV === "development"
? "'self' ws://localhost:*"
: "'self' https://notesnook.com/api/v1/reports/submit";
: "'self'";
return (
`script-src ${script_src} 'strict-dynamic'; ` +

View File

@@ -85,8 +85,7 @@ export async function loader({ params }: LoaderFunctionArgs) {
const metadata = getMonographMetadata(monograph);
return {
monograph,
metadata,
apiHost: API_HOST
metadata
};
} catch (e) {
// console.error(e);
@@ -97,24 +96,19 @@ export async function loader({ params }: LoaderFunctionArgs) {
fullDescription: "This monograph does not exist.",
shortDescription: "This monograph does not exist.",
datePublished: ""
},
apiHost: API_HOST
}
};
}
}
export default function MonographPost() {
const { monograph, apiHost } = useLoaderData<typeof loader>();
const { monograph } = useLoaderData<typeof loader>();
const [_, hashParams] = useHashLocation();
return (
<>
{monograph ? (
<MonographPage
monograph={monograph}
encodedKey={hashParams.key}
apiHost={apiHost}
/>
<MonographPage monograph={monograph} encodedKey={hashParams.key} />
) : (
<>
<Header />

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/monograph",
"version": "1.2.5",
"version": "1.2.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/monograph",
"version": "1.2.5",
"version": "1.2.4",
"dependencies": {
"@emotion/cache": "11.11.0",
"@emotion/react": "11.11.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/monograph",
"version": "1.2.5",
"version": "1.2.4",
"private": true,
"sideEffects": false,
"type": "module",

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/theme-builder",
"version": "1.4.0",
"version": "1.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/theme-builder",
"version": "1.4.0",
"version": "1.3.0",
"license": "GPL-3.0-or-later",
"dependencies": {
"@emotion/react": "11.11.1",

View File

@@ -1,7 +1,7 @@
{
"name": "@notesnook/theme-builder",
"description": "Your private note taking space",
"version": "1.4.0",
"version": "1.3.0",
"private": true,
"main": "./src/app.js",
"homepage": "https://notesnook.com/",

View File

@@ -430,12 +430,12 @@ test("control + alt + right arrow should go to next note", async ({ page }) => {
await note1?.openNote();
await note2?.openNote(true);
await page.keyboard.press("ControlOrMeta+Alt+ArrowRight");
await page.keyboard.press("Control+Alt+ArrowRight");
expect(await notes.editor.getTitle()).toBe("Note 1");
expect(await notes.editor.getContent("text")).toBe("Note 1 content");
await page.keyboard.press("ControlOrMeta+Alt+ArrowRight");
await page.keyboard.press("Control+Alt+ArrowRight");
expect(await notes.editor.getTitle()).toBe("Note 2");
expect(await notes.editor.getContent("text")).toBe("Note 2 content");
@@ -458,12 +458,12 @@ test("control + alt + left arrow should go to previous note", async ({
await note1?.openNote();
await note2?.openNote(true);
await page.keyboard.press("ControlOrMeta+Alt+ArrowLeft");
await page.keyboard.press("Control+Alt+ArrowLeft");
expect(await notes.editor.getTitle()).toBe("Note 1");
expect(await notes.editor.getContent("text")).toBe("Note 1 content");
await page.keyboard.press("ControlOrMeta+Alt+ArrowLeft");
await page.keyboard.press("Control+Alt+ArrowLeft");
expect(await notes.editor.getTitle()).toBe("Note 2");
expect(await notes.editor.getContent("text")).toBe("Note 2 content");

View File

@@ -1 +1 @@
An edit I madeThis is Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1
This is Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1An edit I made

View File

@@ -1 +1 @@
An edit I madeThis is Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1
This is Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1An edit I made

View File

@@ -1 +1 @@
An edit I madeThis is Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1
This is Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1An edit I made

View File

@@ -162,9 +162,9 @@ export class SettingsViewModel {
await appLockSwitch.click();
await fillPasswordDialog(this.page, userPassword);
await this.page.waitForTimeout(500);
await this.page.waitForTimeout(100);
await fillConfirmPasswordDialog(this.page, appLockPassword);
await this.page.waitForTimeout(500);
await this.page.waitForTimeout(100);
}
async disableAppLock(appLockPassword: string) {

View File

@@ -91,7 +91,6 @@ export async function fillReminderDialog(
}
await confirmDialog(dialog);
await dialog.waitFor({ state: "hidden" });
}
export async function fillItemDialog(page: Page, item: Item) {
@@ -134,6 +133,7 @@ export async function fillConfirmPasswordDialog(page: Page, password: string) {
export async function confirmDialog(dialog: Locator) {
const dialogConfirm = dialog.locator(getTestId("dialog-yes"));
await dialogConfirm.click();
// await dialogConfirm.waitFor({ state: "detached" });
}
export async function denyDialog(page: Page) {

Some files were not shown because too many files have changed in this diff Show More