mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-29 18:19:38 +02:00
Compare commits
239 Commits
theme-engi
...
fix-beta-t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
993b96901c | ||
|
|
8c1629f7dd | ||
|
|
9a31c07ac5 | ||
|
|
11f39316b5 | ||
|
|
f157229e00 | ||
|
|
a0f25c4e6f | ||
|
|
003b3d2a84 | ||
|
|
1775e0bda1 | ||
|
|
d5038ac631 | ||
|
|
7eab77ba01 | ||
|
|
89413c56e2 | ||
|
|
4d89643203 | ||
|
|
e119590249 | ||
|
|
ace2dfac30 | ||
|
|
5843df0ce8 | ||
|
|
403ed8adb6 | ||
|
|
1eb5c8bc46 | ||
|
|
46f7ec5e00 | ||
|
|
52adece147 | ||
|
|
de50be9d1b | ||
|
|
0f9e32b49f | ||
|
|
b4c15adf5d | ||
|
|
a3d3886c51 | ||
|
|
0cfa7ce774 | ||
|
|
8299ea909e | ||
|
|
8b056fe610 | ||
|
|
511c40db5c | ||
|
|
42d0d42188 | ||
|
|
c80a998444 | ||
|
|
0a10df75d1 | ||
|
|
40d755d191 | ||
|
|
0409a610ab | ||
|
|
aa9cd23cf3 | ||
|
|
12d89114a4 | ||
|
|
d5b679187a | ||
|
|
463ba3754a | ||
|
|
2fa8fd14e7 | ||
|
|
fac788d2a9 | ||
|
|
6554f900d7 | ||
|
|
81d3ec83d9 | ||
|
|
339c5a357b | ||
|
|
b484992037 | ||
|
|
87e42e98c6 | ||
|
|
51bef01609 | ||
|
|
6a49244614 | ||
|
|
dad2b61a74 | ||
|
|
fabc595f3d | ||
|
|
a700787cf3 | ||
|
|
fb26229715 | ||
|
|
c4f894f8d0 | ||
|
|
ddd1c19c64 | ||
|
|
79cb3bdba4 | ||
|
|
8447c2150b | ||
|
|
bc465971be | ||
|
|
f8ce23aa11 | ||
|
|
84004c4d73 | ||
|
|
2b6aa9b89b | ||
|
|
54b8a3e789 | ||
|
|
7ff5f0001c | ||
|
|
d384aa9dcf | ||
|
|
1190da7c91 | ||
|
|
6561791530 | ||
|
|
26e233d3eb | ||
|
|
0417981538 | ||
|
|
81fc56937e | ||
|
|
cb778d7e35 | ||
|
|
8442b43715 | ||
|
|
28b3306506 | ||
|
|
f51f45aeb2 | ||
|
|
7f1184736c | ||
|
|
e4c8e7a0c5 | ||
|
|
44e4722de2 | ||
|
|
7c2b0c5947 | ||
|
|
6ea912ad0e | ||
|
|
124bbb6ac1 | ||
|
|
30d6345152 | ||
|
|
60ff7b2f97 | ||
|
|
c6e90142bb | ||
|
|
1289a025a5 | ||
|
|
18d0b72785 | ||
|
|
0157cf496f | ||
|
|
3450f2d19d | ||
|
|
e509c82807 | ||
|
|
d5afd6f32e | ||
|
|
fb34dd2864 | ||
|
|
0cd1ce3431 | ||
|
|
a24447d569 | ||
|
|
0bf26afa33 | ||
|
|
49455e8054 | ||
|
|
1136e43115 | ||
|
|
4b74cba4e4 | ||
|
|
c8a77934f2 | ||
|
|
e1739e04d1 | ||
|
|
a5d39a18a7 | ||
|
|
77eb661b0e | ||
|
|
667d0d16ea | ||
|
|
445ea660c3 | ||
|
|
7a35640739 | ||
|
|
f1650fd383 | ||
|
|
5d67a554d6 | ||
|
|
380bfba776 | ||
|
|
ed6d125391 | ||
|
|
adaa5fd56d | ||
|
|
0a8db237eb | ||
|
|
6d723e022b | ||
|
|
dc3b739719 | ||
|
|
8d67599278 | ||
|
|
c8da5770c8 | ||
|
|
51c11d9d1a | ||
|
|
414c164100 | ||
|
|
aa5fd6d946 | ||
|
|
c89f74d35b | ||
|
|
8c2fdf8554 | ||
|
|
f2438d9524 | ||
|
|
54e7b29716 | ||
|
|
1d7f4036ec | ||
|
|
a2d5eda538 | ||
|
|
1b6a27376c | ||
|
|
8431150f0a | ||
|
|
ed5107afaa | ||
|
|
e52bdd7ca5 | ||
|
|
26e50848da | ||
|
|
41fa52c9d5 | ||
|
|
885aa3035e | ||
|
|
b20c20983a | ||
|
|
42994847e9 | ||
|
|
b59a12e743 | ||
|
|
f67792cd30 | ||
|
|
60ba345e6c | ||
|
|
0074548b84 | ||
|
|
5af5549173 | ||
|
|
f9f0235d7b | ||
|
|
cd51420931 | ||
|
|
000d11976b | ||
|
|
392261c208 | ||
|
|
71aee40f8d | ||
|
|
565ef2c321 | ||
|
|
2d5c8c9e6d | ||
|
|
800f983688 | ||
|
|
7291e54db7 | ||
|
|
794b1c70eb | ||
|
|
330b227027 | ||
|
|
59a66a63c5 | ||
|
|
ceb28f08c5 | ||
|
|
7287754218 | ||
|
|
80d2f64f10 | ||
|
|
9951b83539 | ||
|
|
66d50be35a | ||
|
|
9eb5a4cee4 | ||
|
|
3c575c01ad | ||
|
|
643028f5d8 | ||
|
|
3831b9409e | ||
|
|
985ae848d3 | ||
|
|
0c8c8b6040 | ||
|
|
a01c084045 | ||
|
|
7577adf09d | ||
|
|
394002eb4c | ||
|
|
5c386148a4 | ||
|
|
3558d3db93 | ||
|
|
2bed812df1 | ||
|
|
c5db2e0ea9 | ||
|
|
6cd1d7c41b | ||
|
|
36662d58da | ||
|
|
e85cc6ac56 | ||
|
|
a17d2d69ef | ||
|
|
3ec615a6ec | ||
|
|
8eadc6aea1 | ||
|
|
506678e72c | ||
|
|
aa3561555e | ||
|
|
405a8aaad0 | ||
|
|
7edd45419e | ||
|
|
5af028698c | ||
|
|
ce822e7538 | ||
|
|
9936bee8e0 | ||
|
|
b903e8461d | ||
|
|
4d2c42330e | ||
|
|
0ea37fdb31 | ||
|
|
22bf738d21 | ||
|
|
922ba7d192 | ||
|
|
c38a98c239 | ||
|
|
0f9452f6ca | ||
|
|
5cc9c6e3e4 | ||
|
|
5da30faf71 | ||
|
|
99d984f516 | ||
|
|
20f0760f13 | ||
|
|
20ffa9bee5 | ||
|
|
bdd003f177 | ||
|
|
a45a960bfd | ||
|
|
fbf56f2663 | ||
|
|
0e7ac37b9d | ||
|
|
bcf0d86605 | ||
|
|
954f894738 | ||
|
|
908def1b69 | ||
|
|
f26f638ad4 | ||
|
|
700f47fd49 | ||
|
|
3d0eaacaac | ||
|
|
c76118d9d6 | ||
|
|
148c39158b | ||
|
|
89c2d45a4b | ||
|
|
c30c54ab97 | ||
|
|
734cfba45d | ||
|
|
3b363a1b20 | ||
|
|
a747bafa69 | ||
|
|
9d0c11b051 | ||
|
|
bd9e997ef1 | ||
|
|
f222327a89 | ||
|
|
4e5351134f | ||
|
|
ec1d5cc211 | ||
|
|
8f41e25ac1 | ||
|
|
2d90caafc6 | ||
|
|
a9f36076b3 | ||
|
|
8365e557d5 | ||
|
|
080160ca70 | ||
|
|
e6164b0b3e | ||
|
|
26d786a368 | ||
|
|
c6172644f0 | ||
|
|
5810947a5d | ||
|
|
708c2d373d | ||
|
|
d11a9b2219 | ||
|
|
32b8b008f2 | ||
|
|
e066d1ce8d | ||
|
|
4f3f19a785 | ||
|
|
6c3ea4517d | ||
|
|
1e7e3b8c42 | ||
|
|
a54c18e89b | ||
|
|
ed4d5efea6 | ||
|
|
4bab279345 | ||
|
|
ec32eeba0b | ||
|
|
2b74e2afb2 | ||
|
|
c4a9d244a9 | ||
|
|
6643fb0d63 | ||
|
|
b2a3949ad0 | ||
|
|
93c1a44285 | ||
|
|
62c4fb328c | ||
|
|
578fb72ddc | ||
|
|
19faaea10e | ||
|
|
9951aae398 | ||
|
|
0963559a5f | ||
|
|
622294b807 |
@@ -33,7 +33,8 @@ const SCOPES = [
|
||||
"misc",
|
||||
"common",
|
||||
"global",
|
||||
"docs"
|
||||
"docs",
|
||||
"themebuilder"
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
|
||||
31
.github/workflows/theme-builder.publish.yml
vendored
Normal file
31
.github/workflows/theme-builder.publish.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Publish @notesnook/theme-builder
|
||||
|
||||
on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node-with-cache
|
||||
|
||||
- name: Install packages
|
||||
run: |
|
||||
npm ci --ignore-scripts --prefer-offline --no-audit
|
||||
npm run bootstrap -- --scope=themebuilder
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
|
||||
echo "CLOUDFLARE_ACCOUNT_ID=${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" >> $GITHUB_ENV
|
||||
echo "CLOUDFLARE_API_TOKEN=${{ secrets.CLOUDFLARE_API_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Generate build
|
||||
run: npm run build:theme-builder
|
||||
|
||||
- name: Publish to Cloudflare Pages
|
||||
run: npx --yes wrangler pages deploy --project-name=notesnook-theme-builder ./apps/theme-builder/build/
|
||||
4
.github/workflows/vericrypt.publish.yml
vendored
4
.github/workflows/vericrypt.publish.yml
vendored
@@ -24,8 +24,8 @@ jobs:
|
||||
echo "CLOUDFLARE_ACCOUNT_ID=${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" >> $GITHUB_ENV
|
||||
echo "CLOUDFLARE_API_TOKEN=${{ secrets.CLOUDFLARE_API_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Generate desktop build
|
||||
- name: Generate build
|
||||
run: npm run build:vericrypt
|
||||
|
||||
- name: Publish on Cloudflare Pages
|
||||
run: npx --yes wrangler pages publish --project-name=vericrypt ./apps/vericrypt/build/
|
||||
run: npx --yes wrangler pages deploy --project-name=vericrypt ./apps/vericrypt/build/
|
||||
|
||||
67
.github/workflows/web.beta.publish.yml
vendored
Normal file
67
.github/workflows/web.beta.publish.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
name: Publish @notesnook/web Beta
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- "apps/mobile/**"
|
||||
- "packages/editor-mobile/**"
|
||||
- "apps/desktop/**"
|
||||
- "apps/vericrypt/**"
|
||||
- "servers/**"
|
||||
- "packages/clipper/**"
|
||||
- "extensions/web-clipper/**"
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
- ready_for_review
|
||||
- reopened
|
||||
- opened
|
||||
- synchronize
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- "apps/mobile/**"
|
||||
- "packages/editor-mobile/**"
|
||||
- "apps/desktop/**"
|
||||
- "apps/vericrypt/**"
|
||||
- "servers/**"
|
||||
- "packages/clipper/**"
|
||||
- "extensions/web-clipper/**"
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node-with-cache
|
||||
|
||||
- name: Install packages
|
||||
run: |
|
||||
npm ci --ignore-scripts --prefer-offline --no-audit
|
||||
npm run bootstrap -- --scope=web
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
|
||||
echo "CLOUDFLARE_ACCOUNT_ID=${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" >> $GITHUB_ENV
|
||||
echo "CLOUDFLARE_API_TOKEN=${{ secrets.CLOUDFLARE_API_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Generate beta build
|
||||
run: npm run build:beta:web
|
||||
|
||||
- name: Publish to Cloudflare Pages
|
||||
uses: unlike-ltd/github-actions-cloudflare-pages@v0.1.1
|
||||
id: pages
|
||||
with:
|
||||
cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
cloudflare-project-name: notesnook-beta-app
|
||||
directory: ./apps/web/build
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
github-environment: ${{ (github.ref == 'refs/heads/master' && 'beta') || 'preview' }}
|
||||
6
.github/workflows/web.publish.yml
vendored
6
.github/workflows/web.publish.yml
vendored
@@ -24,8 +24,8 @@ jobs:
|
||||
echo "CLOUDFLARE_ACCOUNT_ID=${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" >> $GITHUB_ENV
|
||||
echo "CLOUDFLARE_API_TOKEN=${{ secrets.CLOUDFLARE_API_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Generate desktop build
|
||||
- name: Generate build
|
||||
run: npm run build:web
|
||||
|
||||
- name: Publish on Cloudflare Pages
|
||||
run: npx --yes wrangler pages publish --project-name=notesnook-app ./apps/web/build/
|
||||
- name: Publish to Cloudflare Pages
|
||||
run: npx --yes wrangler pages deploy --project-name=notesnook-app ./apps/web/build/
|
||||
|
||||
31
apps/desktop/global.d.ts
vendored
31
apps/desktop/global.d.ts
vendored
@@ -19,9 +19,40 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
/* eslint-disable no-var */
|
||||
|
||||
import { BrowserWindow } from "electron";
|
||||
import {
|
||||
type FormData as FormDataType,
|
||||
type Headers as HeadersType,
|
||||
type Request as RequestType,
|
||||
type Response as ResponseType
|
||||
} from "undici";
|
||||
|
||||
declare global {
|
||||
var window: BrowserWindow | null;
|
||||
var RELEASE: boolean;
|
||||
var MAC_APP_STORE: boolean;
|
||||
|
||||
// Re-export undici fetch function and various classes to global scope.
|
||||
// These are classes and functions expected to be at global scope according to Node.js v18 API
|
||||
// documentation.
|
||||
// See: https://nodejs.org/dist/latest-v18.x/docs/api/globals.html
|
||||
// eslint-disable-next-line no-var
|
||||
export var {
|
||||
FormData,
|
||||
Headers,
|
||||
Request,
|
||||
Response,
|
||||
fetch
|
||||
}: typeof import("undici");
|
||||
|
||||
type FormData = FormDataType;
|
||||
type Headers = HeadersType;
|
||||
type Request = RequestType;
|
||||
type Response = ResponseType;
|
||||
}
|
||||
|
||||
// NOTE: the import in the global block above needs to be a var for this to work properly.
|
||||
globalThis.fetch = fetch;
|
||||
globalThis.FormData = FormData;
|
||||
globalThis.Headers = Headers;
|
||||
globalThis.Request = Request;
|
||||
globalThis.Response = Response;
|
||||
|
||||
1579
apps/desktop/package-lock.json
generated
1579
apps/desktop/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "2.5.8",
|
||||
"version": "2.6.1",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./dist/index.js",
|
||||
@@ -22,15 +22,15 @@
|
||||
"zod": "^3.21.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.15.0",
|
||||
"@types/node": "18.16.1",
|
||||
"@types/yargs": "^17.0.24",
|
||||
"chokidar": "^3.5.3",
|
||||
"electron": "24.5.1",
|
||||
"electron-builder": "24.4.0",
|
||||
"electron": "^26.1.0",
|
||||
"electron-builder": "^24.6.3",
|
||||
"electron-builder-notarize": "^1.5.1",
|
||||
"esbuild": "^0.17.19",
|
||||
"node-fetch": "^3.3.1",
|
||||
"tree-kill": "^1.2.2"
|
||||
"tree-kill": "^1.2.2",
|
||||
"undici": "^5.23.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"dmg-license": "^1.0.11"
|
||||
|
||||
@@ -52,21 +52,21 @@ export const osIntegrationRouter = t.router({
|
||||
|
||||
privacyMode: t.procedure.query(() => config.privacyMode),
|
||||
setPrivacyMode: t.procedure
|
||||
.input(z.boolean().optional())
|
||||
.mutation(({ input: privacyMode }) => {
|
||||
.input(z.object({ enabled: z.boolean() }))
|
||||
.mutation(({ input: { enabled } }) => {
|
||||
if (!globalThis.window || !["win32", "darwin"].includes(process.platform))
|
||||
return;
|
||||
|
||||
globalThis.window.setContentProtection(!!privacyMode);
|
||||
globalThis.window.setContentProtection(enabled);
|
||||
|
||||
if (process.platform === "win32") {
|
||||
globalThis.window.setThumbnailClip(
|
||||
privacyMode
|
||||
enabled
|
||||
? { x: 0, y: 0, width: 1, height: 1 }
|
||||
: { x: 0, y: 0, width: 0, height: 0 }
|
||||
);
|
||||
}
|
||||
config.privacyMode = !!privacyMode;
|
||||
config.privacyMode = enabled;
|
||||
}),
|
||||
|
||||
desktopIntegration: t.procedure.query(() => config.desktopSettings),
|
||||
|
||||
@@ -114,9 +114,9 @@ export const spellCheckerRouter = t.router({
|
||||
globalThis.window?.webContents.session.setSpellCheckerLanguages(languages)
|
||||
),
|
||||
toggle: t.procedure
|
||||
.input(z.boolean().optional())
|
||||
.mutation(({ input: enabled }) => {
|
||||
globalThis.window?.webContents.session.setSpellCheckerEnabled(!!enabled);
|
||||
config.isSpellCheckerEnabled = !!enabled;
|
||||
.input(z.object({ enabled: z.boolean() }))
|
||||
.mutation(({ input: { enabled } }) => {
|
||||
globalThis.window?.webContents.session.setSpellCheckerEnabled(enabled);
|
||||
config.isSpellCheckerEnabled = enabled;
|
||||
})
|
||||
});
|
||||
|
||||
@@ -41,10 +41,9 @@ export const updaterRouter = t.router({
|
||||
}),
|
||||
|
||||
toggleAutoUpdates: t.procedure
|
||||
.input(z.boolean().optional())
|
||||
.mutation(({ input }) => {
|
||||
config.automaticUpdates =
|
||||
input === undefined ? !config.automaticUpdates : input;
|
||||
.input(z.object({ enabled: z.boolean() }))
|
||||
.mutation(({ input: { enabled } }) => {
|
||||
config.automaticUpdates = enabled;
|
||||
}),
|
||||
|
||||
onChecking: createSubscription("checking-for-update"),
|
||||
|
||||
@@ -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 { app, BrowserWindow, nativeTheme, session, shell } from "electron";
|
||||
import { app, BrowserWindow, nativeTheme, shell } from "electron";
|
||||
import { isDevelopment } from "./utils";
|
||||
import { registerProtocol, PROTOCOL_URL } from "./utils/protocol";
|
||||
import { configureAutoUpdater } from "./utils/autoupdater";
|
||||
@@ -33,6 +33,13 @@ import { createIPCHandler } from "electron-trpc/main";
|
||||
import { router, api } from "./api";
|
||||
import { config } from "./utils/config";
|
||||
import path from "path";
|
||||
import { bringToFront } from "./utils/bring-to-front";
|
||||
|
||||
// only run a single instance
|
||||
if (!MAC_APP_STORE && !app.requestSingleInstanceLock()) {
|
||||
console.log("Another instance is already running!");
|
||||
app.exit();
|
||||
}
|
||||
|
||||
if (process.platform == "win32" && process.env.PORTABLE_EXECUTABLE_DIR) {
|
||||
console.log("Portable app: true");
|
||||
@@ -42,11 +49,6 @@ if (process.platform == "win32" && process.env.PORTABLE_EXECUTABLE_DIR) {
|
||||
app.setPath("userData", path.join(root, "UserData"));
|
||||
}
|
||||
|
||||
// only run a single instance
|
||||
if (!MAC_APP_STORE && !app.requestSingleInstanceLock()) {
|
||||
app.exit();
|
||||
}
|
||||
|
||||
if (process.platform === "win32") {
|
||||
app.setAppUserModelId(app.name);
|
||||
}
|
||||
@@ -91,7 +93,7 @@ async function createWindow() {
|
||||
mainWindow.setOpacity(1);
|
||||
|
||||
if (config.privacyMode) {
|
||||
await api.integration.setPrivacyMode(config.privacyMode);
|
||||
await api.integration.setPrivacyMode({ enabled: config.privacyMode });
|
||||
}
|
||||
|
||||
await AssetManager.loadIcons();
|
||||
@@ -137,6 +139,11 @@ app.once("window-all-closed", () => {
|
||||
}
|
||||
});
|
||||
|
||||
app.on("second-instance", () => {
|
||||
if (!globalThis.window) return;
|
||||
bringToFront();
|
||||
});
|
||||
|
||||
app.on("activate", () => {
|
||||
if (globalThis.window === null) {
|
||||
createWindow();
|
||||
|
||||
@@ -38,8 +38,8 @@ export const config = {
|
||||
closeToSystemTray: false
|
||||
},
|
||||
privacyMode: false,
|
||||
isSpellCheckerEnabled: false,
|
||||
zoomFactor: 0,
|
||||
isSpellCheckerEnabled: true,
|
||||
zoomFactor: 1,
|
||||
theme: nativeTheme.themeSource,
|
||||
automaticUpdates: true
|
||||
};
|
||||
|
||||
@@ -17,16 +17,14 @@ 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 { protocol, ProtocolRequest } from "electron";
|
||||
import { protocol, net } from "electron";
|
||||
import { isDevelopment } from "./index";
|
||||
import { createReadStream } from "fs";
|
||||
import { extname, normalize } from "path";
|
||||
import { URL } from "url";
|
||||
import fetch, { Response } from "node-fetch";
|
||||
|
||||
const BASE_PATH = isDevelopment() ? "../public" : "";
|
||||
const HOSTNAME = `app.notesnook.com`;
|
||||
const FILE_NOT_FOUND = -6;
|
||||
const SCHEME = "https";
|
||||
const extensionToMimeType: Record<string, string> = {
|
||||
html: "text/html",
|
||||
@@ -34,101 +32,47 @@ const extensionToMimeType: Record<string, string> = {
|
||||
js: "application/javascript",
|
||||
css: "text/css",
|
||||
svg: "image/svg+xml",
|
||||
png: "image/png"
|
||||
png: "image/png",
|
||||
jpg: "image/jpg",
|
||||
ttf: "font/ttf",
|
||||
woff: "font/woff",
|
||||
woff2: "font/woff2"
|
||||
};
|
||||
|
||||
function registerProtocol() {
|
||||
const protocolInterceptionResult = protocol.interceptStreamProtocol(
|
||||
SCHEME,
|
||||
async (request, callback) => {
|
||||
const url = new URL(request.url);
|
||||
if (shouldInterceptRequest(url)) {
|
||||
console.info("Intercepting request:", request.url);
|
||||
|
||||
const loadIndex = !extname(url.pathname);
|
||||
const filePath = normalize(
|
||||
`${__dirname}${
|
||||
loadIndex
|
||||
? `${BASE_PATH}/index.html`
|
||||
: `${BASE_PATH}/${url.pathname}`
|
||||
}`
|
||||
);
|
||||
if (!filePath) {
|
||||
console.error("Local asset file not found at", filePath);
|
||||
callback({ error: FILE_NOT_FOUND });
|
||||
return;
|
||||
}
|
||||
const fileExtension = extname(filePath).replace(".", "");
|
||||
|
||||
const data = createReadStream(filePath);
|
||||
callback({
|
||||
data,
|
||||
mimeType: extensionToMimeType[fileExtension]
|
||||
});
|
||||
} else {
|
||||
let response: Response;
|
||||
try {
|
||||
const body = await getBody(request);
|
||||
response = await fetch(request.url, {
|
||||
...request,
|
||||
body,
|
||||
headers: {
|
||||
...request.headers
|
||||
// origin: `${PROTOCOL}://${HOSTNAME}/`
|
||||
},
|
||||
referrer: request.referrer,
|
||||
redirect: "manual"
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
console.error(`Error sending request to `, request.url, "Error: ", e);
|
||||
callback({ statusCode: 400 });
|
||||
return;
|
||||
}
|
||||
callback({
|
||||
statusCode: response.status,
|
||||
data: response.body || undefined,
|
||||
headers: Object.fromEntries(response.headers.entries()),
|
||||
mimeType: response.headers.get("Content-Type") || undefined
|
||||
protocol.handle(SCHEME, async (request) => {
|
||||
const url = new URL(request.url);
|
||||
if (shouldInterceptRequest(url)) {
|
||||
console.info("Intercepting request:", request.url);
|
||||
const loadIndex = !extname(url.pathname);
|
||||
const filePath = normalize(
|
||||
`${__dirname}${
|
||||
loadIndex ? `${BASE_PATH}/index.html` : `${BASE_PATH}/${url.pathname}`
|
||||
}`
|
||||
);
|
||||
if (!filePath) {
|
||||
console.error("Local asset file not found at", filePath);
|
||||
return new Response(undefined, {
|
||||
status: 404,
|
||||
statusText: "FILE_NOT_FOUND"
|
||||
});
|
||||
}
|
||||
const fileExtension = extname(filePath).replace(".", "");
|
||||
return new Response(createReadStream(filePath), {
|
||||
headers: { "Content-Type": extensionToMimeType[fileExtension] }
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
return net.fetch(request, {
|
||||
bypassCustomProtocolHandlers: true
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
return Response.error();
|
||||
}
|
||||
);
|
||||
|
||||
console.info(
|
||||
`${SCHEME} protocol inteception ${
|
||||
protocolInterceptionResult ? "successful" : "failed"
|
||||
}.`
|
||||
);
|
||||
|
||||
// protocol.handle(SCHEME, (request) => {
|
||||
// const url = new URL(request.url);
|
||||
// if (shouldInterceptRequest(url)) {
|
||||
// console.info("Intercepting request:", request.url);
|
||||
// const loadIndex = !extname(url.pathname);
|
||||
// const absoluteFilePath = normalize(
|
||||
// `${__dirname}${
|
||||
// loadIndex ? `${BASE_PATH}/index.html` : `${BASE_PATH}/${url.pathname}`
|
||||
// }`
|
||||
// );
|
||||
// const filePath = getPath(absoluteFilePath);
|
||||
// if (!filePath) {
|
||||
// console.error("Local asset file not found at", filePath);
|
||||
// return new Response(undefined, {
|
||||
// status: 404,
|
||||
// statusText: "FILE_NOT_FOUND"
|
||||
// });
|
||||
// }
|
||||
// const fileExtension = extname(filePath).replace(".", "");
|
||||
// const data = createReadStream(filePath);
|
||||
// return new Response(data, {
|
||||
// headers: { "Content-Type": extensionToMimeType[fileExtension] }
|
||||
// });
|
||||
// } else {
|
||||
// return net.fetch(request);
|
||||
// }
|
||||
// });
|
||||
// console.info(`${SCHEME} protocol inteception "successful"`);
|
||||
});
|
||||
console.info(`${SCHEME} protocol inteception "successful"`);
|
||||
}
|
||||
|
||||
const bypassedRoutes: string[] = [];
|
||||
@@ -139,22 +83,3 @@ function shouldInterceptRequest(url: URL) {
|
||||
|
||||
const PROTOCOL_URL = `${SCHEME}://${HOSTNAME}/`;
|
||||
export { registerProtocol, PROTOCOL_URL };
|
||||
|
||||
async function getBody(request: ProtocolRequest) {
|
||||
const session = globalThis?.window?.webContents?.session;
|
||||
|
||||
const blobParts = [];
|
||||
if (!request.uploadData || !request.uploadData.length) return null;
|
||||
for (const data of request.uploadData) {
|
||||
if (data.bytes) {
|
||||
blobParts.push(new Uint8Array(data.bytes));
|
||||
} else if (session && data.blobUUID) {
|
||||
const buffer = await session.getBlobData(data.blobUUID);
|
||||
if (!buffer) continue;
|
||||
blobParts.push(new Uint8Array(buffer));
|
||||
}
|
||||
}
|
||||
const blob = new Blob(blobParts);
|
||||
return blob;
|
||||
}
|
||||
|
||||
|
||||
1
apps/mobile/.gitignore
vendored
1
apps/mobile/.gitignore
vendored
@@ -5,7 +5,6 @@ artifacts/
|
||||
.DS_Store
|
||||
|
||||
native/android/app/src/main/assets/
|
||||
|
||||
*Issues.md
|
||||
build_cache/
|
||||
#
|
||||
|
||||
@@ -16,6 +16,8 @@ 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 "@azure/core-asynciterator-polyfill";
|
||||
import SettingsService from "./services/settings";
|
||||
import {
|
||||
THEME_COMPATIBILITY_VERSION,
|
||||
useThemeEngineStore
|
||||
@@ -31,13 +33,11 @@ import GlobalSafeAreaProvider from "./components/globalsafearea";
|
||||
import { useAppEvents } from "./hooks/use-app-events";
|
||||
import { ApplicationHolder } from "./navigation";
|
||||
import Notifications from "./services/notifications";
|
||||
import SettingsService from "./services/settings";
|
||||
import { TipManager } from "./services/tip-manager";
|
||||
import { useThemeStore } from "./stores/use-theme-store";
|
||||
import { useUserStore } from "./stores/use-user-store";
|
||||
import { themeTrpcClient } from "./screens/settings/theme-selector";
|
||||
|
||||
SettingsService.init();
|
||||
SettingsService.checkOrientation();
|
||||
const App = () => {
|
||||
const init = useAppEvents();
|
||||
@@ -46,6 +46,7 @@ const App = () => {
|
||||
if (appLockMode && appLockMode !== "none") {
|
||||
useUserStore.getState().lockApp(true);
|
||||
}
|
||||
globalThis["IS_MAIN_APP_RUNNING"] = true;
|
||||
init();
|
||||
setTimeout(async () => {
|
||||
SettingsService.onFirstLaunch();
|
||||
@@ -93,6 +94,12 @@ const App = () => {
|
||||
);
|
||||
};
|
||||
|
||||
let currTheme =
|
||||
useThemeStore.getState().colorScheme === "dark"
|
||||
? SettingsService.getProperty("darkTheme")
|
||||
: SettingsService.getProperty("lightTheme");
|
||||
useThemeEngineStore.getState().setTheme(currTheme);
|
||||
|
||||
export const withTheme = (Element) => {
|
||||
return function AppWithThemeProvider() {
|
||||
const [colorScheme, darkTheme, lightTheme] = useThemeStore((state) => [
|
||||
@@ -102,31 +109,35 @@ export const withTheme = (Element) => {
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const currentTheme = colorScheme === "dark" ? darkTheme : lightTheme;
|
||||
if (!currentTheme) return;
|
||||
themeTrpcClient.updateTheme
|
||||
.query({
|
||||
version: currentTheme.version,
|
||||
compatibilityVersion: THEME_COMPATIBILITY_VERSION,
|
||||
id: currentTheme.id
|
||||
})
|
||||
.then((theme) => {
|
||||
if (theme) {
|
||||
console.log(theme.version, "theme updated");
|
||||
theme.colorScheme === "dark"
|
||||
? useThemeStore.getState().setDarkTheme(theme)
|
||||
: useThemeStore.getState().setLightTheme(theme);
|
||||
}
|
||||
})
|
||||
.catch(console.log);
|
||||
setTimeout(() => {
|
||||
const currentTheme = colorScheme === "dark" ? darkTheme : lightTheme;
|
||||
if (!currentTheme) return;
|
||||
themeTrpcClient.updateTheme
|
||||
.query({
|
||||
version: currentTheme.version,
|
||||
compatibilityVersion: THEME_COMPATIBILITY_VERSION,
|
||||
id: currentTheme.id
|
||||
})
|
||||
.then((theme) => {
|
||||
if (theme) {
|
||||
console.log(theme.version, "theme updated");
|
||||
theme.colorScheme === "dark"
|
||||
? useThemeStore.getState().setDarkTheme(theme)
|
||||
: useThemeStore.getState().setLightTheme(theme);
|
||||
}
|
||||
})
|
||||
.catch(console.log);
|
||||
}, 1000);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const nextTheme = colorScheme === "dark" ? darkTheme : lightTheme;
|
||||
if (JSON.stringify(nextTheme) !== JSON.stringify(currTheme)) {
|
||||
useThemeEngineStore
|
||||
.getState()
|
||||
.setTheme(colorScheme === "dark" ? darkTheme : lightTheme);
|
||||
}, [colorScheme, darkTheme, lightTheme]);
|
||||
currTheme = nextTheme;
|
||||
}
|
||||
|
||||
return <Element />;
|
||||
};
|
||||
|
||||
@@ -126,6 +126,18 @@ export async function decrypt(password, data) {
|
||||
return await Sodium.decrypt(password, _data);
|
||||
}
|
||||
|
||||
export async function decryptMulti(password, data) {
|
||||
if (!password.password && !password.key) return undefined;
|
||||
if (password.password && password.password === "" && !password.key)
|
||||
return undefined;
|
||||
|
||||
data = data.map((d) => {
|
||||
d.output = "plain";
|
||||
return d;
|
||||
});
|
||||
return await Sodium.decryptMulti(password, data);
|
||||
}
|
||||
|
||||
export function parseAlgorithm(alg) {
|
||||
if (!alg) return {};
|
||||
const [enc, kdf, compressed, compressionAlg, base64variant] = alg.split("-");
|
||||
@@ -154,3 +166,24 @@ export async function encrypt(password, data) {
|
||||
alg: getAlgorithm(7)
|
||||
};
|
||||
}
|
||||
|
||||
export async function encryptMulti(password, data) {
|
||||
if (!password.password && !password.key) return undefined;
|
||||
if (password.password && password.password === "" && !password.key)
|
||||
return undefined;
|
||||
|
||||
let results = await Sodium.encryptMulti(
|
||||
password,
|
||||
data.map((item) => ({
|
||||
type: "plain",
|
||||
data: item
|
||||
}))
|
||||
);
|
||||
|
||||
return !results
|
||||
? []
|
||||
: results.map((result) => ({
|
||||
...result,
|
||||
alg: getAlgorithm(7)
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -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 { database } from "@notesnook/common";
|
||||
import { initalize, logger as dbLogger } from "@notesnook/core/logger";
|
||||
import { initalize, logger as dbLogger } from "@notesnook/core/dist/logger";
|
||||
import { Platform } from "react-native";
|
||||
import { MMKVLoader } from "react-native-mmkv-storage";
|
||||
import filesystem from "../filesystem";
|
||||
@@ -30,43 +30,40 @@ const LoggerStorage = new MMKVLoader()
|
||||
.withInstanceID("notesnook_logs")
|
||||
.initialize();
|
||||
|
||||
|
||||
database.host(
|
||||
__DEV__
|
||||
? {
|
||||
API_HOST: "https://api.notesnook.com",
|
||||
AUTH_HOST: "https://auth.streetwriters.co",
|
||||
SSE_HOST: "https://events.streetwriters.co",
|
||||
SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
|
||||
ISSUES_HOST: "https://issues.streetwriters.co"
|
||||
// API_HOST: "http://192.168.8.101:5264",
|
||||
// AUTH_HOST: "http://192.168.8.101:8264",
|
||||
// SSE_HOST: "http://192.168.8.101:7264",
|
||||
// SUBSCRIPTIONS_HOST: "http://192.168.8.101:9264",
|
||||
// ISSUES_HOST: "http://192.168.8.101:2624"
|
||||
}
|
||||
: {
|
||||
API_HOST: "https://api.notesnook.com",
|
||||
AUTH_HOST: "https://auth.streetwriters.co",
|
||||
SSE_HOST: "https://events.streetwriters.co",
|
||||
SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
|
||||
ISSUES_HOST: "https://issues.streetwriters.co"
|
||||
}
|
||||
);
|
||||
database.host(
|
||||
__DEV__
|
||||
? {
|
||||
API_HOST: "https://api.notesnook.com",
|
||||
AUTH_HOST: "https://auth.streetwriters.co",
|
||||
SSE_HOST: "https://events.streetwriters.co",
|
||||
SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
|
||||
ISSUES_HOST: "https://issues.streetwriters.co"
|
||||
// API_HOST: "http://192.168.43.108:5264",
|
||||
// AUTH_HOST: "http://192.168.43.108:8264",
|
||||
// SSE_HOST: "http://192.168.43.108:7264",
|
||||
// SUBSCRIPTIONS_HOST: "http://192.168.43.108:9264",
|
||||
// ISSUES_HOST: "http://192.168.43.108:2624"
|
||||
}
|
||||
: {
|
||||
API_HOST: "https://api.notesnook.com",
|
||||
AUTH_HOST: "https://auth.streetwriters.co",
|
||||
SSE_HOST: "https://events.streetwriters.co",
|
||||
SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
|
||||
ISSUES_HOST: "https://issues.streetwriters.co"
|
||||
}
|
||||
);
|
||||
|
||||
database.setup(
|
||||
Storage,
|
||||
Platform.OS === "ios" ? EventSource : AndroidEventSource,
|
||||
filesystem,
|
||||
{
|
||||
compress: Gzip.deflate,
|
||||
decompress: Gzip.inflate
|
||||
}
|
||||
);
|
||||
database.setup(
|
||||
Storage,
|
||||
Platform.OS === "ios" ? EventSource : AndroidEventSource,
|
||||
filesystem,
|
||||
{
|
||||
compress: Gzip.deflate,
|
||||
decompress: Gzip.inflate
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
initalize(new KV(LoggerStorage), true);
|
||||
initalize(new KV(LoggerStorage));
|
||||
|
||||
export const db = database;
|
||||
export const DatabaseLogger = dbLogger;
|
||||
|
||||
@@ -18,9 +18,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Platform } from "react-native";
|
||||
import MMKVStorage, { ProcessingModes } from "react-native-mmkv-storage";
|
||||
import { ProcessingModes, MMKVLoader } from "react-native-mmkv-storage";
|
||||
|
||||
export const MMKV = new MMKVStorage.Loader()
|
||||
export const MMKV = new MMKVLoader()
|
||||
.setProcessingMode(
|
||||
Platform.OS === "ios"
|
||||
? ProcessingModes.MULTI_PROCESS
|
||||
|
||||
@@ -27,11 +27,16 @@ import {
|
||||
getCryptoKey,
|
||||
getRandomBytes,
|
||||
hash,
|
||||
removeCryptoKey
|
||||
removeCryptoKey,
|
||||
decryptMulti,
|
||||
encryptMulti
|
||||
} from "./encryption";
|
||||
import { MMKV } from "./mmkv";
|
||||
|
||||
export class KV {
|
||||
/**
|
||||
* @type {typeof MMKV}
|
||||
*/
|
||||
storage = null;
|
||||
constructor(storage) {
|
||||
this.storage = storage;
|
||||
@@ -53,6 +58,7 @@ export class KV {
|
||||
key,
|
||||
typeof data === "string" ? data : JSON.stringify(data)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -60,27 +66,32 @@ export class KV {
|
||||
if (keys.length <= 0) {
|
||||
return [];
|
||||
} else {
|
||||
let data = await this.storage.getMultipleItemsAsync(keys.slice());
|
||||
|
||||
return data.map(([key, value]) => {
|
||||
let obj;
|
||||
try {
|
||||
obj = JSON.parse(value);
|
||||
} catch (e) {
|
||||
obj = value;
|
||||
}
|
||||
|
||||
return [key, obj];
|
||||
});
|
||||
try {
|
||||
let data = await this.storage.getMultipleItemsAsync(
|
||||
keys.slice(),
|
||||
"string"
|
||||
);
|
||||
return data.map(([key, value]) => {
|
||||
let obj;
|
||||
try {
|
||||
obj = JSON.parse(value);
|
||||
} catch (e) {
|
||||
obj = value;
|
||||
}
|
||||
return [key, obj];
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async remove(key) {
|
||||
return await this.storage.removeItem(key);
|
||||
return this.storage.removeItem(key);
|
||||
}
|
||||
|
||||
async clear() {
|
||||
return await this.storage.clearStore();
|
||||
return this.storage.clearStore();
|
||||
}
|
||||
|
||||
async getAllKeys() {
|
||||
@@ -96,6 +107,10 @@ export class KV {
|
||||
);
|
||||
return keys;
|
||||
}
|
||||
|
||||
async writeMulti(items) {
|
||||
return this.storage.setMultipleItemsAsync(items, "object");
|
||||
}
|
||||
}
|
||||
|
||||
const DefaultStorage = new KV(MMKV);
|
||||
@@ -129,8 +144,10 @@ export default {
|
||||
remove: (key) => DefaultStorage.remove(key),
|
||||
clear: () => DefaultStorage.clear(),
|
||||
getAllKeys: () => DefaultStorage.getAllKeys(),
|
||||
writeMulti: (items) => DefaultStorage.writeMulti(items),
|
||||
encrypt,
|
||||
decrypt,
|
||||
decryptMulti,
|
||||
getRandomBytes,
|
||||
checkAndCreateDir,
|
||||
requestPermission,
|
||||
@@ -138,5 +155,6 @@ export default {
|
||||
getCryptoKey,
|
||||
removeCryptoKey,
|
||||
hash,
|
||||
generateCryptoKey
|
||||
generateCryptoKey,
|
||||
encryptMulti
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
getFileNameWithExtension,
|
||||
isImage,
|
||||
isDocument
|
||||
} from "@notesnook/core/utils/filename";
|
||||
} from "@notesnook/core/dist/utils/filename";
|
||||
import React from "react";
|
||||
import { Platform } from "react-native";
|
||||
import * as ScopedStorage from "react-native-scoped-storage";
|
||||
|
||||
@@ -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 hosts from "@notesnook/core/utils/constants";
|
||||
import hosts from "@notesnook/core/dist/utils/constants";
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
import RNFetchBlob from "react-native-blob-util";
|
||||
import { ToastEvent } from "../../services/event-manager";
|
||||
|
||||
@@ -21,7 +21,7 @@ import RNFetchBlob from "react-native-blob-util";
|
||||
import { useAttachmentStore } from "../../stores/use-attachment-store";
|
||||
import { DatabaseLogger, db } from "../database";
|
||||
import { cacheDir } from "./utils";
|
||||
import { isImage, isDocument } from "@notesnook/core/utils/filename";
|
||||
import { isImage, isDocument } from "@notesnook/core/dist/utils/filename";
|
||||
import { Platform } from "react-native";
|
||||
import { IOS_APPGROUPID } from "../../utils/constants";
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ const AppLockedOverlay = () => {
|
||||
appLocked && (
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: colors.bg,
|
||||
backgroundColor: colors.primary.background,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
position: "absolute",
|
||||
@@ -160,7 +160,7 @@ const AppLockedOverlay = () => {
|
||||
style={{
|
||||
width: "100%",
|
||||
padding: 12,
|
||||
backgroundColor: colors.bg
|
||||
backgroundColor: colors.primary.background
|
||||
}}
|
||||
>
|
||||
{user ? (
|
||||
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
isDocument,
|
||||
isImage,
|
||||
isVideo
|
||||
} from "@notesnook/core/utils/filename";
|
||||
} from "@notesnook/core/dist/utils/filename";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
|
||||
|
||||
@@ -241,7 +241,7 @@ export const AttachmentDialog = ({ note }) => {
|
||||
width: "100%",
|
||||
height: 50,
|
||||
flexDirection: "row",
|
||||
backgroundColor: colors.bg
|
||||
backgroundColor: colors.primary.background
|
||||
}}
|
||||
contentContainerStyle={{
|
||||
minWidth: "100%",
|
||||
|
||||
@@ -17,18 +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 { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Modal, View } from "react-native";
|
||||
import { db } from "../../common/database";
|
||||
import { MMKV } from "../../common/database/mmkv";
|
||||
import BiometricService from "../../services/biometrics";
|
||||
import {
|
||||
ToastEvent,
|
||||
eSendEvent,
|
||||
eSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import { setLoginMessage } from "../../services/message";
|
||||
import SettingsService from "../../services/settings";
|
||||
import Sync from "../../services/sync";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { clearAllStores } from "../../stores";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { eLoginSessionExpired, eUserLoggedIn } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { sleep } from "../../utils/time";
|
||||
@@ -71,9 +75,12 @@ export const SessionExpired = () => {
|
||||
try {
|
||||
await db.user.logout();
|
||||
await BiometricService.resetCredentials();
|
||||
SettingsService.set({
|
||||
introCompleted: true
|
||||
});
|
||||
setLoginMessage();
|
||||
SettingsService.resetSettings();
|
||||
useUserStore.getState().setUser(null);
|
||||
useUserStore.getState().setSyncing(false);
|
||||
MMKV.clearStore();
|
||||
clearAllStores();
|
||||
setVisible(false);
|
||||
} catch (e) {
|
||||
ToastEvent.show({
|
||||
|
||||
@@ -67,6 +67,7 @@ export const Signup = ({ changeMode, trial }) => {
|
||||
|
||||
const signup = async () => {
|
||||
if (!validateInfo() || error) return;
|
||||
if (loading) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
await db.user.signup(email.current.toLowerCase(), password.current);
|
||||
|
||||
@@ -64,6 +64,7 @@ export const useLogin = (onFinishLogin) => {
|
||||
const login = async () => {
|
||||
if (!validateInfo() || error) return;
|
||||
try {
|
||||
if (loading) return;
|
||||
setLoading(true);
|
||||
switch (step) {
|
||||
case LoginSteps.emailAuth: {
|
||||
|
||||
@@ -17,12 +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 React from "react";
|
||||
import React, { PropsWithChildren } from "react";
|
||||
import { View } from "react-native";
|
||||
import { useSelectionStore } from "../../stores/use-selection-store";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
|
||||
export const ContainerHeader = ({ children }) => {
|
||||
export const ContainerHeader = (props: PropsWithChildren) => {
|
||||
const { colors } = useThemeColors();
|
||||
const selectionMode = useSelectionStore((state) => state.selectionMode);
|
||||
|
||||
@@ -34,7 +34,7 @@ export const ContainerHeader = ({ children }) => {
|
||||
overflow: "hidden"
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
{props.children}
|
||||
</View>
|
||||
) : null;
|
||||
};
|
||||
@@ -17,8 +17,9 @@ 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, { useCallback, useEffect } from "react";
|
||||
import { Keyboard, Platform, View } from "react-native";
|
||||
import { Keyboard, View } from "react-native";
|
||||
import Animated, {
|
||||
Easing,
|
||||
useAnimatedStyle,
|
||||
@@ -34,14 +35,20 @@ import { getElevationStyle } from "../../utils/elevation";
|
||||
import { SIZE, normalize } from "../../utils/size";
|
||||
import NativeTooltip from "../../utils/tooltip";
|
||||
import { PressableButton } from "../ui/pressable";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
|
||||
export const FloatingButton = ({
|
||||
interface FloatingButton {
|
||||
title?: string;
|
||||
onPress: () => void;
|
||||
color?: string;
|
||||
alwaysVisible?: boolean;
|
||||
}
|
||||
|
||||
const FloatingButton = ({
|
||||
title,
|
||||
onPress,
|
||||
color,
|
||||
shouldShow = false
|
||||
}) => {
|
||||
alwaysVisible = false
|
||||
}: FloatingButton) => {
|
||||
const { colors } = useThemeColors();
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const selectionMode = useSelectionStore((state) => state.selectionMode);
|
||||
@@ -60,12 +67,8 @@ export const FloatingButton = ({
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
animate(selectionMode ? 150 : 0);
|
||||
}, [animate, selectionMode]);
|
||||
|
||||
const animate = useCallback(
|
||||
(toValue) => {
|
||||
(toValue: number) => {
|
||||
translate.value = withTiming(toValue, {
|
||||
duration: 250,
|
||||
easing: Easing.elastic(1)
|
||||
@@ -74,39 +77,39 @@ export const FloatingButton = ({
|
||||
[translate]
|
||||
);
|
||||
|
||||
const onKeyboardHide = useCallback(async () => {
|
||||
editorState().keyboardState = false;
|
||||
if (deviceMode !== "mobile") return;
|
||||
animate(0);
|
||||
}, [animate, deviceMode]);
|
||||
|
||||
const onKeyboardShow = useCallback(async () => {
|
||||
editorState().keyboardState = true;
|
||||
if (deviceMode !== "mobile") return;
|
||||
animate(150);
|
||||
}, [animate, deviceMode]);
|
||||
useEffect(() => {
|
||||
animate(selectionMode ? 150 : 0);
|
||||
}, [animate, selectionMode]);
|
||||
|
||||
useEffect(() => {
|
||||
let sub1 = Keyboard.addListener("keyboardDidShow", onKeyboardShow);
|
||||
let sub2 = Keyboard.addListener("keyboardDidHide", onKeyboardHide);
|
||||
return () => {
|
||||
sub1?.remove();
|
||||
sub2?.remove();
|
||||
const onKeyboardHide = () => {
|
||||
editorState().keyboardState = false;
|
||||
if (deviceMode !== "mobile") return;
|
||||
animate(0);
|
||||
};
|
||||
}, [deviceMode, onKeyboardHide, onKeyboardShow]);
|
||||
const paddings = {
|
||||
ios: 20,
|
||||
android: 20,
|
||||
iPad: 20
|
||||
};
|
||||
|
||||
return deviceMode !== "mobile" && !shouldShow ? null : (
|
||||
const onKeyboardShow = () => {
|
||||
editorState().keyboardState = true;
|
||||
if (deviceMode !== "mobile") return;
|
||||
animate(150);
|
||||
};
|
||||
|
||||
const sub = [
|
||||
Keyboard.addListener("keyboardDidShow", onKeyboardShow),
|
||||
Keyboard.addListener("keyboardDidHide", onKeyboardHide)
|
||||
];
|
||||
return () => {
|
||||
sub.forEach((sub) => sub?.remove?.());
|
||||
};
|
||||
}, [deviceMode, animate]);
|
||||
|
||||
return deviceMode !== "mobile" && !alwaysVisible ? null : (
|
||||
<Animated.View
|
||||
style={[
|
||||
{
|
||||
position: "absolute",
|
||||
right: 12,
|
||||
bottom: paddings[Platform.isPad ? "iPad" : Platform.OS],
|
||||
bottom: 20,
|
||||
zIndex: 10
|
||||
},
|
||||
animatedStyle
|
||||
@@ -115,16 +118,15 @@ export const FloatingButton = ({
|
||||
<PressableButton
|
||||
testID={notesnook.buttons.add}
|
||||
type="accent"
|
||||
buttonType={{
|
||||
color: colors.static[color],
|
||||
text: colors.static.white
|
||||
}}
|
||||
accentColor={colors.static[color as keyof typeof colors.static]}
|
||||
customStyle={{
|
||||
...getElevationStyle(5),
|
||||
borderRadius: 100
|
||||
}}
|
||||
onLongPress={(event) => {
|
||||
NativeTooltip.show(event, title, NativeTooltip.POSITIONS.LEFT);
|
||||
if (title) {
|
||||
NativeTooltip.show(event, title, NativeTooltip.POSITIONS.LEFT);
|
||||
}
|
||||
}}
|
||||
onPress={onPress}
|
||||
>
|
||||
@@ -146,3 +148,5 @@ export const FloatingButton = ({
|
||||
</Animated.View>
|
||||
);
|
||||
};
|
||||
|
||||
export { FloatingButton };
|
||||
@@ -17,44 +17,40 @@ 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 React from "react";
|
||||
import { KeyboardAvoidingView, Platform, View } from "react-native";
|
||||
import React, { PropsWithChildren } from "react";
|
||||
import { KeyboardAvoidingView, Platform } from "react-native";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import useIsFloatingKeyboard from "../../hooks/use-is-floating-keyboard";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { Header } from "../header";
|
||||
import SelectionHeader from "../selection-header";
|
||||
export const Container = ({ children }) => {
|
||||
|
||||
export const Container = ({ children }: PropsWithChildren) => {
|
||||
const floating = useIsFloatingKeyboard();
|
||||
const introCompleted = useSettingStore(
|
||||
(state) => state.settings.introCompleted
|
||||
);
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
behavior="padding"
|
||||
enabled={Platform.OS === "ios" && !floating}
|
||||
style={{
|
||||
flex: 1
|
||||
flex: 1,
|
||||
overflow: "hidden",
|
||||
paddingTop: Platform.OS === "android" ? 0 : insets.top,
|
||||
paddingBottom: Platform.OS === "android" ? 0 : insets.bottom
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
overflow: "hidden",
|
||||
paddingTop: Platform.OS === "android" ? 0 : insets.top,
|
||||
paddingBottom: Platform.OS === "android" ? 0 : insets.bottom
|
||||
}}
|
||||
>
|
||||
{!introCompleted ? null : (
|
||||
<>
|
||||
<SelectionHeader />
|
||||
<Header title="Header" screen="Header" />
|
||||
</>
|
||||
)}
|
||||
{!introCompleted ? null : (
|
||||
<>
|
||||
<SelectionHeader />
|
||||
<Header />
|
||||
</>
|
||||
)}
|
||||
|
||||
{children}
|
||||
</View>
|
||||
{children}
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
};
|
||||
@@ -43,7 +43,7 @@ const _Header = () => {
|
||||
);
|
||||
|
||||
const onScroll = useCallback(
|
||||
(data) => {
|
||||
(data: { x: number; y: number }) => {
|
||||
if (data.y > 150) {
|
||||
if (!hide) return;
|
||||
setHide(false);
|
||||
@@ -43,12 +43,6 @@ export const LeftMenus = () => {
|
||||
return;
|
||||
}
|
||||
Navigation.goBack();
|
||||
if (
|
||||
useNavigationStore.getState().currentScreen.name === "Signup" ||
|
||||
useNavigationStore.getState().currentScreen.name === "Login"
|
||||
) {
|
||||
tabBarRef.current.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
return isTablet && !canGoBack ? null : (
|
||||
@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useRef } from "react";
|
||||
import { Platform, StyleSheet, View } from "react-native";
|
||||
//@ts-ignore
|
||||
import Menu from "react-native-reanimated-material-menu";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import Navigation from "../../services/navigation";
|
||||
@@ -33,12 +34,15 @@ import { IconButton } from "../ui/icon-button";
|
||||
|
||||
export const RightMenus = () => {
|
||||
const { colors } = useThemeColors();
|
||||
const { contextMenuColors } = useThemeColors("contextMenu");
|
||||
const { colors: contextMenuColors } = useThemeColors("contextMenu");
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const rightButtons = useNavigationStore((state) => state.headerRightButtons);
|
||||
const buttons = useNavigationStore((state) => state.headerRightButtons);
|
||||
const currentScreen = useNavigationStore((state) => state.currentScreen.name);
|
||||
const buttonAction = useNavigationStore((state) => state.buttonAction);
|
||||
const menuRef = useRef();
|
||||
const menuRef = useRef<{
|
||||
show: () => void;
|
||||
hide: () => void;
|
||||
}>();
|
||||
|
||||
return (
|
||||
<View style={styles.rightBtnContainer}>
|
||||
@@ -46,9 +50,12 @@ export const RightMenus = () => {
|
||||
<IconButton
|
||||
onPress={async () => {
|
||||
SearchService.prepareSearch();
|
||||
Navigation.navigate({
|
||||
name: "Search"
|
||||
});
|
||||
Navigation.navigate(
|
||||
{
|
||||
name: "Search"
|
||||
},
|
||||
{}
|
||||
);
|
||||
}}
|
||||
testID="icon-search"
|
||||
name="magnify"
|
||||
@@ -82,7 +89,7 @@ export const RightMenus = () => {
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{rightButtons && rightButtons.length > 0 ? (
|
||||
{buttons && buttons.length > 0 ? (
|
||||
<Menu
|
||||
ref={menuRef}
|
||||
animationDuration={200}
|
||||
@@ -93,7 +100,7 @@ export const RightMenus = () => {
|
||||
onRequestClose={() => {
|
||||
menuRef.current?.hide();
|
||||
}}
|
||||
button={
|
||||
anchor={
|
||||
<IconButton
|
||||
onPress={() => {
|
||||
menuRef.current?.show();
|
||||
@@ -104,7 +111,7 @@ export const RightMenus = () => {
|
||||
/>
|
||||
}
|
||||
>
|
||||
{rightButtons.map((item) => (
|
||||
{buttons.map((item) => (
|
||||
<Button
|
||||
style={{
|
||||
width: 150,
|
||||
@@ -113,7 +120,7 @@ export const RightMenus = () => {
|
||||
}}
|
||||
type="gray"
|
||||
buttonType={{
|
||||
text: colors.primary.paragraph
|
||||
text: contextMenuColors.primary.paragraph
|
||||
}}
|
||||
key={item.title}
|
||||
title={item.title}
|
||||
@@ -17,8 +17,9 @@ 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 React, { useEffect, useState } from "react";
|
||||
import { Platform, View } from "react-native";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { Platform } from "react-native";
|
||||
import { db } from "../../common/database";
|
||||
import Notebook from "../../screens/notebook";
|
||||
import {
|
||||
@@ -26,14 +27,12 @@ import {
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { eScrollEvent } from "../../utils/events";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import { useCallback } from "react";
|
||||
import Tag from "../ui/tag";
|
||||
import Heading from "../ui/typography/heading";
|
||||
|
||||
const titleState = {};
|
||||
const titleState: { [name: string]: boolean } = {};
|
||||
|
||||
export const Title = () => {
|
||||
const { colors } = useThemeColors();
|
||||
@@ -42,12 +41,12 @@ export const Title = () => {
|
||||
const isTopic = currentScreen?.name === "TopicNotes";
|
||||
const [hide, setHide] = useState(
|
||||
isNotebook
|
||||
? typeof titleState[currentScreen.id] === "boolean"
|
||||
? titleState[currentScreen.id]
|
||||
? typeof titleState[currentScreen.id as string] === "boolean"
|
||||
? titleState[currentScreen.id as string]
|
||||
: true
|
||||
: false
|
||||
);
|
||||
const isHidden = titleState[currentScreen.id];
|
||||
const isHidden = titleState[currentScreen.id as string];
|
||||
const notebook =
|
||||
isTopic && currentScreen.notebookId
|
||||
? db.notebooks?.notebook(currentScreen.notebookId)?.data
|
||||
@@ -56,18 +55,18 @@ export const Title = () => {
|
||||
const isTag = currentScreen?.name === "TaggedNotes";
|
||||
|
||||
const onScroll = useCallback(
|
||||
(data) => {
|
||||
(data: { x: number; y: number }) => {
|
||||
if (currentScreen.name !== "Notebook") {
|
||||
setHide(false);
|
||||
return;
|
||||
}
|
||||
if (data.y > 150) {
|
||||
if (!hide) return;
|
||||
titleState[currentScreen.id] = false;
|
||||
titleState[currentScreen.id as string] = false;
|
||||
setHide(false);
|
||||
} else {
|
||||
if (hide) return;
|
||||
titleState[currentScreen.id] = true;
|
||||
titleState[currentScreen.id as string] = true;
|
||||
setHide(true);
|
||||
}
|
||||
},
|
||||
@@ -76,13 +75,13 @@ export const Title = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (currentScreen.name === "Notebook") {
|
||||
let value =
|
||||
typeof titleState[currentScreen.id] === "boolean"
|
||||
? titleState[currentScreen.id]
|
||||
const value =
|
||||
typeof titleState[currentScreen.id as string] === "boolean"
|
||||
? titleState[currentScreen.id as string]
|
||||
: true;
|
||||
setHide(value);
|
||||
} else {
|
||||
setHide(titleState[currentScreen.id]);
|
||||
setHide(titleState[currentScreen.id as string]);
|
||||
}
|
||||
}, [currentScreen.id, currentScreen.name]);
|
||||
|
||||
@@ -98,14 +97,7 @@ export const Title = () => {
|
||||
Notebook.navigate(notebook, true);
|
||||
}
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
opacity: 1,
|
||||
flexShrink: 1,
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<>
|
||||
{!hide && !isHidden ? (
|
||||
<Heading
|
||||
onPress={navigateToNotebook}
|
||||
@@ -122,10 +114,17 @@ export const Title = () => {
|
||||
#
|
||||
</Heading>
|
||||
) : null}
|
||||
{title}
|
||||
{title}{" "}
|
||||
<Tag
|
||||
visible={currentScreen.beta}
|
||||
text="BETA"
|
||||
style={{
|
||||
backgroundColor: "transparent"
|
||||
}}
|
||||
textColor={colors.primary.accent}
|
||||
/>
|
||||
</Heading>
|
||||
) : null}
|
||||
<Tag visible={currentScreen.beta} text="BETA" />
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Sodium from "@ammarahmed/react-native-sodium";
|
||||
import dataurl from "@notesnook/core/utils/dataurl";
|
||||
import dataurl from "@notesnook/core/dist/utils/dataurl";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Platform, View } from "react-native";
|
||||
|
||||
@@ -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 { getUpcomingReminder } from "@notesnook/core/collections/reminders";
|
||||
import { getUpcomingReminder } from "@notesnook/core/dist/collections/reminders";
|
||||
import { decode, EntityLevel } from "entities";
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
@@ -53,15 +53,10 @@ const renderItems = {
|
||||
const RenderItem = ({ item, index, type, ...restArgs }) => {
|
||||
if (!item) return <View />;
|
||||
const Item = renderItems[item.itemType || item.type] || View;
|
||||
const groupOptions = db.settings?.getGroupOptions(type);
|
||||
const dateBy =
|
||||
groupOptions.sortBy !== "title" ? groupOptions.sortBy : "dateEdited";
|
||||
const totalNotes = getTotalNotes(item);
|
||||
|
||||
return (
|
||||
<Item
|
||||
item={item}
|
||||
dateBy={dateBy}
|
||||
index={index}
|
||||
type={type}
|
||||
totalNotes={totalNotes}
|
||||
@@ -103,6 +98,12 @@ const List = ({
|
||||
(type === "notes" && notesListMode === "compact") ||
|
||||
type === "notebooks" ||
|
||||
notebooksListMode === "compact";
|
||||
const groupType =
|
||||
screen === "Notes" ? "home" : screen === "Favorites" ? "favorites" : type;
|
||||
|
||||
const groupOptions = db.settings?.getGroupOptions(groupType);
|
||||
const dateBy =
|
||||
groupOptions.sortBy !== "title" ? groupOptions.sortBy : "dateEdited";
|
||||
|
||||
const renderItem = React.useCallback(
|
||||
({ item, index }) => (
|
||||
@@ -111,6 +112,7 @@ const List = ({
|
||||
index={index}
|
||||
color={headerProps?.color}
|
||||
title={headerProps?.heading}
|
||||
dateBy={dateBy}
|
||||
type={
|
||||
screen === "Notes"
|
||||
? "home"
|
||||
@@ -122,7 +124,7 @@ const List = ({
|
||||
isSheet={isSheet}
|
||||
/>
|
||||
),
|
||||
[headerProps?.color, headerProps?.heading, screen, type, isSheet]
|
||||
[headerProps?.color, headerProps?.heading, screen, type, isSheet, dateBy]
|
||||
);
|
||||
|
||||
const _onRefresh = async () => {
|
||||
|
||||
@@ -109,7 +109,6 @@ export default function NotePreview({ session, content, note }) {
|
||||
flex: 1
|
||||
}}
|
||||
>
|
||||
<EditorOverlay editorId={editorId} />
|
||||
<Editor
|
||||
noHeader
|
||||
noToolbar
|
||||
@@ -117,7 +116,6 @@ export default function NotePreview({ session, content, note }) {
|
||||
editorId={editorId}
|
||||
onLoad={async () => {
|
||||
const _note = note || db.notes.note(session?.noteId)?.data;
|
||||
await sleep(1000);
|
||||
eSendEvent(eOnLoadNote + editorId, {
|
||||
..._note,
|
||||
content: {
|
||||
|
||||
@@ -102,7 +102,11 @@ const SheetProvider = ({ context = "global" }) => {
|
||||
setData(null);
|
||||
}}
|
||||
bottomPadding={!data.noBottomPadding}
|
||||
enableGesturesInScrollView={data.enableGesturesInScrollView}
|
||||
enableGesturesInScrollView={
|
||||
typeof data.enableGesturesInScrollView === "undefined"
|
||||
? true
|
||||
: data.enableGesturesInScrollView
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
@@ -127,7 +131,7 @@ const SheetProvider = ({ context = "global" }) => {
|
||||
|
||||
{data?.icon ? (
|
||||
<Icon
|
||||
color={colors[dialogData.iconColor] || colors.primary.accent}
|
||||
color={colors[data.iconColor] || colors.primary.accent}
|
||||
name={data.icon}
|
||||
size={50}
|
||||
/>
|
||||
|
||||
@@ -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 { EVENTS } from "@notesnook/core/common";
|
||||
import { EVENTS } from "@notesnook/core/dist/common";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { Platform, View } from "react-native";
|
||||
import { db } from "../../../common/database";
|
||||
|
||||
@@ -16,6 +16,7 @@ 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 } from "react-native";
|
||||
import { ActionSheetRef, ScrollView } from "react-native-actions-sheet";
|
||||
@@ -25,7 +26,6 @@ import {
|
||||
ToastEvent,
|
||||
presentSheet
|
||||
} from "../../../services/event-manager";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { Button } from "../../ui/button";
|
||||
import Input from "../../ui/input";
|
||||
@@ -223,7 +223,7 @@ export default function ReminderSheet({
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
maxHeight: DDS.isTab ? "90%" : "99.99%"
|
||||
maxHeight: DDS.isTab ? "99.99%" : "99.99%"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
@@ -247,7 +247,12 @@ export default function ReminderSheet({
|
||||
/>
|
||||
</View>
|
||||
<Dialog context="local" />
|
||||
<ScrollView>
|
||||
<ScrollView
|
||||
bounces={false}
|
||||
style={{
|
||||
marginBottom: DDS.isTab ? 25 : undefined
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
fwdRef={titleRef}
|
||||
defaultValue={reminder?.title || referencedItem?.title}
|
||||
@@ -601,7 +606,7 @@ ReminderSheet.present = (
|
||||
) => {
|
||||
presentSheet({
|
||||
context: isSheet ? "local" : undefined,
|
||||
enableGesturesInScrollView: true,
|
||||
enableGesturesInScrollView: false,
|
||||
noBottomPadding: true,
|
||||
component: (ref, close, update) => (
|
||||
<ReminderSheet
|
||||
|
||||
@@ -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 { EVENTS } from "@notesnook/core/common";
|
||||
import { EVENTS } from "@notesnook/core/dist/common";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { ActivityIndicator, Platform, View } from "react-native";
|
||||
import { FlatList } from "react-native-actions-sheet";
|
||||
@@ -377,7 +377,10 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
|
||||
height: 100
|
||||
}}
|
||||
>
|
||||
<ActivityIndicator color={colors.primary.accent} size={SIZE.lg} />
|
||||
<ActivityIndicator
|
||||
color={colors.primary.accent}
|
||||
size={SIZE.lg}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<View
|
||||
@@ -434,7 +437,9 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
|
||||
</Paragraph>
|
||||
</>
|
||||
) : (
|
||||
<Paragraph color={colors.secondary.paragraph}>No backups found</Paragraph>
|
||||
<Paragraph color={colors.secondary.paragraph}>
|
||||
No backups found
|
||||
</Paragraph>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
|
||||
@@ -54,7 +54,7 @@ import {
|
||||
import { normalize, SIZE } from "../../../utils/size";
|
||||
import { GroupHeader, NotebookType, TopicType } from "../../../utils/types";
|
||||
|
||||
import { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import { groupArray } from "@notesnook/core/dist/utils/grouping";
|
||||
import Config from "react-native-config";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { notesnook } from "../../../../e2e/test.ids";
|
||||
|
||||
@@ -28,7 +28,8 @@ import { useNoteStore } from "../../stores/use-notes-store";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { MenuItemsList, SUBSCRIPTION_STATUS } from "../../utils/constants";
|
||||
import { MenuItemsList } from "../../utils/menu-items";
|
||||
import { SUBSCRIPTION_STATUS } from "../../utils/constants";
|
||||
import { eOpenPremiumDialog } from "../../utils/events";
|
||||
import { ColorSection } from "./color-section";
|
||||
import { MenuItem } from "./menu-item";
|
||||
|
||||
@@ -128,7 +128,21 @@ export const MenuItem = React.memo(
|
||||
<Paragraph size={SIZE.md}>{item.name}</Paragraph>
|
||||
)}
|
||||
|
||||
<Tag visible={item.isBeta} text="BETA" />
|
||||
{item.isBeta ? (
|
||||
<View
|
||||
style={{
|
||||
borderRadius: 100,
|
||||
backgroundColor: colors.primary.accent,
|
||||
paddingHorizontal: 4,
|
||||
marginLeft: 5,
|
||||
paddingVertical: 2
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.static.white} size={SIZE.xxs}>
|
||||
BETA
|
||||
</Paragraph>
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
{item.switch ? (
|
||||
|
||||
@@ -214,6 +214,10 @@ export const FluidTabs = forwardRef<TabsRef, TabProps>(function FluidTabs(
|
||||
return;
|
||||
}
|
||||
translateX.value = animated ? withTiming(homePosition) : homePosition;
|
||||
if (!animated) {
|
||||
translateX.value = 299;
|
||||
translateX.value = 300;
|
||||
}
|
||||
onDrawerStateChange(false);
|
||||
isDrawerOpen.value = false;
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@ 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 { isReminderActive } from "@notesnook/core/collections/reminders";
|
||||
import { isReminderActive } from "@notesnook/core/dist/collections/reminders";
|
||||
import React from "react";
|
||||
import { ViewStyle } from "react-native";
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ const SheetWrapper = ({
|
||||
const largeTablet = deviceMode === "tablet";
|
||||
const smallTablet = deviceMode === "smallTablet";
|
||||
const dimensions = useSettingStore((state) => state.dimensions);
|
||||
const pitchBlack = useSettingStore((state) => state.settings.pitchBlack);
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
let width = dimensions.width > 600 ? 600 : 500;
|
||||
|
||||
@@ -121,19 +120,17 @@ const SheetWrapper = ({
|
||||
}
|
||||
onClose={_onClose}
|
||||
>
|
||||
<BouncingView>
|
||||
{children}
|
||||
{bottomPadding ? (
|
||||
<View
|
||||
style={{
|
||||
height:
|
||||
Platform.OS === "ios" && insets.bottom !== 0
|
||||
? insets.bottom + 5
|
||||
: 20
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</BouncingView>
|
||||
{children}
|
||||
{bottomPadding ? (
|
||||
<View
|
||||
style={{
|
||||
height:
|
||||
Platform.OS === "ios" && insets.bottom !== 0
|
||||
? insets.bottom + 5
|
||||
: 20
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</ActionSheet>
|
||||
</ScopedThemeProvider>
|
||||
);
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function Tag({
|
||||
}) {
|
||||
const { colors } = useThemeColors();
|
||||
return !visible ? null : (
|
||||
<View
|
||||
<Paragraph
|
||||
style={{
|
||||
backgroundColor: background || colors.primary.accent,
|
||||
borderRadius: 100,
|
||||
@@ -46,13 +46,14 @@ export default function Tag({
|
||||
marginLeft: 2,
|
||||
marginTop: -10,
|
||||
height: 20,
|
||||
justifyContent: "center",
|
||||
textAlignVertical: "center",
|
||||
textAlign: "center",
|
||||
...style
|
||||
}}
|
||||
color={textColor || colors.static.white}
|
||||
size={SIZE.xxs}
|
||||
>
|
||||
<Paragraph color={textColor || colors.static.white} size={SIZE.xxs}>
|
||||
{text}
|
||||
</Paragraph>
|
||||
</View>
|
||||
{text}
|
||||
</Paragraph>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 { EV, EVENTS, SYNC_CHECK_IDS } from "@notesnook/core/common";
|
||||
import { EV, EVENTS, SYNC_CHECK_IDS } from "@notesnook/core/dist/common";
|
||||
import notifee from "@notifee/react-native";
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
import React, { useCallback, useEffect, useRef } from "react";
|
||||
@@ -631,6 +631,7 @@ const doAppLoadActions = async () => {
|
||||
await useMessageStore.getState().setAnnouncement();
|
||||
if (NewFeature.present()) return;
|
||||
if (await checkAppUpdateAvailable()) return;
|
||||
|
||||
if (!(await db.user.getUser())) {
|
||||
setLoginMessage();
|
||||
return;
|
||||
|
||||
@@ -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 { EVENTS } from "@notesnook/core/common";
|
||||
import { EVENTS } from "@notesnook/core/dist/common";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { db } from "../common/database";
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"html-to-text": "9.0.5",
|
||||
"phone": "^3.1.14",
|
||||
"qclone": "^1.2.0",
|
||||
"react-native-actions-sheet": "0.9.0-alpha.21",
|
||||
"react-native-actions-sheet": "0.9.0-alpha.23",
|
||||
"react-native-check-version": "https://github.com/flexible-agency/react-native-check-version",
|
||||
"react-native-drax": "^0.10.2",
|
||||
"react-native-image-zoom-viewer": "^3.0.1",
|
||||
@@ -30,7 +30,8 @@
|
||||
"toggle-switch-react-native": "3.2.0",
|
||||
"url": "^0.11.0",
|
||||
"validator": "^13.5.2",
|
||||
"zustand": "^3.6.0"
|
||||
"zustand": "^3.6.0",
|
||||
"katex": "0.16.2"
|
||||
},
|
||||
"sideEffects": false
|
||||
}
|
||||
|
||||
@@ -18,35 +18,33 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import { EV, EVENTS } from "@notesnook/core/common";
|
||||
import { EV, EVENTS } from "@notesnook/core/dist/common";
|
||||
import React, {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
useState
|
||||
useLayoutEffect,
|
||||
useRef
|
||||
} from "react";
|
||||
import { Platform, ViewStyle } from "react-native";
|
||||
import WebView from "react-native-webview";
|
||||
import { ShouldStartLoadRequest } from "react-native-webview/lib/WebViewTypes";
|
||||
import { notesnook } from "../../../e2e/test.ids";
|
||||
import { db } from "../../common/database";
|
||||
import {
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent
|
||||
} from "../../services/event-manager";
|
||||
import { IconButton } from "../../components/ui/icon-button";
|
||||
import useKeyboard from "../../hooks/use-keyboard";
|
||||
import { eSubscribeEvent } from "../../services/event-manager";
|
||||
import { useEditorStore } from "../../stores/use-editor-store";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { openLinkInBrowser } from "../../utils/functions";
|
||||
import { NoteType } from "../../utils/types";
|
||||
import EditorOverlay from "./loading";
|
||||
import { EDITOR_URI } from "./source";
|
||||
import { EditorProps, useEditorType } from "./tiptap/types";
|
||||
import { useEditor } from "./tiptap/use-editor";
|
||||
import { useEditorEvents } from "./tiptap/use-editor-events";
|
||||
import { editorController } from "./tiptap/utils";
|
||||
import { useLayoutEffect } from "react";
|
||||
import useKeyboard from "../../hooks/use-keyboard";
|
||||
|
||||
const style: ViewStyle = {
|
||||
height: "100%",
|
||||
@@ -127,21 +125,26 @@ const Editor = React.memo(
|
||||
const onError = useCallback(() => {
|
||||
renderKey.current =
|
||||
renderKey.current === `editor-0` ? `editor-1` : `editor-0`;
|
||||
// editor.state.current.ready = false;
|
||||
//editor.setLoading(true);
|
||||
editor.state.current.ready = false;
|
||||
editor.setLoading(true);
|
||||
}, [editor]);
|
||||
|
||||
useEffect(() => {
|
||||
eSubscribeEvent("webview_reset", onError);
|
||||
EV.subscribe(EVENTS.mediaAttachmentDownloaded, onMediaDownloaded);
|
||||
const sub = [
|
||||
eSubscribeEvent("webview_reset", onError),
|
||||
EV.subscribe(EVENTS.mediaAttachmentDownloaded, onMediaDownloaded)
|
||||
];
|
||||
|
||||
return () => {
|
||||
eUnSubscribeEvent("webview_reset", onError);
|
||||
sub.forEach((s) => s.unsubscribe());
|
||||
EV.unsubscribe(EVENTS.mediaAttachmentDownloaded, onMediaDownloaded);
|
||||
};
|
||||
}, [onError, onMediaDownloaded]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
onLoad && onLoad();
|
||||
setImmediate(() => {
|
||||
onLoad && onLoad();
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [onLoad]);
|
||||
|
||||
@@ -193,9 +196,8 @@ const Editor = React.memo(
|
||||
autoManageStatusBarEnabled={false}
|
||||
onMessage={onMessage || undefined}
|
||||
/>
|
||||
{editorId === "shareEditor" ? null : (
|
||||
<AppSection editor={editor} editorId={editorId} />
|
||||
)}
|
||||
<EditorOverlay editorId={editorId || ""} editor={editor} />
|
||||
<ReadonlyButton editor={editor} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -205,32 +207,6 @@ const Editor = React.memo(
|
||||
|
||||
export default Editor;
|
||||
|
||||
let EditorOverlay: React.ElementType;
|
||||
let IconButton: React.ElementType;
|
||||
const AppSection = ({
|
||||
editor,
|
||||
editorId
|
||||
}: {
|
||||
editor: useEditorType;
|
||||
editorId: string;
|
||||
}) => {
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
useEffect(() => {
|
||||
EditorOverlay = require("./loading.js").default;
|
||||
IconButton =
|
||||
require("../../components/ui/icon-button/index.tsx").IconButton;
|
||||
setLoaded(true);
|
||||
}, []);
|
||||
return loaded ? (
|
||||
<>
|
||||
{EditorOverlay ? (
|
||||
<EditorOverlay editorId={editorId || ""} editor={editor} />
|
||||
) : null}
|
||||
{editorId === "" ? <ReadonlyButton editor={editor} /> : null}
|
||||
</>
|
||||
) : null;
|
||||
};
|
||||
|
||||
const ReadonlyButton = ({ editor }: { editor: useEditorType }) => {
|
||||
const readonly = useEditorStore((state) => state.readonly);
|
||||
const keyboard = useKeyboard();
|
||||
@@ -244,7 +220,7 @@ const ReadonlyButton = ({ editor }: { editor: useEditorType }) => {
|
||||
}
|
||||
};
|
||||
|
||||
return readonly && IconButton && !keyboard.keyboardShown ? (
|
||||
return readonly && !keyboard.keyboardShown ? (
|
||||
<IconButton
|
||||
name="pencil-lock"
|
||||
type="grayBg"
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Sodium from "@ammarahmed/react-native-sodium";
|
||||
import { isImage } from "@notesnook/core/utils/filename";
|
||||
import { isImage } from "@notesnook/core/dist/utils/filename";
|
||||
import { Platform } from "react-native";
|
||||
import RNFetchBlob from "react-native-blob-util";
|
||||
import DocumentPicker from "react-native-document-picker";
|
||||
|
||||
@@ -48,7 +48,7 @@ export type Settings = {
|
||||
keyboardShown?: boolean;
|
||||
doubleSpacedLines?: boolean;
|
||||
corsProxy: string;
|
||||
fontSize: string;
|
||||
fontSize: number;
|
||||
fontFamily: string;
|
||||
dateFormat: string;
|
||||
timeFormat: string;
|
||||
|
||||
@@ -189,7 +189,10 @@ export const useEditorEvents = (
|
||||
noToolbar: readonly || editorPropReadonly || noToolbar,
|
||||
doubleSpacedLines: doubleSpacedLines,
|
||||
corsProxy: corsProxy,
|
||||
fontSize: defaultFontSize,
|
||||
fontSize:
|
||||
typeof defaultFontSize === "string"
|
||||
? parseInt(defaultFontSize)
|
||||
: defaultFontSize,
|
||||
fontFamily: SettingsService.get().defaultFontFamily,
|
||||
dateFormat: db.settings?.getDateFormat(),
|
||||
timeFormat: db.settings?.getTimeFormat()
|
||||
|
||||
@@ -17,7 +17,8 @@ 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 { EVENTS } from "@notesnook/core/common";
|
||||
import { getFormattedDate } from "@notesnook/common";
|
||||
import { EVENTS } from "@notesnook/core/dist/common";
|
||||
import { useThemeEngineStore } from "@notesnook/theme";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import WebView from "react-native-webview";
|
||||
@@ -31,6 +32,7 @@ import {
|
||||
openVault
|
||||
} from "../../../services/event-manager";
|
||||
import Navigation from "../../../services/navigation";
|
||||
import Notifications from "../../../services/notifications";
|
||||
import SettingsService from "../../../services/settings";
|
||||
import { TipManager } from "../../../services/tip-manager";
|
||||
import { useEditorStore } from "../../../stores/use-editor-store";
|
||||
@@ -52,7 +54,6 @@ import {
|
||||
makeSessionId,
|
||||
post
|
||||
} from "./utils";
|
||||
import { getFormattedDate } from "@notesnook/common";
|
||||
|
||||
export const useEditor = (
|
||||
editorId = "",
|
||||
@@ -80,6 +81,7 @@ export const useEditor = (
|
||||
const lock = useRef(false);
|
||||
const loadedImages = useRef<{ [name: string]: boolean }>({});
|
||||
const lockedSessionId = useRef<string>();
|
||||
const loadingState = useRef<string>();
|
||||
|
||||
const postMessage = useCallback(
|
||||
async <T>(type: string, data: T, waitFor = 300) =>
|
||||
@@ -117,9 +119,8 @@ export const useEditor = (
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
setLoading(false);
|
||||
} else {
|
||||
state.current.ready = false;
|
||||
setLoading(false);
|
||||
}
|
||||
}, [loading]);
|
||||
|
||||
@@ -133,7 +134,7 @@ export const useEditor = (
|
||||
|
||||
const reset = useCallback(
|
||||
async (resetState = true, resetContent = true) => {
|
||||
currentNote.current?.id && db.fs?.cancel(currentNote.current.id);
|
||||
currentNote.current?.id && db.fs?.cancel(currentNote.current.id, null);
|
||||
currentNote.current = null;
|
||||
loadedImages.current = {};
|
||||
currentContent.current = null;
|
||||
@@ -234,6 +235,10 @@ export const useEditor = (
|
||||
id && useEditorStore.getState().setCurrentlyEditingNote(id);
|
||||
});
|
||||
}
|
||||
|
||||
if (Notifications.isNotePinned(id as string)) {
|
||||
Notifications.pinNote(id as string);
|
||||
}
|
||||
} else {
|
||||
noteData.contentId = note?.contentId;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -363,6 +368,9 @@ export const useEditor = (
|
||||
isDefaultEditor && editorState.setCurrentlyEditingNote(item.id);
|
||||
currentNote.current && (await reset(false, false));
|
||||
await loadContent(item as NoteType);
|
||||
if (loadingState.current === currentContent.current?.data) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!currentContent.current?.data ||
|
||||
currentContent.current?.data.length < 50000
|
||||
@@ -385,11 +393,13 @@ export const useEditor = (
|
||||
currentNote.current = item as NoteType;
|
||||
await commands.setStatus(getFormattedDate(item.dateEdited), "Saved");
|
||||
await postMessage(EditorEvents.title, item.title);
|
||||
loadingState.current = currentContent.current?.data;
|
||||
await postMessage(
|
||||
EditorEvents.html,
|
||||
currentContent.current?.data,
|
||||
10000
|
||||
);
|
||||
loadingState.current = undefined;
|
||||
useEditorStore.getState().setReadonly(item.readonly);
|
||||
await commands.setTags(currentNote.current);
|
||||
commands.setSettings();
|
||||
@@ -601,9 +611,11 @@ export const useEditor = (
|
||||
|
||||
const onReady = useCallback(async () => {
|
||||
if (!(await isEditorLoaded(editorRef, sessionIdRef.current))) {
|
||||
eSendEvent("webview_reset");
|
||||
eSendEvent("webview_reset", "onReady");
|
||||
return false;
|
||||
} else {
|
||||
isDefaultEditor && restoreEditorState();
|
||||
return true;
|
||||
}
|
||||
}, [isDefaultEditor, restoreEditorState]);
|
||||
|
||||
@@ -616,15 +628,18 @@ export const useEditor = (
|
||||
isDefaultEditor ? insets : { top: 0, left: 0, right: 0, bottom: 0 }
|
||||
);
|
||||
await commands.setSessionId(sessionIdRef.current);
|
||||
await onReady();
|
||||
state.current.ready = true;
|
||||
await commands.setSettings();
|
||||
if (currentNote.current) {
|
||||
loadNote({ ...currentNote.current, forced: true });
|
||||
} else {
|
||||
await commands.setPlaceholder(placeholderTip.current);
|
||||
}
|
||||
}, 1000);
|
||||
timers.current["editor:loaded"] = setTimeout(async () => {
|
||||
if (!state.current.ready && (await onReady())) {
|
||||
state.current.ready = true;
|
||||
}
|
||||
if (currentNote.current) {
|
||||
loadNote({ ...currentNote.current, forced: true });
|
||||
} else {
|
||||
await commands.setPlaceholder(placeholderTip.current);
|
||||
}
|
||||
});
|
||||
});
|
||||
}, [
|
||||
onReady,
|
||||
postMessage,
|
||||
|
||||
@@ -29,6 +29,7 @@ import SettingsService from "../../services/settings";
|
||||
import useNavigationStore from "../../stores/use-navigation-store";
|
||||
import { useNoteStore } from "../../stores/use-notes-store";
|
||||
import { openEditor } from "../notes/common";
|
||||
|
||||
const prepareSearch = () => {
|
||||
SearchService.update({
|
||||
placeholder: "Type a keyword to search in notes",
|
||||
|
||||
@@ -16,7 +16,7 @@ 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 { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import { groupArray } from "@notesnook/core/dist/utils/grouping";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { db } from "../../common/database";
|
||||
import DelayLayout from "../../components/delay-layout";
|
||||
|
||||
@@ -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 { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import { groupArray } from "@notesnook/core/dist/utils/grouping";
|
||||
import React from "react";
|
||||
import NotesPage, { PLACEHOLDER_DATA } from ".";
|
||||
import { db } from "../../common/database";
|
||||
|
||||
@@ -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 { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import { groupArray } from "@notesnook/core/dist/utils/grouping";
|
||||
import React from "react";
|
||||
import NotesPage, { PLACEHOLDER_DATA } from ".";
|
||||
import { db } from "../../common/database";
|
||||
|
||||
@@ -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 { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import { groupArray } from "@notesnook/core/dist/utils/grouping";
|
||||
import React from "react";
|
||||
import NotesPage, { PLACEHOLDER_DATA } from ".";
|
||||
import { db } from "../../common/database";
|
||||
|
||||
@@ -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 { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import { groupArray } from "@notesnook/core/dist/utils/grouping";
|
||||
import React from "react";
|
||||
import NotesPage, { PLACEHOLDER_DATA } from ".";
|
||||
import { db } from "../../common/database";
|
||||
|
||||
@@ -66,6 +66,7 @@ export const Reminders = ({
|
||||
});
|
||||
|
||||
SearchService.prepareSearch = prepareSearch;
|
||||
useNavigationStore.getState().setButtonAction(PLACEHOLDER_DATA.action);
|
||||
return !prev?.current;
|
||||
},
|
||||
onBlur: () => false,
|
||||
|
||||
@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Clipboard from "@react-native-clipboard/clipboard";
|
||||
import { LogMessage } from "@notesnook/logger";
|
||||
import { format, LogLevel, logManager } from "@notesnook/core/logger";
|
||||
import { format, LogLevel, logManager } from "@notesnook/core/dist/logger";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { FlatList, Platform, TouchableOpacity, View } from "react-native";
|
||||
import * as ScopedStorage from "react-native-scoped-storage";
|
||||
|
||||
@@ -42,7 +42,8 @@ export async function verifyUser(
|
||||
negativeText: closeText || "Cancel",
|
||||
positivePress: async (value) => {
|
||||
try {
|
||||
let verified = await db.user.verifyPassword(value);
|
||||
const user = await db.user.getUser();
|
||||
let verified = !user ? true : await db.user.verifyPassword(value);
|
||||
if (verified) {
|
||||
sleep(300).then(async () => {
|
||||
await onsuccess();
|
||||
@@ -58,7 +59,7 @@ export async function verifyUser(
|
||||
}
|
||||
} catch (e) {
|
||||
ToastEvent.show({
|
||||
heading: "Failed to backup data",
|
||||
heading: "Failed to verify",
|
||||
message: e.message,
|
||||
type: "error",
|
||||
context: "global"
|
||||
|
||||
@@ -87,12 +87,6 @@ const Home = ({
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Heading color={colors.primary.heading} size={SIZE.lg}>
|
||||
Logging out
|
||||
</Heading>
|
||||
<Paragraph color={colors.secondary.icon}>
|
||||
Please wait while we log out and clear app data.
|
||||
</Paragraph>
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
|
||||
@@ -27,6 +27,8 @@ import PremiumService from "../../../services/premium";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import { sleep } from "../../../utils/time";
|
||||
import { verifyUser } from "../functions";
|
||||
import { Dialog } from "../../../components/dialog";
|
||||
|
||||
interface PickerOptions<T> {
|
||||
getValue: () => T;
|
||||
@@ -37,6 +39,7 @@ interface PickerOptions<T> {
|
||||
options: T[];
|
||||
premium?: boolean;
|
||||
onCheckOptionIsPremium?: (item: T) => boolean;
|
||||
requiresVerification?: () => boolean;
|
||||
}
|
||||
|
||||
export function SettingsPicker<T>({
|
||||
@@ -47,7 +50,8 @@ export function SettingsPicker<T>({
|
||||
options,
|
||||
getItemKey,
|
||||
premium,
|
||||
onCheckOptionIsPremium = () => true
|
||||
onCheckOptionIsPremium = () => true,
|
||||
requiresVerification = () => false
|
||||
}: PickerOptions<T>) {
|
||||
const { colors } = useThemeColors("contextMenu");
|
||||
const menuRef = useRef<any>();
|
||||
@@ -118,11 +122,18 @@ export function SettingsPicker<T>({
|
||||
</PressableButton>
|
||||
}
|
||||
>
|
||||
<Dialog context="local" />
|
||||
{options.map((item) => (
|
||||
<MenuItem
|
||||
key={getItemKey(item)}
|
||||
onPress={async () => {
|
||||
onChange(item);
|
||||
if (requiresVerification?.()) {
|
||||
verifyUser("local", () => {
|
||||
onChange(item);
|
||||
});
|
||||
} else {
|
||||
onChange(item);
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
backgroundColor: compareValue(currentValue, item)
|
||||
|
||||
@@ -21,11 +21,12 @@ import { db } from "../../../common/database";
|
||||
import { ToastEvent } from "../../../services/event-manager";
|
||||
import SettingsService from "../../../services/settings";
|
||||
import { useSettingStore } from "../../../stores/use-setting-store";
|
||||
import { MenuItemsList } from "../../../utils/constants";
|
||||
import { MenuItemsList } from "../../../utils/menu-items";
|
||||
import { createSettingsPicker } from ".";
|
||||
import { getFontById, getFonts } from "@notesnook/editor/dist/utils/font";
|
||||
import { DATE_FORMATS, TIME_FORMATS } from "@notesnook/core/common";
|
||||
import { DATE_FORMATS, TIME_FORMATS } from "@notesnook/core/dist/common";
|
||||
import dayjs from "dayjs";
|
||||
import { useUserStore } from "../../../stores/use-user-store";
|
||||
|
||||
export const FontPicker = createSettingsPicker({
|
||||
getValue: () => useSettingStore.getState().settings.defaultFontFamily,
|
||||
@@ -127,6 +128,12 @@ export const BackupReminderPicker = createSettingsPicker({
|
||||
options: ["useroff", "daily", "weekly", "monthly"],
|
||||
compareValue: (current, item) => current === item,
|
||||
premium: true,
|
||||
requiresVerification: () => {
|
||||
return (
|
||||
!useSettingStore.getState().settings.encryptedBackup &&
|
||||
useUserStore.getState().user
|
||||
);
|
||||
},
|
||||
onCheckOptionIsPremium: (item) => {
|
||||
return item !== "useroff";
|
||||
}
|
||||
|
||||
@@ -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 { ThemeDark, ThemePitchBlack } from "@notesnook/theme";
|
||||
import notifee from "@notifee/react-native";
|
||||
import dayjs from "dayjs";
|
||||
import React from "react";
|
||||
@@ -288,9 +287,9 @@ export const settingsGroups: SettingSection[] = [
|
||||
positiveText: "Logout",
|
||||
positivePress: async () => {
|
||||
try {
|
||||
eSendEvent("settings-loading", true);
|
||||
setImmediate(async () => {
|
||||
eSendEvent(eCloseSimpleDialog);
|
||||
eSendEvent(eCloseSimpleDialog);
|
||||
setTimeout(async () => {
|
||||
eSendEvent("settings-loading", true);
|
||||
Navigation.popToTop();
|
||||
await db.user?.logout();
|
||||
setLoginMessage();
|
||||
@@ -306,7 +305,7 @@ export const settingsGroups: SettingSection[] = [
|
||||
setTimeout(() => {
|
||||
eSendEvent("settings-loading", false);
|
||||
}, 2000);
|
||||
});
|
||||
}, 300);
|
||||
} catch (e) {
|
||||
ToastEvent.error(e as Error, "Error logging out");
|
||||
eSendEvent("settings-loading", false);
|
||||
@@ -783,10 +782,11 @@ export const settingsGroups: SettingSection[] = [
|
||||
description: "Create a backup of your data",
|
||||
modifer: async () => {
|
||||
const user = useUserStore.getState().user;
|
||||
if (!user) {
|
||||
if (!user || SettingsService.getProperty("encryptedBackup")) {
|
||||
await BackupService.run(true);
|
||||
return;
|
||||
}
|
||||
|
||||
verifyUser(null, () => BackupService.run(true));
|
||||
}
|
||||
},
|
||||
@@ -870,9 +870,17 @@ export const settingsGroups: SettingSection[] = [
|
||||
});
|
||||
return;
|
||||
}
|
||||
SettingsService.set({
|
||||
encryptedBackup: !settings.encryptedBackup
|
||||
});
|
||||
if (settings.encryptedBackup) {
|
||||
await verifyUser(null, () => {
|
||||
SettingsService.set({
|
||||
encryptedBackup: false
|
||||
});
|
||||
});
|
||||
} else {
|
||||
SettingsService.set({
|
||||
encryptedBackup: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -902,7 +910,9 @@ export const settingsGroups: SettingSection[] = [
|
||||
description:
|
||||
"Export all notes as pdf, markdown, html or text in a single zip file",
|
||||
modifer: () => {
|
||||
ExportNotesSheet.present(undefined, true);
|
||||
verifyUser(null, () => {
|
||||
ExportNotesSheet.present(undefined, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -972,7 +982,7 @@ export const settingsGroups: SettingSection[] = [
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "reminder-sound",
|
||||
id: "reminder-sound-ios",
|
||||
type: "screen",
|
||||
name: "Change notification sound",
|
||||
description:
|
||||
@@ -984,7 +994,7 @@ export const settingsGroups: SettingSection[] = [
|
||||
(Platform.OS === "android" && Platform.Version > 25)
|
||||
},
|
||||
{
|
||||
id: "reminder-sound",
|
||||
id: "reminder-sound-android",
|
||||
name: "Change notification sound",
|
||||
description:
|
||||
"Set the notification sound for reminder notifications",
|
||||
|
||||
@@ -51,11 +51,11 @@ import { ToastEvent, presentSheet } from "../../services/event-manager";
|
||||
import { useThemeStore } from "../../stores/use-theme-store";
|
||||
import { SIZE } from "../../utils/size";
|
||||
import { getElevationStyle } from "../../utils/elevation";
|
||||
import { MenuItemsList } from "../../utils/constants";
|
||||
import { MenuItemsList } from "../../utils/menu-items";
|
||||
import { IconButton } from "../../components/ui/icon-button";
|
||||
import { PressableButton } from "../../components/ui/pressable";
|
||||
|
||||
const THEME_SERVER_URL = "https://themes.notesnook.com";
|
||||
const THEME_SERVER_URL = "https://themes-api.notesnook.com";
|
||||
//@ts-ignore
|
||||
export const themeTrpcClient = createTRPCProxyClient<ThemesRouter>({
|
||||
links: [
|
||||
@@ -103,10 +103,12 @@ function ThemeSelector() {
|
||||
}
|
||||
);
|
||||
|
||||
const select = (item: Partial<ThemeMetadata>) => {
|
||||
const select = (item: Partial<ThemeMetadata>, fromFile?: boolean) => {
|
||||
presentSheet({
|
||||
context: "theme-details",
|
||||
component: (ref, close) => <ThemeSetter close={close} theme={item} />
|
||||
component: (ref, close) => (
|
||||
<ThemeSetter close={close} theme={item} fromFile={fromFile} />
|
||||
)
|
||||
});
|
||||
};
|
||||
|
||||
@@ -327,7 +329,6 @@ function ThemeSelector() {
|
||||
) || []
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<SheetProvider context="theme-details" />
|
||||
@@ -397,16 +398,17 @@ function ThemeSelector() {
|
||||
icon="folder"
|
||||
fontSize={SIZE.xs}
|
||||
onPress={() => {
|
||||
DocumentPicker.pickSingle().then((r) => {
|
||||
DocumentPicker.pickSingle({
|
||||
allowMultiSelection: false
|
||||
}).then((r) => {
|
||||
fetch(r.uri).then(async (response) => {
|
||||
const json = await response.json();
|
||||
console.log(json);
|
||||
const result = validateTheme(json);
|
||||
if (result.error) {
|
||||
ToastEvent.error(new Error(result.error));
|
||||
return;
|
||||
}
|
||||
select(json);
|
||||
select(json, true);
|
||||
});
|
||||
});
|
||||
}}
|
||||
@@ -445,6 +447,22 @@ function ThemeSelector() {
|
||||
)}
|
||||
</View>
|
||||
}
|
||||
ListFooterComponent={
|
||||
themes.isError ? (
|
||||
<View
|
||||
style={{
|
||||
height: 100,
|
||||
width: "100%",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Paragraph color={colors.error.paragraph}>
|
||||
Error loading themes. {themes.error.message}.
|
||||
</Paragraph>
|
||||
</View>
|
||||
) : null
|
||||
}
|
||||
estimatedItemSize={200}
|
||||
renderItem={renderItem}
|
||||
onEndReachedThreshold={0.1}
|
||||
@@ -483,10 +501,12 @@ export default function ThemeSelectorWithQueryClient() {
|
||||
|
||||
const ThemeSetter = ({
|
||||
theme,
|
||||
close
|
||||
close,
|
||||
fromFile
|
||||
}: {
|
||||
theme: Partial<CompiledThemeDefinition>;
|
||||
close?: (ctx?: string) => void;
|
||||
fromFile?: boolean;
|
||||
}) => {
|
||||
const [darkTheme, lightTheme] = useThemeStore((state) => [
|
||||
state.darkTheme,
|
||||
@@ -502,14 +522,13 @@ const ThemeSetter = ({
|
||||
if (!theme.id) return;
|
||||
try {
|
||||
const user = await db.user?.getUser();
|
||||
const fullTheme = theme.scopes
|
||||
const fullTheme = fromFile
|
||||
? (theme as ThemeDefinition)
|
||||
: await themeTrpcClient.installTheme.query({
|
||||
compatibilityVersion: THEME_COMPATIBILITY_VERSION,
|
||||
id: theme.id,
|
||||
userId: user?.id
|
||||
});
|
||||
|
||||
if (!fullTheme) return;
|
||||
theme.colorScheme === "dark"
|
||||
? useThemeStore.getState().setDarkTheme(fullTheme)
|
||||
|
||||
@@ -114,7 +114,7 @@ export const Trash = ({ navigation, route }: NavigationProps<"Trash">) => {
|
||||
<FloatingButton
|
||||
title="Clear all trash"
|
||||
onPress={onPressFloatingButton}
|
||||
shouldShow={true}
|
||||
alwaysVisible={true}
|
||||
/>
|
||||
) : null}
|
||||
</DelayLayout>
|
||||
|
||||
@@ -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 EventManager from "@notesnook/core/utils/event-manager";
|
||||
import EventManager from "@notesnook/core/dist/utils/event-manager";
|
||||
import Clipboard from "@react-native-clipboard/clipboard";
|
||||
import { RefObject } from "react";
|
||||
import { ActionSheetRef } from "react-native-actions-sheet";
|
||||
|
||||
@@ -46,6 +46,9 @@ import { useRelationStore } from "../stores/use-relation-store";
|
||||
import { useReminderStore } from "../stores/use-reminder-store";
|
||||
import { presentDialog } from "../components/dialog/functions";
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
import { encodeNonAsciiHTML } from "entities";
|
||||
import { convertNoteToText } from "../utils/note-to-text";
|
||||
|
||||
|
||||
export type Reminder = {
|
||||
id: string;
|
||||
@@ -86,6 +89,28 @@ async function getNextMonthlyReminderDate(
|
||||
return await getNextMonthlyReminderDate(reminder, dayjs().year() + 1);
|
||||
}
|
||||
|
||||
export function textToHTML(src: string) {
|
||||
return src
|
||||
.split(/[\r\n]/)
|
||||
.map((line) =>
|
||||
line
|
||||
? `<p data-spacing="single">${encodeLine(line)}</p>`
|
||||
: `<p data-spacing="single"></p>`
|
||||
)
|
||||
.join("");
|
||||
}
|
||||
|
||||
function encodeLine(line: string) {
|
||||
line = encodeNonAsciiHTML(line);
|
||||
line = line.replace(/(^ +)|( {2,})/g, (sub, ...args) => {
|
||||
const [starting, inline] = args;
|
||||
if (starting) return " ".repeat(starting.length);
|
||||
if (inline) return " ".repeat(inline.length);
|
||||
return sub;
|
||||
});
|
||||
return line;
|
||||
}
|
||||
|
||||
async function initDatabase(notes = true) {
|
||||
if (!db.isInitialized) {
|
||||
await db.initCollections();
|
||||
@@ -206,16 +231,41 @@ const onEvent = async ({ type, detail }: Event) => {
|
||||
});
|
||||
if (!db.isInitialized) await db.init();
|
||||
await db.notes?.init();
|
||||
await db.notes?.add({
|
||||
|
||||
const id = await db.notes?.add({
|
||||
content: {
|
||||
type: "tiptap",
|
||||
data: `<p>${input} </p>`
|
||||
data: textToHTML(input as string)
|
||||
}
|
||||
});
|
||||
|
||||
const defaultNotebook = db.settings?.getDefaultNotebook();
|
||||
|
||||
if (defaultNotebook) {
|
||||
if (!defaultNotebook.topic) {
|
||||
await db.relations?.add(
|
||||
{ type: "notebook", id: defaultNotebook.id },
|
||||
{ type: "note", id: id }
|
||||
);
|
||||
} else {
|
||||
await db.notes?.addToNotebook(
|
||||
{
|
||||
topic: defaultNotebook.topic,
|
||||
id: defaultNotebook?.id
|
||||
},
|
||||
id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const status = await NetInfo.fetch();
|
||||
if (status.isInternetReachable) {
|
||||
try {
|
||||
await db.sync(false, false);
|
||||
if (!globalThis["IS_MAIN_APP_RUNNING" as never]) {
|
||||
await db.sync(false, false);
|
||||
} else {
|
||||
console.log("main app running, skipping sync");
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e, (e as Error).stack);
|
||||
}
|
||||
@@ -757,6 +807,11 @@ function getPinnedNotes(): DisplayedNotification[] {
|
||||
return pinned;
|
||||
}
|
||||
|
||||
function isNotePinned(id: string) {
|
||||
if (Platform.OS !== "android") return false;
|
||||
return pinned.findIndex((notification) => notification.id === id) > -1;
|
||||
}
|
||||
|
||||
function get(): Promise<DisplayedNotification[]> {
|
||||
return new Promise((resolve) => {
|
||||
if (Platform.OS === "ios") resolve([]);
|
||||
@@ -844,6 +899,26 @@ async function setupReminders(checkNeedsScheduling = false) {
|
||||
);
|
||||
}
|
||||
|
||||
async function pinNote(id: string) {
|
||||
try {
|
||||
const note = db.notes?.note(id as string) as any;
|
||||
let text = await convertNoteToText(note as any, false);
|
||||
if (!text) text = "";
|
||||
let html = text.replace(/\n/g, "<br />");
|
||||
Notifications.displayNotification({
|
||||
title: note.title,
|
||||
message: note.headline || text,
|
||||
subtitle: note.headline || text,
|
||||
bigText: html,
|
||||
ongoing: true,
|
||||
actions: ["UNPIN"],
|
||||
id: note.id
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
const Notifications = {
|
||||
init,
|
||||
displayNotification,
|
||||
@@ -859,7 +934,9 @@ const Notifications = {
|
||||
checkAndRequestPermissions,
|
||||
clearAllTriggers,
|
||||
setupReminders,
|
||||
getChannelId
|
||||
getChannelId,
|
||||
isNotePinned,
|
||||
pinNote
|
||||
};
|
||||
|
||||
export default Notifications;
|
||||
|
||||
@@ -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 { CHECK_IDS } from "@notesnook/core/common";
|
||||
import { CHECK_IDS } from "@notesnook/core/dist/common";
|
||||
import React from "react";
|
||||
import { Platform } from "react-native";
|
||||
import Config from "react-native-config";
|
||||
|
||||
@@ -166,4 +166,5 @@ export const SettingsService = {
|
||||
resetSettings
|
||||
};
|
||||
|
||||
init();
|
||||
export default SettingsService;
|
||||
|
||||
@@ -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 { EVENTS } from "@notesnook/core/common";
|
||||
import { EVENTS } from "@notesnook/core/dist/common";
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
import { db } from "../common/database";
|
||||
import { DatabaseLogger } from "../common/database/index";
|
||||
@@ -41,6 +41,11 @@ const run = async (
|
||||
full = true,
|
||||
onCompleted
|
||||
) => {
|
||||
if (useUserStore.getState().syncing) {
|
||||
DatabaseLogger.log("Sync in progress");
|
||||
console.log("Sync in progress");
|
||||
return;
|
||||
}
|
||||
clearTimeout(syncTimer);
|
||||
syncTimer = setTimeout(async () => {
|
||||
const status = await NetInfo.fetch();
|
||||
|
||||
@@ -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 { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import { groupArray } from "@notesnook/core/dist/utils/grouping";
|
||||
import create, { State } from "zustand";
|
||||
import { db } from "../common/database";
|
||||
import { NoteType } from "../utils/types";
|
||||
|
||||
@@ -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 { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import { groupArray } from "@notesnook/core/dist/utils/grouping";
|
||||
import create, { State } from "zustand";
|
||||
import { db } from "../common/database";
|
||||
import { NotebookType } from "../utils/types";
|
||||
|
||||
@@ -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 { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import { groupArray } from "@notesnook/core/dist/utils/grouping";
|
||||
import create, { State } from "zustand";
|
||||
import { db } from "../common/database";
|
||||
import { NoteType } from "../utils/types";
|
||||
|
||||
@@ -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 { groupReminders } from "@notesnook/core/utils/grouping";
|
||||
import { groupReminders } from "@notesnook/core/dist/utils/grouping";
|
||||
import create, { State } from "zustand";
|
||||
import { db } from "../common/database";
|
||||
import { Reminder } from "../services/notifications";
|
||||
|
||||
@@ -69,7 +69,7 @@ export type Settings = {
|
||||
corsProxy: string;
|
||||
disableRealtimeSync?: boolean;
|
||||
notificationSound?: Sound & { platform: PlatformOSType };
|
||||
defaultFontSize: string;
|
||||
defaultFontSize: number;
|
||||
defaultFontFamily: string;
|
||||
colorScheme: "dark" | "light";
|
||||
lighTheme: ThemeDefinition;
|
||||
@@ -149,7 +149,7 @@ export const defaultSettings: SettingStore["settings"] = {
|
||||
reminderNotificationMode: "urgent",
|
||||
notificationSound: undefined,
|
||||
defaultFontFamily: "sans-serif",
|
||||
defaultFontSize: "16",
|
||||
defaultFontSize: 16,
|
||||
colorScheme: "light",
|
||||
lighTheme: ThemeLight,
|
||||
darkTheme: ThemeDark
|
||||
|
||||
@@ -17,8 +17,8 @@ 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 "@notesnook/core/types";
|
||||
import { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import "@notesnook/core/dist/types";
|
||||
import { groupArray } from "@notesnook/core/dist/utils/grouping";
|
||||
import create, { State } from "zustand";
|
||||
import { db } from "../common/database";
|
||||
import { TagType } from "../utils/types";
|
||||
|
||||
@@ -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 { groupArray } from "@notesnook/core/utils/grouping";
|
||||
import { groupArray } from "@notesnook/core/dist/utils/grouping";
|
||||
import create, { State } from "zustand";
|
||||
import { db } from "../common/database";
|
||||
import { TrashType } from "../utils/types";
|
||||
|
||||
@@ -100,49 +100,6 @@ export const SUBSCRIPTION_PROVIDER = {
|
||||
}
|
||||
};
|
||||
|
||||
export const MenuItemsList = [
|
||||
{
|
||||
name: "Notes",
|
||||
icon: "home-variant-outline",
|
||||
close: true
|
||||
},
|
||||
{
|
||||
name: "Notebooks",
|
||||
icon: "book-outline",
|
||||
close: true
|
||||
},
|
||||
{
|
||||
name: "Favorites",
|
||||
icon: "star-outline",
|
||||
close: true
|
||||
},
|
||||
{
|
||||
name: "Tags",
|
||||
icon: "pound",
|
||||
close: true
|
||||
},
|
||||
{
|
||||
name: "Reminders",
|
||||
icon: "bell",
|
||||
close: true,
|
||||
isBeta: true
|
||||
},
|
||||
{
|
||||
name: "Monographs",
|
||||
icon: "text-box-multiple-outline",
|
||||
close: true,
|
||||
func: () => {
|
||||
const Monographs = require("../screens/notes/monographs").Monographs;
|
||||
Monographs.navigate();
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Trash",
|
||||
icon: "delete-outline",
|
||||
close: true
|
||||
}
|
||||
];
|
||||
|
||||
export const BUTTON_TYPES = {
|
||||
transparent: {
|
||||
primary: "transparent",
|
||||
|
||||
60
apps/mobile/app/utils/menu-items.js
Normal file
60
apps/mobile/app/utils/menu-items.js
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
export const MenuItemsList = [
|
||||
{
|
||||
name: "Notes",
|
||||
icon: "home-variant-outline",
|
||||
close: true
|
||||
},
|
||||
{
|
||||
name: "Notebooks",
|
||||
icon: "book-outline",
|
||||
close: true
|
||||
},
|
||||
{
|
||||
name: "Favorites",
|
||||
icon: "star-outline",
|
||||
close: true
|
||||
},
|
||||
{
|
||||
name: "Tags",
|
||||
icon: "pound",
|
||||
close: true
|
||||
},
|
||||
{
|
||||
name: "Reminders",
|
||||
icon: "bell",
|
||||
close: true,
|
||||
isBeta: true
|
||||
},
|
||||
{
|
||||
name: "Monographs",
|
||||
icon: "text-box-multiple-outline",
|
||||
close: true,
|
||||
func: () => {
|
||||
const Monographs = require("../screens/notes/monographs").Monographs;
|
||||
Monographs.navigate();
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "Trash",
|
||||
icon: "delete-outline",
|
||||
close: true
|
||||
}
|
||||
];
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Sodium from "@ammarahmed/react-native-sodium";
|
||||
import { isImage } from "@notesnook/core/utils/filename";
|
||||
import { isImage } from "@notesnook/core/dist/utils/filename";
|
||||
import { Platform } from "react-native";
|
||||
import RNFetchBlob from "react-native-blob-util";
|
||||
import { db } from "../common/database";
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"testEnvironment": "./environment",
|
||||
"testRunner": "jest-circus/runner",
|
||||
"setupFilesAfterEnv": ["./setup.ts"],
|
||||
"testTimeout": 120000,
|
||||
"testRegex": "\\.e2e\\.js$",
|
||||
"reporters": ["detox/runners/jest/streamlineReporter"],
|
||||
"transformIgnorePatterns": ["<rootDir>/node_modules/"],
|
||||
"transform": {
|
||||
"^.+\\.(js|jsx|ts|tsx)$": [
|
||||
"babel-jest",
|
||||
{ "configFile": "../native/babel.config.js" }
|
||||
]
|
||||
},
|
||||
"verbose": true
|
||||
}
|
||||
@@ -1,57 +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/>.
|
||||
*/
|
||||
|
||||
const {
|
||||
DetoxCircusEnvironment,
|
||||
SpecReporter,
|
||||
WorkerAssignReporter
|
||||
} = require("detox/runners/jest-circus");
|
||||
|
||||
const sleep = (duration) =>
|
||||
new Promise((resolve) =>
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, duration)
|
||||
);
|
||||
|
||||
class CustomDetoxEnvironment extends DetoxCircusEnvironment {
|
||||
constructor(config, context) {
|
||||
super(config, context);
|
||||
|
||||
// Can be safely removed, if you are content with the default value (=300000ms)
|
||||
this.initTimeout = 300000;
|
||||
|
||||
// This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level.
|
||||
// This is strictly optional.
|
||||
this.registerListeners({
|
||||
SpecReporter,
|
||||
WorkerAssignReporter
|
||||
});
|
||||
}
|
||||
|
||||
async initDetox() {
|
||||
const instance = await this.detox.init(undefined, { launchApp: false });
|
||||
await sleep(10000);
|
||||
await instance.device.reverseTcpPort(8081); // added this line
|
||||
await instance.device.launchApp();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CustomDetoxEnvironment;
|
||||
20
apps/mobile/e2e/jest.config.js
Normal file
20
apps/mobile/e2e/jest.config.js
Normal file
@@ -0,0 +1,20 @@
|
||||
/** @type {import('@jest/types').Config.InitialOptions} */
|
||||
module.exports = {
|
||||
rootDir: "..",
|
||||
testMatch: ["<rootDir>/e2e/**/*.e2e.js"],
|
||||
testTimeout: 120000,
|
||||
maxWorkers: 1,
|
||||
setupFilesAfterEnv: ["./e2e/setup.js"],
|
||||
globalSetup: "detox/runners/jest/globalSetup",
|
||||
globalTeardown: "detox/runners/jest/globalTeardown",
|
||||
reporters: ["detox/runners/jest/reporter"],
|
||||
testEnvironment: "detox/runners/jest/testEnvironment",
|
||||
verbose: true,
|
||||
transform: {
|
||||
"^.+\\.(js|jsx|ts|tsx)$": [
|
||||
"babel-jest",
|
||||
{ configFile: "../native/babel.config.js" }
|
||||
]
|
||||
},
|
||||
transformIgnorePatterns: ["<rootDir>/../node_modules/"]
|
||||
};
|
||||
7
apps/mobile/e2e/setup.js
Normal file
7
apps/mobile/e2e/setup.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import { device } from "detox";
|
||||
|
||||
beforeAll(async () => {
|
||||
await device.launchApp();
|
||||
});
|
||||
|
||||
export {};
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 80 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 81 KiB |
@@ -35,7 +35,7 @@ async function sortBy(sorting, elementText = "Default") {
|
||||
}
|
||||
|
||||
describe("Sort & filter", () => {
|
||||
it.only("Sort by date-edited/date-created", async () => {
|
||||
it("Sort by date-edited/date-created", async () => {
|
||||
await prepare();
|
||||
let webview = web(by.id(notesnook.editor.id));
|
||||
await createNote("Note 1", "Note 1");
|
||||
@@ -52,11 +52,11 @@ describe("Sort & filter", () => {
|
||||
await device.pressBack();
|
||||
await sortBy("Date created");
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await visibleByText("Note 2");
|
||||
//await visibleByText("Note 2");
|
||||
await device.pressBack();
|
||||
await sortBy("Date edited");
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await visibleByText("Edited Note 1");
|
||||
//await visibleByText("Edited Note 1");
|
||||
await device.pressBack();
|
||||
});
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ import { sleep } from "./utils";
|
||||
|
||||
async function lockNote() {
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-Vault");
|
||||
await tapById("icon-lock-unlock");
|
||||
await sleep(1000);
|
||||
await visibleByText("Lock");
|
||||
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
|
||||
@@ -46,7 +46,7 @@ async function lockNote() {
|
||||
|
||||
async function removeFromVault() {
|
||||
await tapById(notesnook.listitem.menu);
|
||||
await tapById("icon-Vault");
|
||||
await tapById("icon-lock-unlock");
|
||||
await sleep(1000);
|
||||
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
|
||||
await tapByText("Unlock");
|
||||
@@ -127,7 +127,7 @@ describe("VAULT", () => {
|
||||
await notVisibleById(notesnook.listitem.menu);
|
||||
});
|
||||
|
||||
it.only("Add a note to vault", async () => {
|
||||
it("Add a note to vault", async () => {
|
||||
await prepare();
|
||||
await createNote();
|
||||
await lockNote();
|
||||
|
||||
85
apps/mobile/native/.detoxrc.js
Normal file
85
apps/mobile/native/.detoxrc.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/** @type {Detox.DetoxConfig} */
|
||||
module.exports = {
|
||||
testRunner: {
|
||||
args: {
|
||||
'$0': 'jest',
|
||||
config: '../e2e/jest.config.js'
|
||||
},
|
||||
jest: {
|
||||
setupTimeout: 120000
|
||||
}
|
||||
},
|
||||
apps: {
|
||||
'ios.debug': {
|
||||
type: 'ios.app',
|
||||
binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/Notesnook.app',
|
||||
build: 'xcodebuild -workspace ios/Notesnook.xcworkspace -scheme YOUR_APP -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build'
|
||||
},
|
||||
'ios.release': {
|
||||
type: 'ios.app',
|
||||
binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/Notesnook.app',
|
||||
build: 'xcodebuild -workspace ios/Notesnook.xcworkspace -scheme YOUR_APP -configuration Release -sdk iphonesimulator -derivedDataPath ios/build'
|
||||
},
|
||||
'android.debug': {
|
||||
type: 'android.apk',
|
||||
"binaryPath": "android/app/build/outputs/apk/debug/app-x86_64-debug.apk",
|
||||
"testBinaryPath": "android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk",
|
||||
"build": "cd android ; ENVFILE=.env.test ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..",
|
||||
reversePorts: [
|
||||
8081
|
||||
]
|
||||
},
|
||||
'android.release': {
|
||||
type: 'android.apk',
|
||||
"binaryPath": "android/app/build/outputs/apk/release/app-x86_64-release.apk",
|
||||
"testBinaryPath": "android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk",
|
||||
"build": "cd android ; ENVFILE=.env.test ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release ; cd .."
|
||||
}
|
||||
},
|
||||
devices: {
|
||||
simulator: {
|
||||
type: 'ios.simulator',
|
||||
device: {
|
||||
type: 'iPhone 12'
|
||||
}
|
||||
},
|
||||
attached: {
|
||||
type: 'android.attached',
|
||||
device: {
|
||||
adbName: '.*'
|
||||
}
|
||||
},
|
||||
emulator: {
|
||||
type: 'android.emulator',
|
||||
device: {
|
||||
avdName: 'Pixel_5_API_31'
|
||||
}
|
||||
}
|
||||
},
|
||||
configurations: {
|
||||
'ios.sim.debug': {
|
||||
device: 'simulator',
|
||||
app: 'ios.debug'
|
||||
},
|
||||
'ios.sim.release': {
|
||||
device: 'simulator',
|
||||
app: 'ios.release'
|
||||
},
|
||||
'android.att.debug': {
|
||||
device: 'attached',
|
||||
app: 'android.debug'
|
||||
},
|
||||
'android.att.release': {
|
||||
device: 'attached',
|
||||
app: 'android.release'
|
||||
},
|
||||
'android.emu.debug': {
|
||||
device: 'emulator',
|
||||
app: 'android.debug'
|
||||
},
|
||||
'android.emu.release': {
|
||||
device: 'emulator',
|
||||
app: 'android.release'
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,52 +0,0 @@
|
||||
{
|
||||
"testRunner": "jest",
|
||||
"runnerConfig": "../e2e/config.json",
|
||||
"devices": {
|
||||
"emulator": {
|
||||
"type": "android.emulator",
|
||||
"device": {
|
||||
"avdName": "Pixel_5_API_31"
|
||||
}
|
||||
},
|
||||
"simulator": {
|
||||
"type": "ios.simulator",
|
||||
"device": {
|
||||
"type": "iPhone 8"
|
||||
}
|
||||
}
|
||||
},
|
||||
"apps": {
|
||||
"android.debug": {
|
||||
"type": "android.apk",
|
||||
"binaryPath": "android/app/build/outputs/apk/debug/app-x86_64-debug.apk",
|
||||
"testBinaryPath": "android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk",
|
||||
"build": "cd android ; ENVFILE=.env.test ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd .."
|
||||
},
|
||||
"android.release": {
|
||||
"type": "android.apk",
|
||||
"binaryPath": "android/app/build/outputs/apk/release/app-x86_64-release.apk",
|
||||
"testBinaryPath": "android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk",
|
||||
"build": "cd android ; ENVFILE=.env.test ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release ; cd .."
|
||||
},
|
||||
"ios.release": {
|
||||
"name": "Notesnook",
|
||||
"type": "ios.app",
|
||||
"binaryPath": "ios/build/Build/Products/Release-iphonesimulator/Notesnook.app",
|
||||
"build": "ENVFILE=.env.test xcodebuild -workspace ios/Notesnook.xcworkspace -scheme NotesnookRelease -sdk iphonesimulator -derivedDataPath ios/build"
|
||||
}
|
||||
},
|
||||
"configurations": {
|
||||
"android.emu.debug": {
|
||||
"app": "android.debug",
|
||||
"device": "emulator"
|
||||
},
|
||||
"android.emu.release": {
|
||||
"app": "android.release",
|
||||
"device": "emulator"
|
||||
},
|
||||
"ios.sim.release": {
|
||||
"device": "simulator",
|
||||
"app": "ios.release"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,7 +111,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 2055
|
||||
versionCode 2057
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
@@ -220,24 +220,31 @@ dependencies {
|
||||
}
|
||||
|
||||
|
||||
|
||||
task deleteFiles(type: Delete) {
|
||||
delete './src/main/assets/static'
|
||||
}
|
||||
|
||||
task copyFiles(type: Copy) {
|
||||
task copyEditorBundle(type: Copy) {
|
||||
from '../../../../../packages/editor-mobile/build.bundle'
|
||||
into './src/main/assets'
|
||||
}
|
||||
copyFiles.dependsOn(deleteFiles)
|
||||
preBuild.dependsOn(copyFiles)
|
||||
|
||||
task copyPlainEditorBundle(type: Copy) {
|
||||
from '../../ios/extension.bundle'
|
||||
into './src/main/assets'
|
||||
}
|
||||
|
||||
task copyFontFiles(type: Copy) {
|
||||
from '../../fonts'
|
||||
into './src/main/assets/fonts'
|
||||
}
|
||||
|
||||
|
||||
project.ext.vectoricons = [
|
||||
iconFontNames: ['MaterialCommunityIcons.ttf' ],
|
||||
iconFontsDir: "../../../node_modules/react-native-vector-icons/Fonts"
|
||||
]
|
||||
|
||||
apply from: "../../../node_modules/react-native-vector-icons/fonts.gradle"
|
||||
copyEditorBundle.dependsOn(deleteFiles)
|
||||
copyFontFiles.dependsOn(copyEditorBundle);
|
||||
copyPlainEditorBundle.dependsOn(copyFontFiles);
|
||||
preBuild.dependsOn(copyPlainEditorBundle)
|
||||
|
||||
apply from: file("../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
||||
|
||||
|
||||
@@ -14,17 +14,11 @@ import androidx.test.rule.ActivityTestRule;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
public class DetoxTest {
|
||||
// Replace 'MainActivity' with the value of android:name entry in
|
||||
// <activity> in AndroidManifest.xml
|
||||
@Rule
|
||||
@Rule // (2)
|
||||
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);
|
||||
|
||||
@Test
|
||||
public void runDetoxTests() {
|
||||
// This is optional - in case you've decided to integrate TestButler
|
||||
// See https://github.com/wix/Detox/blob/master/docs/Introduction.Android.md#8-test-butler-support-optional
|
||||
//TestButlerProbe.assertReadyIfInstalled();
|
||||
|
||||
DetoxConfig detoxConfig = new DetoxConfig();
|
||||
detoxConfig.idlePolicyConfig.masterTimeoutSec = 90;
|
||||
detoxConfig.idlePolicyConfig.idleResourceTimeoutSec = 60;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user