mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-29 18:19:38 +02:00
Compare commits
1 Commits
fix/system
...
fix-codebl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74a0a117e6 |
1
.github/workflows/desktop.publish.yml
vendored
1
.github/workflows/desktop.publish.yml
vendored
@@ -97,7 +97,6 @@ jobs:
|
||||
- name: Get App Store Version
|
||||
id: appstore
|
||||
uses: streetwriters/appstore-connect-app-version@develop
|
||||
if: inputs.publish-apple
|
||||
with:
|
||||
app-id: ${{ steps.app_metadata.outputs.apple_app_id }}
|
||||
key-id: ${{ secrets.api_key_id }}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
owner: streetwriters
|
||||
repo: notesnook
|
||||
provider: github
|
||||
30
apps/desktop/package-lock.json
generated
30
apps/desktop/package-lock.json
generated
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.6.7",
|
||||
"version": "2.6.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/desktop",
|
||||
"version": "2.6.7",
|
||||
"version": "2.6.3",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@notesnook/crypto": "file:../../packages/crypto",
|
||||
"@trpc/client": "10.38.3",
|
||||
"@trpc/server": "10.38.3",
|
||||
"@trpc/client": "10.31.0",
|
||||
"@trpc/server": "10.31.0",
|
||||
"electron-trpc": "0.5.2",
|
||||
"electron-updater": "6.1.4",
|
||||
"icojs": "^0.17.1",
|
||||
@@ -24,7 +24,7 @@
|
||||
"@types/node": "18.16.1",
|
||||
"@types/yargs": "^17.0.24",
|
||||
"chokidar": "^3.5.3",
|
||||
"electron": "25.8.1",
|
||||
"electron": "25.8.0",
|
||||
"electron-builder": "^24.6.4",
|
||||
"electron-builder-notarize": "^1.5.1",
|
||||
"esbuild": "^0.17.19",
|
||||
@@ -831,20 +831,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@trpc/client": {
|
||||
"version": "10.38.3",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/client/-/client-10.38.3.tgz",
|
||||
"integrity": "sha512-hHPsElTL4sB+UGzuAZ4iwRVHjELrk/Nsl2owsYw9ITJz3EY7VWRsQ6vK48kJvlHPYPhP+efID5UOgyZMs4bFXA==",
|
||||
"version": "10.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/client/-/client-10.31.0.tgz",
|
||||
"integrity": "sha512-VCqbJEvFJb8C4hQFw7AD+dkQTjgEdV/QAzO4D+/cX5e93u5NpfNXI+PKS0QFXwG/zqgwQwVV6OkYc/D/MFwA6g==",
|
||||
"funding": [
|
||||
"https://trpc.io/sponsor"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"@trpc/server": "10.38.3"
|
||||
"@trpc/server": "10.31.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@trpc/server": {
|
||||
"version": "10.38.3",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.38.3.tgz",
|
||||
"integrity": "sha512-9s8/kwo2IDB5hwB2SKZZrfevRhdb1f9fdXtIYd3lbQuf2jQaC/LyQuHaIQjDQoUx9updBfsHXcFFPiCP1DL6pg==",
|
||||
"version": "10.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.31.0.tgz",
|
||||
"integrity": "sha512-9EnRTSDE9nF11LZsvSOqNKqkRYzHqFX4ch5AJ6VIu8uta2vxVTN4FxxsNRSOluTzVYZDeaCISbwmOJ5iihCCIg==",
|
||||
"funding": [
|
||||
"https://trpc.io/sponsor"
|
||||
]
|
||||
@@ -1751,9 +1751,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron": {
|
||||
"version": "25.8.1",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-25.8.1.tgz",
|
||||
"integrity": "sha512-GtcP1nMrROZfFg0+mhyj1hamrHvukfF6of2B/pcWxmWkd5FVY1NJib0tlhiorFZRzQN5Z+APLPr7aMolt7i2AQ==",
|
||||
"version": "25.8.0",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-25.8.0.tgz",
|
||||
"integrity": "sha512-T3kC1a/3ntSaYMCVVfUUc9v7myPzi6J2GP0Ad/CyfWKDPp054dGyKxb2EEjKnxQQ7wfjsT1JTEdBG04x6ekVBw==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@electron/get": "^2.0.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@notesnook/desktop",
|
||||
"productName": "Notesnook",
|
||||
"description": "Your private note taking space",
|
||||
"version": "2.6.7",
|
||||
"version": "2.6.3",
|
||||
"appAppleId": "1544027013",
|
||||
"private": true,
|
||||
"main": "./dist/index.js",
|
||||
@@ -12,8 +12,8 @@
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@notesnook/crypto": "file:../../packages/crypto",
|
||||
"@trpc/client": "10.38.3",
|
||||
"@trpc/server": "10.38.3",
|
||||
"@trpc/client": "10.31.0",
|
||||
"@trpc/server": "10.31.0",
|
||||
"electron-trpc": "0.5.2",
|
||||
"electron-updater": "6.1.4",
|
||||
"icojs": "^0.17.1",
|
||||
@@ -25,7 +25,7 @@
|
||||
"@types/node": "18.16.1",
|
||||
"@types/yargs": "^17.0.24",
|
||||
"chokidar": "^3.5.3",
|
||||
"electron": "25.8.1",
|
||||
"electron": "25.8.0",
|
||||
"electron-builder": "^24.6.4",
|
||||
"electron-builder-notarize": "^1.5.1",
|
||||
"esbuild": "^0.17.19",
|
||||
@@ -191,7 +191,6 @@
|
||||
"allowNativeWayland": true
|
||||
},
|
||||
"extraResources": [
|
||||
"app-update.yml",
|
||||
"./assets/**"
|
||||
],
|
||||
"extraMetadata": {
|
||||
|
||||
@@ -117,14 +117,6 @@ export const osIntegrationRouter = t.router({
|
||||
writeFileSync(resolvedPath, data);
|
||||
}),
|
||||
|
||||
resolvePath: t.procedure
|
||||
.input(z.object({ filePath: z.string() }))
|
||||
.query(({ input }) => {
|
||||
const { filePath } = input;
|
||||
if (!filePath) return;
|
||||
return resolvePath(filePath);
|
||||
}),
|
||||
|
||||
showNotification: t.procedure
|
||||
.input(NotificationOptions)
|
||||
.query(({ input }) => {
|
||||
|
||||
@@ -27,23 +27,19 @@ export type CLIOptions = {
|
||||
hidden: boolean;
|
||||
};
|
||||
|
||||
export async function parseArguments(argv: string[]): Promise<CLIOptions> {
|
||||
export async function parseArguments(): Promise<CLIOptions> {
|
||||
const result: CLIOptions = {
|
||||
note: false,
|
||||
notebook: false,
|
||||
reminder: false,
|
||||
hidden: false
|
||||
};
|
||||
const { hidden } = await yargs(hideBin(argv))
|
||||
const { hidden } = await yargs(hideBin(process.argv))
|
||||
.boolean("hidden")
|
||||
// have to account for this flag added on Windows when launching
|
||||
// via Jumplist
|
||||
.boolean("allow-file-access-from-files")
|
||||
.command("new", "Create a new item", (yargs) => {
|
||||
return yargs
|
||||
.command("note", "Create a new note", {}, () => {
|
||||
result.note = true;
|
||||
console.log("HERE!");
|
||||
})
|
||||
.command("notebook", "Create a new notebook", {}, () => {
|
||||
result.notebook = true;
|
||||
|
||||
@@ -34,7 +34,6 @@ import { router, api } from "./api";
|
||||
import { config } from "./utils/config";
|
||||
import path from "path";
|
||||
import { bringToFront } from "./utils/bring-to-front";
|
||||
import { bridge } from "./api/bridge";
|
||||
|
||||
// only run a single instance
|
||||
if (!MAC_APP_STORE && !app.requestSingleInstanceLock()) {
|
||||
@@ -64,7 +63,7 @@ process.on("unhandledRejection", (reason) => {
|
||||
app.commandLine.appendSwitch("lang", "en-US");
|
||||
|
||||
async function createWindow() {
|
||||
const cliOptions = await parseArguments(process.argv);
|
||||
const cliOptions = await parseArguments();
|
||||
setTheme(getTheme());
|
||||
|
||||
const mainWindowState = new WindowState({});
|
||||
@@ -91,7 +90,6 @@ async function createWindow() {
|
||||
}
|
||||
});
|
||||
|
||||
createIPCHandler({ router, windows: [mainWindow] });
|
||||
globalThis.window = mainWindow;
|
||||
mainWindowState.manage(mainWindow);
|
||||
|
||||
@@ -107,6 +105,7 @@ async function createWindow() {
|
||||
|
||||
await AssetManager.loadIcons();
|
||||
setupDesktopIntegration();
|
||||
createIPCHandler({ router, windows: [mainWindow] });
|
||||
|
||||
mainWindow.webContents.session.setSpellCheckerDictionaryDownloadURL(
|
||||
"http://dictionaries.notesnook.com/"
|
||||
@@ -147,12 +146,8 @@ app.once("window-all-closed", () => {
|
||||
}
|
||||
});
|
||||
|
||||
app.on("second-instance", async (_ev, argv) => {
|
||||
app.on("second-instance", () => {
|
||||
if (!globalThis.window) return;
|
||||
const cliOptions = await parseArguments(argv);
|
||||
if (cliOptions.note) bridge.onCreateItem("note");
|
||||
if (cliOptions.notebook) bridge.onCreateItem("notebook");
|
||||
if (cliOptions.reminder) bridge.onCreateItem("reminder");
|
||||
bringToFront();
|
||||
});
|
||||
|
||||
@@ -193,7 +188,7 @@ function setupDesktopIntegration() {
|
||||
app.on("before-quit", () => app.exit(0));
|
||||
}
|
||||
|
||||
globalThis.window?.on("close", (e) => {
|
||||
globalThis.window?.once("close", (e) => {
|
||||
if (config.desktopSettings.closeToSystemTray) {
|
||||
e.preventDefault();
|
||||
if (process.platform == "darwin") {
|
||||
|
||||
@@ -31,6 +31,7 @@ declare global {
|
||||
}
|
||||
|
||||
process.once("loaded", async () => {
|
||||
console.log("HELLO!");
|
||||
const electronTRPC: RendererGlobalElectronTRPC = {
|
||||
sendMessage: (operation) =>
|
||||
ipcRenderer.send(ELECTRON_TRPC_CHANNEL, operation),
|
||||
|
||||
@@ -34,7 +34,6 @@ import { useAttachmentStore } from "../../stores/use-attachment-store";
|
||||
import { db } from "../database";
|
||||
import Storage from "../database/storage";
|
||||
import { cacheDir, copyFileAsync, releasePermissions } from "./utils";
|
||||
import { createCacheDir } from "./io";
|
||||
|
||||
export const FileDownloadStatus = {
|
||||
Success: 1,
|
||||
@@ -63,7 +62,6 @@ export async function downloadAttachments(
|
||||
canceled,
|
||||
groupId
|
||||
) {
|
||||
await createCacheDir();
|
||||
if (!attachments || !attachments.length) return;
|
||||
const result = new Map();
|
||||
|
||||
@@ -184,8 +182,6 @@ export default async function downloadAttachment(
|
||||
groupId: undefined
|
||||
}
|
||||
) {
|
||||
await createCacheDir();
|
||||
|
||||
let attachment = db.attachments.attachment(hash);
|
||||
if (!attachment) {
|
||||
console.log("attachment not found");
|
||||
|
||||
@@ -24,18 +24,15 @@ import { ToastEvent } from "../../services/event-manager";
|
||||
import { useAttachmentStore } from "../../stores/use-attachment-store";
|
||||
import { db } from "../database";
|
||||
import { cacheDir, fileCheck } from "./utils";
|
||||
import { createCacheDir, exists } from "./io";
|
||||
|
||||
export async function downloadFile(filename, data, cancelToken) {
|
||||
if (!data) return false;
|
||||
|
||||
await createCacheDir();
|
||||
|
||||
let { url, headers } = data;
|
||||
let path = `${cacheDir}/${filename}`;
|
||||
|
||||
let path = `${cacheDir}/${filename}`;
|
||||
try {
|
||||
if (await exists(filename)) {
|
||||
let exists = await RNFetchBlob.fs.exists(path);
|
||||
if (exists) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -63,34 +60,29 @@ export async function downloadFile(filename, data, cancelToken) {
|
||||
console.log("downloading: ", recieved, total);
|
||||
});
|
||||
|
||||
cancelToken.cancel = () => {
|
||||
useAttachmentStore.getState().remove(filename);
|
||||
request.cancel();
|
||||
};
|
||||
cancelToken.cancel = request.cancel;
|
||||
let response = await request;
|
||||
await fileCheck(response, totalSize);
|
||||
let status = response.info().status;
|
||||
useAttachmentStore.getState().remove(filename);
|
||||
return status >= 200 && status < 300;
|
||||
} catch (e) {
|
||||
if (e.message !== "canceled") {
|
||||
ToastEvent.show({
|
||||
heading: "Error downloading file",
|
||||
message: e.message,
|
||||
type: "error",
|
||||
context: "global"
|
||||
});
|
||||
ToastEvent.show({
|
||||
heading: "Error downloading file",
|
||||
message: e.message,
|
||||
type: "error",
|
||||
context: "local"
|
||||
});
|
||||
}
|
||||
ToastEvent.show({
|
||||
heading: "Error downloading file",
|
||||
message: e.message,
|
||||
type: "error",
|
||||
context: "global"
|
||||
});
|
||||
ToastEvent.show({
|
||||
heading: "Error downloading file",
|
||||
message: e.message,
|
||||
type: "error",
|
||||
context: "local"
|
||||
});
|
||||
|
||||
useAttachmentStore.getState().remove(filename);
|
||||
RNFetchBlob.fs.unlink(path).catch(console.log);
|
||||
console.log("Download file error:", e, url, headers);
|
||||
console.log("download file error: ", e, url, headers);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,25 +20,32 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { Platform } from "react-native";
|
||||
import Sodium from "@ammarahmed/react-native-sodium";
|
||||
import RNFetchBlob from "react-native-blob-util";
|
||||
import { cacheDir, cacheDirOld, getRandomId } from "./utils";
|
||||
import { cacheDir, getRandomId } from "./utils";
|
||||
import { db } from "../database";
|
||||
import { compressToBase64 } from "./compress";
|
||||
import { IOS_APPGROUPID } from "../../utils/constants";
|
||||
|
||||
export async function readEncrypted(filename, key, cipherData) {
|
||||
await migrateFilesFromCache();
|
||||
console.log("Read encrypted file...");
|
||||
let path = `${cacheDir}/${filename}`;
|
||||
|
||||
try {
|
||||
if (!(await exists(filename))) {
|
||||
const iosAppGroup =
|
||||
Platform.OS === "ios"
|
||||
? await RNFetchBlob.fs.pathForAppGroup(IOS_APPGROUPID)
|
||||
: null;
|
||||
|
||||
const appGroupPath = `${iosAppGroup}/${filename}`;
|
||||
let exists =
|
||||
(await RNFetchBlob.fs.exists(path)) ||
|
||||
(Platform.OS === "ios" && (await RNFetchBlob.fs.exists(appGroupPath)));
|
||||
|
||||
if (!exists) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const attachment = db.attachments.attachment(filename);
|
||||
const isPng = /(png)/g.test(attachment?.metadata.type);
|
||||
const isJpeg = /(jpeg|jpg)/g.test(attachment?.metadata.type);
|
||||
console.log("decrypting....");
|
||||
|
||||
let output = await Sodium.decryptFile(
|
||||
key,
|
||||
{
|
||||
@@ -52,7 +59,6 @@ export async function readEncrypted(filename, key, cipherData) {
|
||||
: "base64"
|
||||
: "text"
|
||||
);
|
||||
console.log("file decrypted...");
|
||||
if (cipherData.outputType === "base64" && (isPng || isJpeg)) {
|
||||
const dCachePath = `${cacheDir}/${output}`;
|
||||
output = await compressToBase64(
|
||||
@@ -64,7 +70,7 @@ export async function readEncrypted(filename, key, cipherData) {
|
||||
return output;
|
||||
} catch (e) {
|
||||
RNFetchBlob.fs.unlink(path).catch(console.log);
|
||||
console.log("readEncrypted", e);
|
||||
console.log(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -82,7 +88,6 @@ export async function hashBase64(data) {
|
||||
}
|
||||
|
||||
export async function writeEncryptedBase64({ data, key }) {
|
||||
await createCacheDir();
|
||||
let filepath = cacheDir + `/${getRandomId("imagecache_")}`;
|
||||
await RNFetchBlob.fs.writeFile(filepath, data, "base64");
|
||||
let output = await Sodium.encryptFile(key, {
|
||||
@@ -98,7 +103,6 @@ export async function writeEncryptedBase64({ data, key }) {
|
||||
}
|
||||
|
||||
export async function deleteFile(filename, data) {
|
||||
await createCacheDir();
|
||||
let delFilePath = cacheDir + `/${filename}`;
|
||||
if (!data) {
|
||||
if (!filename) return;
|
||||
@@ -124,89 +128,19 @@ export async function deleteFile(filename, data) {
|
||||
export async function clearFileStorage() {
|
||||
try {
|
||||
let files = await RNFetchBlob.fs.ls(cacheDir);
|
||||
let oldCache = await RNFetchBlob.fs.ls(cacheDirOld);
|
||||
|
||||
for (let file of files) {
|
||||
await RNFetchBlob.fs.unlink(cacheDir + `/${file}`).catch(console.log);
|
||||
}
|
||||
for (let file of oldCache) {
|
||||
await RNFetchBlob.fs.unlink(cacheDirOld + `/${file}`).catch(console.log);
|
||||
try {
|
||||
await RNFetchBlob.fs.unlink(cacheDir + `/${file}`);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("clearFileStorage", e);
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function createCacheDir() {
|
||||
if (!(await RNFetchBlob.fs.exists(cacheDir))) {
|
||||
await RNFetchBlob.fs.mkdir(cacheDir);
|
||||
console.log("Cache directory created");
|
||||
}
|
||||
}
|
||||
|
||||
export async function migrateFilesFromCache() {
|
||||
try {
|
||||
await createCacheDir();
|
||||
const migratedFilesPath = cacheDir + "/.migrated_1";
|
||||
const migrated = await RNFetchBlob.fs.exists(migratedFilesPath);
|
||||
if (migrated) {
|
||||
console.log("Files migrated already");
|
||||
return;
|
||||
}
|
||||
|
||||
let files = await RNFetchBlob.fs.ls(cacheDir);
|
||||
console.log("Files to migrate:", files.join(","));
|
||||
|
||||
let oldCache = await RNFetchBlob.fs.ls(cacheDirOld);
|
||||
for (let file of oldCache) {
|
||||
if (file.startsWith("org.") || file.startsWith("com.")) continue;
|
||||
RNFetchBlob.fs
|
||||
.mv(cacheDirOld + `/${file}`, cacheDir + `/${file}`)
|
||||
.catch(console.log);
|
||||
console.log("Moved", file);
|
||||
}
|
||||
await RNFetchBlob.fs.createFile(migratedFilesPath, "1", "utf8");
|
||||
} catch (e) {
|
||||
console.log("migrateFilesFromCache", e);
|
||||
}
|
||||
}
|
||||
|
||||
const ABYTES = 17;
|
||||
export async function exists(filename) {
|
||||
let path = `${cacheDir}/${filename}`;
|
||||
|
||||
const iosAppGroup =
|
||||
Platform.OS === "ios"
|
||||
? await RNFetchBlob.fs.pathForAppGroup(IOS_APPGROUPID)
|
||||
: null;
|
||||
const appGroupPath = `${iosAppGroup}/${filename}`;
|
||||
|
||||
let exists = await RNFetchBlob.fs.exists(path);
|
||||
|
||||
// Check if file is present in app group path.
|
||||
let existsInAppGroup = false;
|
||||
if (!exists && Platform.OS === "ios") {
|
||||
existsInAppGroup = await RNFetchBlob.fs.exists(appGroupPath);
|
||||
}
|
||||
|
||||
if (exists || existsInAppGroup) {
|
||||
const attachment = db.attachments.attachment(filename);
|
||||
const totalChunks = Math.ceil(attachment.length / attachment.chunkSize);
|
||||
const totalAbytes = totalChunks * ABYTES;
|
||||
const expectedFileSize = attachment.length + totalAbytes;
|
||||
|
||||
const stat = await RNFetchBlob.fs.stat(
|
||||
existsInAppGroup ? appGroupPath : path
|
||||
);
|
||||
|
||||
if (stat.size !== expectedFileSize) {
|
||||
RNFetchBlob.fs
|
||||
.unlink(existsInAppGroup ? appGroupPath : path)
|
||||
.catch(console.log);
|
||||
return false;
|
||||
}
|
||||
|
||||
exists = true;
|
||||
}
|
||||
let exists = await RNFetchBlob.fs.exists(`${cacheDir}/${filename}`);
|
||||
return exists;
|
||||
}
|
||||
|
||||
@@ -24,12 +24,11 @@ import { cacheDir } from "./utils";
|
||||
import { isImage, isDocument } from "@notesnook/core/dist/utils/filename";
|
||||
import { Platform } from "react-native";
|
||||
import { IOS_APPGROUPID } from "../../utils/constants";
|
||||
import { createCacheDir } from "./io";
|
||||
|
||||
export async function uploadFile(filename, data, cancelToken) {
|
||||
if (!data) return false;
|
||||
let { url, headers } = data;
|
||||
await createCacheDir();
|
||||
|
||||
DatabaseLogger.info(`Preparing to upload file: ${filename}`);
|
||||
|
||||
try {
|
||||
@@ -73,10 +72,7 @@ export async function uploadFile(filename, data, cancelToken) {
|
||||
);
|
||||
});
|
||||
|
||||
cancelToken.cancel = () => {
|
||||
useAttachmentStore.getState().remove(filename);
|
||||
request.cancel();
|
||||
};
|
||||
cancelToken.cancel = request.cancel;
|
||||
let response = await request;
|
||||
|
||||
let status = response.info().status;
|
||||
|
||||
@@ -21,12 +21,7 @@ import * as ScopedStorage from "react-native-scoped-storage";
|
||||
import { Platform } from "react-native";
|
||||
import RNFetchBlob from "react-native-blob-util";
|
||||
|
||||
export const cacheDirOld = RNFetchBlob.fs.dirs.CacheDir;
|
||||
|
||||
export const cacheDir =
|
||||
Platform.OS == "ios"
|
||||
? RNFetchBlob.fs.dirs.LibraryDir + "/.cache"
|
||||
: RNFetchBlob.fs.dirs.DocumentDir + "/.cache";
|
||||
export const cacheDir = RNFetchBlob.fs.dirs.CacheDir;
|
||||
|
||||
export function getRandomId(prefix) {
|
||||
return Math.random()
|
||||
@@ -71,12 +66,9 @@ export function cancelable(operation) {
|
||||
}
|
||||
|
||||
export function copyFileAsync(source, dest) {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve) => {
|
||||
ScopedStorage.copyFile(source, dest, (e, r) => {
|
||||
if (e) {
|
||||
reject(e);
|
||||
return;
|
||||
}
|
||||
console.log(e, r);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -132,7 +132,7 @@ export const AttachmentItem = ({
|
||||
activeOpacity={0.9}
|
||||
onPress={() => {
|
||||
if (encryption || !pressable) return;
|
||||
db.fs.cancel(attachment.metadata.hash, currentProgress.type);
|
||||
db.fs.cancel(attachment.metadata.hash);
|
||||
setCurrentProgress(null);
|
||||
}}
|
||||
style={{
|
||||
|
||||
@@ -85,7 +85,6 @@ const AuthModal = () => {
|
||||
background={colors.primary.background}
|
||||
transparent={false}
|
||||
animated={false}
|
||||
centered={false}
|
||||
>
|
||||
<KeyboardAwareScrollView
|
||||
style={{
|
||||
|
||||
@@ -46,7 +46,6 @@ import Input from "../ui/input";
|
||||
import Heading from "../ui/typography/heading";
|
||||
import Paragraph from "../ui/typography/paragraph";
|
||||
import { LoginSteps, useLogin } from "./use-login";
|
||||
import BaseDialog from "../dialog/base-dialog";
|
||||
|
||||
function getObfuscatedEmail(email) {
|
||||
if (!email) return "";
|
||||
@@ -131,12 +130,7 @@ export const SessionExpired = () => {
|
||||
|
||||
return (
|
||||
visible && (
|
||||
<BaseDialog
|
||||
transparent={false}
|
||||
background={colors.primary.background}
|
||||
bounce={false}
|
||||
animated={false}
|
||||
centered={false}
|
||||
<Modal
|
||||
onShow={async () => {
|
||||
await sleep(300);
|
||||
passwordInputRef.current?.focus();
|
||||
@@ -237,7 +231,7 @@ export const SessionExpired = () => {
|
||||
</View>
|
||||
<Toast context="local" />
|
||||
<Dialog context="session_expiry" />
|
||||
</BaseDialog>
|
||||
</Modal>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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 React, { useEffect, useRef, useState } from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import {
|
||||
KeyboardAvoidingView,
|
||||
Modal,
|
||||
@@ -31,9 +31,6 @@ import useIsFloatingKeyboard from "../../hooks/use-is-floating-keyboard";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import { BouncingView } from "../ui/transitions/bouncing-view";
|
||||
import { ScopedThemeProvider } from "@notesnook/theme";
|
||||
import SettingsService from "../../services/settings";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { useAppState } from "../../hooks/use-app-state";
|
||||
|
||||
const BaseDialog = ({
|
||||
visible,
|
||||
@@ -54,9 +51,6 @@ const BaseDialog = ({
|
||||
avoidKeyboardResize = false
|
||||
}) => {
|
||||
const floating = useIsFloatingKeyboard();
|
||||
const appState = useAppState();
|
||||
const lockEvents = useRef(false);
|
||||
const [internalVisible, setIntervalVisible] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
@@ -64,33 +58,14 @@ const BaseDialog = ({
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (SettingsService.get().appLockMode === "background") {
|
||||
if (appState === "background") {
|
||||
setIntervalVisible(false);
|
||||
if (useUserStore.getState().appLocked) {
|
||||
lockEvents.current = true;
|
||||
const unsub = useUserStore.subscribe((state) => {
|
||||
if (!state.appLocked) {
|
||||
setIntervalVisible(true);
|
||||
unsub();
|
||||
setTimeout(() => {
|
||||
lockEvents.current = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [appState]);
|
||||
|
||||
const Wrapper = useSafeArea ? SafeAreaView : View;
|
||||
|
||||
return (
|
||||
<ScopedThemeProvider value="dialog">
|
||||
<Modal
|
||||
visible={visible && internalVisible}
|
||||
visible={visible}
|
||||
transparent={true}
|
||||
animated
|
||||
statusBarTranslucent={statusBarTranslucent}
|
||||
supportedOrientations={[
|
||||
"portrait",
|
||||
@@ -100,7 +75,6 @@ const BaseDialog = ({
|
||||
"landscape-right"
|
||||
]}
|
||||
onShow={() => {
|
||||
if (lockEvents.current) return;
|
||||
if (onShow) {
|
||||
onShow();
|
||||
useSettingStore.getState().setSheetKeyboardHandler(false);
|
||||
@@ -108,7 +82,6 @@ const BaseDialog = ({
|
||||
}}
|
||||
animationType={animation}
|
||||
onRequestClose={() => {
|
||||
if (lockEvents.current) return;
|
||||
if (!closeOnTouch) return null;
|
||||
useSettingStore.getState().setSheetKeyboardHandler(true);
|
||||
onRequestClose && onRequestClose();
|
||||
@@ -134,7 +107,6 @@ const BaseDialog = ({
|
||||
style={[
|
||||
styles.backdrop,
|
||||
{
|
||||
alignItems: centered ? "center" : undefined,
|
||||
justifyContent: centered
|
||||
? "center"
|
||||
: bottom
|
||||
@@ -161,7 +133,8 @@ const styles = StyleSheet.create({
|
||||
backdrop: {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
justifyContent: "center"
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
},
|
||||
overlayButton: {
|
||||
width: "100%",
|
||||
|
||||
@@ -48,7 +48,7 @@ export const ProFeatures = ({ count = 6 }) => {
|
||||
content: "Instantly sync to unlimited devices"
|
||||
},
|
||||
{
|
||||
content: "A private vault to keep everything important always locked"
|
||||
content: "A private vault to keep everything imporant always locked"
|
||||
},
|
||||
{
|
||||
content:
|
||||
|
||||
@@ -442,7 +442,6 @@ export class VaultDialog extends Component {
|
||||
if (this.state.biometricUnlock && !this.state.isBiometryEnrolled) {
|
||||
await this._enrollFingerprint(this.password);
|
||||
}
|
||||
|
||||
if (this.state.goToEditor) {
|
||||
this._openInEditor(note);
|
||||
} else if (this.state.share) {
|
||||
@@ -453,6 +452,7 @@ export class VaultDialog extends Component {
|
||||
await this._copyNote(note);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
this._takeErrorAction(e);
|
||||
}
|
||||
};
|
||||
@@ -467,40 +467,44 @@ export class VaultDialog extends Component {
|
||||
}
|
||||
|
||||
async _enrollFingerprint(password) {
|
||||
this.setState(
|
||||
{
|
||||
loading: true
|
||||
},
|
||||
async () => {
|
||||
try {
|
||||
await db.vault.unlock(password);
|
||||
await BiometricService.storeCredentials(password);
|
||||
this.setState({
|
||||
loading: false
|
||||
});
|
||||
eSendEvent("vaultUpdated");
|
||||
ToastEvent.show({
|
||||
heading: "Biometric unlocking enabled!",
|
||||
message: "Now you can unlock notes in vault with biometrics.",
|
||||
type: "success",
|
||||
context: "global"
|
||||
});
|
||||
this.close();
|
||||
} catch (e) {
|
||||
this.close();
|
||||
ToastEvent.show({
|
||||
heading: "Incorrect password",
|
||||
message:
|
||||
"Please enter the correct vault password to enable biometrics.",
|
||||
type: "error",
|
||||
context: "local"
|
||||
});
|
||||
this.setState({
|
||||
loading: false
|
||||
});
|
||||
try {
|
||||
this.setState(
|
||||
{
|
||||
loading: true
|
||||
},
|
||||
async () => {
|
||||
try {
|
||||
await db.vault.unlock(password);
|
||||
await BiometricService.storeCredentials(password);
|
||||
this.setState({
|
||||
loading: false
|
||||
});
|
||||
eSendEvent("vaultUpdated");
|
||||
ToastEvent.show({
|
||||
heading: "Biometric unlocking enabled!",
|
||||
message: "Now you can unlock notes in vault with biometrics.",
|
||||
type: "success",
|
||||
context: "global"
|
||||
});
|
||||
this.close();
|
||||
} catch (e) {
|
||||
ToastEvent.show({
|
||||
heading: "Incorrect password",
|
||||
message:
|
||||
"Please enter the correct vault password to enable biometrics.",
|
||||
type: "error",
|
||||
context: "local"
|
||||
});
|
||||
this.setState({
|
||||
loading: false
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
} catch (e) {
|
||||
this._takeErrorAction(e);
|
||||
}
|
||||
}
|
||||
|
||||
async _createVault() {
|
||||
@@ -539,7 +543,7 @@ export class VaultDialog extends Component {
|
||||
.remove(this.state.note.id, this.password)
|
||||
.then(() => {
|
||||
ToastEvent.show({
|
||||
heading: "Note permanently unlocked.",
|
||||
heading: "Note permanantly unlocked.",
|
||||
type: "success",
|
||||
context: "global"
|
||||
});
|
||||
|
||||
@@ -39,9 +39,9 @@ class ExceptionHandler extends React.Component<{
|
||||
error: Error | null;
|
||||
hasError: boolean;
|
||||
} = {
|
||||
hasError: false,
|
||||
error: null
|
||||
};
|
||||
hasError: false,
|
||||
error: null
|
||||
};
|
||||
static getDerivedStateFromError(error: Error) {
|
||||
return { hasError: true, error: error };
|
||||
}
|
||||
@@ -69,7 +69,7 @@ class ExceptionHandler extends React.Component<{
|
||||
this.props.component
|
||||
)}
|
||||
defaultTitle={this.state.error?.message}
|
||||
issueTitle="An exception occurred"
|
||||
issueTitle="An exception occured"
|
||||
/>
|
||||
<Dialog />
|
||||
</SafeAreaView>
|
||||
|
||||
@@ -17,25 +17,35 @@ 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, useRef, useState } from "react";
|
||||
import { TouchableOpacity, useWindowDimensions, View } from "react-native";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useRef } from "react";
|
||||
import { TouchableOpacity, View, useWindowDimensions } from "react-native";
|
||||
import { eSendEvent, presentSheet } from "../../../services/event-manager";
|
||||
import SettingsService from "../../../services/settings";
|
||||
import { useSettingStore } from "../../../stores/use-setting-store";
|
||||
import { ColorValues } from "../../../utils/colors";
|
||||
import {
|
||||
eSendEvent,
|
||||
eSubscribeEvent,
|
||||
eUnSubscribeEvent,
|
||||
presentSheet
|
||||
} from "../../../services/event-manager";
|
||||
import SettingsService from "../../../services/settings";
|
||||
import { GROUP } from "../../../utils/constants";
|
||||
import { ColorValues } from "../../../utils/colors";
|
||||
import { db } from "../../../common/database";
|
||||
import { eOpenJumpToDialog } from "../../../utils/events";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import Sort from "../../sheets/sort";
|
||||
import { Button } from "../../ui/button";
|
||||
import { IconButton } from "../../ui/icon-button";
|
||||
import { Button } from "../../ui/button";
|
||||
import Sort from "../../sheets/sort";
|
||||
import Heading from "../../ui/typography/heading";
|
||||
import { useCallback } from "react";
|
||||
|
||||
export const SectionHeader = React.memo(
|
||||
function SectionHeader({ item, index, type, color, screen, groupOptions }) {
|
||||
function SectionHeader({ item, index, type, color, screen }) {
|
||||
const { colors } = useThemeColors();
|
||||
const { fontScale } = useWindowDimensions();
|
||||
const [groupOptions, setGroupOptions] = useState(
|
||||
db.settings?.getGroupOptions(type)
|
||||
);
|
||||
let groupBy = Object.keys(GROUP).find(
|
||||
(key) => GROUP[key] === groupOptions.groupBy
|
||||
);
|
||||
@@ -55,6 +65,17 @@ export const SectionHeader = React.memo(
|
||||
? "Default"
|
||||
: groupBy.slice(0, 1).toUpperCase() + groupBy.slice(1, groupBy.length);
|
||||
|
||||
const onUpdate = useCallback(() => {
|
||||
setGroupOptions({ ...db.settings?.getGroupOptions(type) });
|
||||
}, [type]);
|
||||
|
||||
useEffect(() => {
|
||||
eSubscribeEvent("groupOptionsUpdate", onUpdate);
|
||||
return () => {
|
||||
eUnSubscribeEvent("groupOptionsUpdate", onUpdate);
|
||||
};
|
||||
}, [onUpdate]);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
|
||||
@@ -77,7 +77,7 @@ export const openNotebookTopic = (item) => {
|
||||
useTrashStore.getState().setTrash();
|
||||
useSelectionStore.getState().setSelectionMode(false);
|
||||
ToastEvent.show({
|
||||
heading: "Permanently deleted items",
|
||||
heading: "Permanantly deleted items",
|
||||
type: "success",
|
||||
context: "local"
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { Dimensions, View } from "react-native";
|
||||
import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { useMessageStore } from "../../stores/use-message-store";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
@@ -32,7 +32,6 @@ export const Card = ({ color, warning }) => {
|
||||
color = color ? color : colors.primary.accent;
|
||||
const messageBoardState = useMessageStore((state) => state.message);
|
||||
const announcement = useMessageStore((state) => state.announcement);
|
||||
const fontScale = Dimensions.get("window").fontScale;
|
||||
|
||||
return !messageBoardState.visible || announcement || warning ? null : (
|
||||
<View
|
||||
@@ -45,6 +44,7 @@ export const Card = ({ color, warning }) => {
|
||||
type="gray"
|
||||
customStyle={{
|
||||
paddingVertical: 12,
|
||||
width: "95%",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
@@ -54,18 +54,17 @@ export const Card = ({ color, warning }) => {
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexShrink: 1
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
width: 40 * fontScale,
|
||||
width: 40,
|
||||
backgroundColor:
|
||||
messageBoardState.type === "error"
|
||||
? hexToRGBA(colors.static.red, 0.15)
|
||||
: hexToRGBA(color, 0.15),
|
||||
height: 40 * fontScale,
|
||||
height: 40,
|
||||
borderRadius: 100,
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
@@ -76,7 +75,6 @@ export const Card = ({ color, warning }) => {
|
||||
color={
|
||||
messageBoardState.type === "error" ? colors.error.icon : color
|
||||
}
|
||||
allowFontScaling
|
||||
name={messageBoardState.icon}
|
||||
/>
|
||||
</View>
|
||||
@@ -93,7 +91,7 @@ export const Card = ({ color, warning }) => {
|
||||
</Paragraph>
|
||||
<Paragraph
|
||||
style={{
|
||||
flexWrap: "nowrap",
|
||||
flexWrap: "wrap",
|
||||
flexShrink: 1
|
||||
}}
|
||||
color={colors.primary.heading}
|
||||
@@ -103,24 +101,22 @@ export const Card = ({ color, warning }) => {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{fontScale > 1 ? null : (
|
||||
<View
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="chevron-right"
|
||||
color={
|
||||
messageBoardState.type === "error" ? colors.error.icon : color
|
||||
}
|
||||
size={SIZE.lg}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
<View
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="chevron-right"
|
||||
color={
|
||||
messageBoardState.type === "error" ? colors.error.icon : color
|
||||
}
|
||||
size={SIZE.lg}
|
||||
/>
|
||||
</View>
|
||||
</PressableButton>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -39,7 +39,6 @@ import { Empty } from "./empty";
|
||||
import { getTotalNotes } from "@notesnook/common";
|
||||
import { useSettingStore } from "../../stores/use-setting-store";
|
||||
import ReminderItem from "../list-items/reminder";
|
||||
import { useGroupOptions } from "../../hooks/use-group-options";
|
||||
|
||||
const renderItems = {
|
||||
note: NoteWrapper,
|
||||
@@ -100,10 +99,9 @@ const List = ({
|
||||
type === "notebooks" ||
|
||||
notebooksListMode === "compact";
|
||||
const groupType =
|
||||
screen === "Home" ? "home" : screen === "Favorites" ? "favorites" : type;
|
||||
|
||||
const groupOptions = useGroupOptions(groupType);
|
||||
screen === "Notes" ? "home" : screen === "Favorites" ? "favorites" : type;
|
||||
|
||||
const groupOptions = db.settings?.getGroupOptions(groupType);
|
||||
const dateBy =
|
||||
groupOptions.sortBy !== "title" ? groupOptions.sortBy : "dateEdited";
|
||||
|
||||
@@ -115,21 +113,18 @@ const List = ({
|
||||
color={headerProps?.color}
|
||||
title={headerProps?.heading}
|
||||
dateBy={dateBy}
|
||||
type={groupType}
|
||||
type={
|
||||
screen === "Notes"
|
||||
? "home"
|
||||
: screen === "Favorites"
|
||||
? "favorites"
|
||||
: type
|
||||
}
|
||||
screen={screen}
|
||||
isSheet={isSheet}
|
||||
groupOptions={groupOptions}
|
||||
/>
|
||||
),
|
||||
[
|
||||
headerProps?.color,
|
||||
headerProps?.heading,
|
||||
screen,
|
||||
isSheet,
|
||||
dateBy,
|
||||
groupType,
|
||||
groupOptions
|
||||
]
|
||||
[headerProps?.color, headerProps?.heading, screen, type, isSheet, dateBy]
|
||||
);
|
||||
|
||||
const _onRefresh = async () => {
|
||||
@@ -235,7 +230,7 @@ const List = ({
|
||||
<JumpToSectionDialog
|
||||
screen={screen}
|
||||
data={listData}
|
||||
type={screen === "Home" ? "home" : type}
|
||||
type={screen === "Notes" ? "home" : type}
|
||||
scrollRef={scrollRef}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -245,17 +245,13 @@ const MergeConflicts = () => {
|
||||
};
|
||||
|
||||
return !visible ? null : (
|
||||
<BaseDialog
|
||||
<Modal
|
||||
statusBarTranslucent
|
||||
transparent={false}
|
||||
animationType="slide"
|
||||
animated={false}
|
||||
bounce={false}
|
||||
onRequestClose={() => {
|
||||
close();
|
||||
}}
|
||||
centered={false}
|
||||
background={colors?.primary.background}
|
||||
supportedOrientations={[
|
||||
"portrait",
|
||||
"portrait-upside-down",
|
||||
@@ -367,7 +363,7 @@ const MergeConflicts = () => {
|
||||
</Animated.View>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</BaseDialog>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ export default function NotePreview({ session, content, note }) {
|
||||
useTrashStore.getState().setTrash();
|
||||
useSelectionStore.getState().setSelectionMode(false);
|
||||
ToastEvent.show({
|
||||
heading: "Permanently deleted items",
|
||||
heading: "Permanantly deleted items",
|
||||
type: "success",
|
||||
context: "local"
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ export const CompactFeatures = ({
|
||||
icon: "shield"
|
||||
},
|
||||
{
|
||||
highlight: "Daily, weekly & monthly",
|
||||
highlight: "Daily, weekly & montly",
|
||||
content: "recurring reminders",
|
||||
icon: "bell"
|
||||
},
|
||||
|
||||
@@ -98,7 +98,7 @@ export const features = [
|
||||
icon: "bell"
|
||||
},
|
||||
{
|
||||
highlight: "Daily, weekly & monthly",
|
||||
highlight: "Daily, weekly & montly",
|
||||
content: "reminders",
|
||||
icon: "refresh",
|
||||
pro: true
|
||||
@@ -194,7 +194,7 @@ export const features = [
|
||||
"Having the right tool at the right time is crucial for note taking. Lists, tables, codeblocks — you name it, we have it.",
|
||||
features: [
|
||||
{
|
||||
highlight: "Basic formatting",
|
||||
highlight: "Basic formating",
|
||||
content: "and lists",
|
||||
icon: "format-bold"
|
||||
},
|
||||
|
||||
@@ -500,7 +500,7 @@ export const PricingPlans = ({
|
||||
textAlign: "center"
|
||||
}}
|
||||
>
|
||||
By subscribing, you will be charged on your Google Account, and
|
||||
By subscribing, your will be charged on your Google Account, and
|
||||
your subscription will automatically renew until you cancel prior
|
||||
to the end of the then current period.
|
||||
</Paragraph>
|
||||
|
||||
@@ -16,8 +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 React, { useRef } from "react";
|
||||
import { Dimensions, Platform, View, useWindowDimensions } from "react-native";
|
||||
import React from "react";
|
||||
import { Platform, View } from "react-native";
|
||||
import { FlatList } from "react-native-actions-sheet";
|
||||
import { db } from "../../common/database";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
@@ -54,6 +54,7 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const alias = item.alias || item.title;
|
||||
const isColor = !!ColorValues[item.title];
|
||||
|
||||
if (!item || !item.id) {
|
||||
return (
|
||||
<Paragraph style={{ marginVertical: 10, alignSelf: "center" }}>
|
||||
@@ -61,7 +62,6 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||
</Paragraph>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
keyboardShouldPersistTaps="always"
|
||||
@@ -135,7 +135,6 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
|
||||
}, 1000);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Synced item={item} close={close} />
|
||||
|
||||
{DDS.isTab ? (
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { Dimensions, FlatList, ScrollView, View } from "react-native";
|
||||
import { FlatList, ScrollView, View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { useActions } from "../../hooks/use-actions";
|
||||
import { DDS } from "../../services/device-detection";
|
||||
@@ -33,11 +33,9 @@ export const Items = ({ item, buttons, close }) => {
|
||||
const dimensions = useSettingStore((state) => state.dimensions);
|
||||
const actions = useActions({ item, close });
|
||||
const data = actions.filter((i) => buttons.indexOf(i.id) > -1 && !i.hidden);
|
||||
|
||||
let width = dimensions.width > 600 ? 600 : dimensions.width;
|
||||
const shouldShrink =
|
||||
Dimensions.get("window").fontScale > 1 &&
|
||||
Dimensions.get("window").width < 450;
|
||||
let columnItemsCount = DDS.isLargeTablet() ? 7 : shouldShrink ? 4 : 5;
|
||||
let columnItemsCount = DDS.isLargeTablet() ? 7 : 5;
|
||||
let columnItemWidth = DDS.isTab
|
||||
? (width - 12) / columnItemsCount
|
||||
: (width - 12) / columnItemsCount;
|
||||
@@ -68,7 +66,6 @@ export const Items = ({ item, buttons, close }) => {
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
allowFontScaling
|
||||
name={item.icon}
|
||||
size={DDS.isTab ? SIZE.xxl : SIZE.lg}
|
||||
color={
|
||||
@@ -81,11 +78,7 @@ export const Items = ({ item, buttons, close }) => {
|
||||
/>
|
||||
</PressableButton>
|
||||
|
||||
<Paragraph
|
||||
size={SIZE.xs}
|
||||
textBreakStrategy="simple"
|
||||
style={{ textAlign: "center" }}
|
||||
>
|
||||
<Paragraph size={SIZE.xs} style={{ textAlign: "center" }}>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</View>
|
||||
@@ -146,7 +139,6 @@ export const Items = ({ item, buttons, close }) => {
|
||||
>
|
||||
<Icon
|
||||
name={item.icon}
|
||||
allowFontScaling
|
||||
size={DDS.isTab ? SIZE.xxl : SIZE.md + 4}
|
||||
color={
|
||||
item.on
|
||||
@@ -158,11 +150,7 @@ export const Items = ({ item, buttons, close }) => {
|
||||
/>
|
||||
</PressableButton>
|
||||
|
||||
<Paragraph
|
||||
textBreakStrategy="simple"
|
||||
size={SIZE.xxs + 1}
|
||||
style={{ textAlign: "center" }}
|
||||
>
|
||||
<Paragraph size={SIZE.xxs + 1} style={{ textAlign: "center" }}>
|
||||
{item.title}
|
||||
</Paragraph>
|
||||
</PressableButton>
|
||||
@@ -175,12 +163,9 @@ export const Items = ({ item, buttons, close }) => {
|
||||
"copy",
|
||||
"share",
|
||||
"export",
|
||||
"lock-unlock"
|
||||
"lock-unlock",
|
||||
"publish"
|
||||
];
|
||||
if (!shouldShrink) {
|
||||
topBarItemsList.push("publish");
|
||||
}
|
||||
|
||||
const topBarItems = data.filter(
|
||||
(item) => topBarItemsList.indexOf(item.id) > -1
|
||||
);
|
||||
|
||||
@@ -17,10 +17,10 @@ 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 from "react";
|
||||
import { View, useWindowDimensions } from "react-native";
|
||||
import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { useUserStore } from "../../stores/use-user-store";
|
||||
import { openLinkInBrowser } from "../../utils/functions";
|
||||
import { SIZE } from "../../utils/size";
|
||||
@@ -33,8 +33,6 @@ export const Synced = ({ item, close }) => {
|
||||
const user = useUserStore((state) => state.user);
|
||||
const lastSynced = useUserStore((state) => state.lastSynced);
|
||||
|
||||
const dimensions = useWindowDimensions();
|
||||
const shouldShrink = dimensions.fontScale > 1 && dimensions.width < 450;
|
||||
return user && lastSynced >= item.dateModified ? (
|
||||
<View
|
||||
style={{
|
||||
@@ -51,11 +49,7 @@ export const Synced = ({ item, close }) => {
|
||||
borderTopColor: colors.secondary.background
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="shield-key-outline"
|
||||
color={colors.primary.accent}
|
||||
size={shouldShrink ? SIZE.xxl : SIZE.xxxl}
|
||||
/>
|
||||
<Icon name="shield-key-outline" color={colors.primary.accent} size={SIZE.xxxl} />
|
||||
|
||||
<View
|
||||
style={{
|
||||
@@ -73,17 +67,15 @@ export const Synced = ({ item, close }) => {
|
||||
>
|
||||
Encrypted and synced
|
||||
</Heading>
|
||||
{shouldShrink ? null : (
|
||||
<Paragraph
|
||||
style={{
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
size={SIZE.xs}
|
||||
color={colors.primary.paragraph}
|
||||
>
|
||||
No one can view this {item.itemType || item.type} except you.
|
||||
</Paragraph>
|
||||
)}
|
||||
<Paragraph
|
||||
style={{
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
size={SIZE.xs}
|
||||
color={colors.primary.paragraph}
|
||||
>
|
||||
No one can view this {item.itemType || item.type} except you.
|
||||
</Paragraph>
|
||||
</View>
|
||||
|
||||
<Button
|
||||
@@ -99,8 +91,8 @@ export const Synced = ({ item, close }) => {
|
||||
console.error(e);
|
||||
}
|
||||
}}
|
||||
title="Learn more"
|
||||
fontSize={SIZE.xs}
|
||||
title="Learn more"
|
||||
height={30}
|
||||
type="grayAccent"
|
||||
/>
|
||||
|
||||
@@ -207,6 +207,7 @@ export const SelectionHeader = React.memo(() => {
|
||||
|
||||
{screen === "Trash" ||
|
||||
screen === "Notebooks" ||
|
||||
screen === "Notebook" ||
|
||||
screen === "Reminders" ? null : (
|
||||
<>
|
||||
<IconButton
|
||||
@@ -223,21 +224,6 @@ export const SelectionHeader = React.memo(() => {
|
||||
name="pound"
|
||||
size={SIZE.xl}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
onPress={async () => {
|
||||
ExportNotesSheet.present(selectedItemsList);
|
||||
}}
|
||||
tooltipText="Export"
|
||||
tooltipPosition={4}
|
||||
customStyle={{
|
||||
marginLeft: 10
|
||||
}}
|
||||
color={colors.primary.paragraph}
|
||||
name="export"
|
||||
size={SIZE.xl}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
onPress={async () => {
|
||||
//setSelectionMode(false);
|
||||
@@ -253,6 +239,20 @@ export const SelectionHeader = React.memo(() => {
|
||||
name="plus"
|
||||
size={SIZE.xl}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
onPress={async () => {
|
||||
ExportNotesSheet.present(selectedItemsList);
|
||||
}}
|
||||
tooltipText="Export"
|
||||
tooltipPosition={4}
|
||||
customStyle={{
|
||||
marginLeft: 10
|
||||
}}
|
||||
color={colors.primary.paragraph}
|
||||
name="export"
|
||||
size={SIZE.xl}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -317,8 +317,23 @@ export const SelectionHeader = React.memo(() => {
|
||||
customStyle={{
|
||||
marginLeft: 10
|
||||
}}
|
||||
onPress={() => {
|
||||
deleteItems();
|
||||
onPress={async () => {
|
||||
presentDialog({
|
||||
title: `Delete ${
|
||||
selectedItemsList.length > 1 ? "items" : "item"
|
||||
}`,
|
||||
paragraph: `Are you sure you want to delete ${
|
||||
selectedItemsList.length > 1 ? "these items?" : "this item?"
|
||||
}`,
|
||||
positiveText: "Delete",
|
||||
negativeText: "Cancel",
|
||||
positivePress: () => {
|
||||
deleteItems();
|
||||
},
|
||||
positiveType: "errorShade"
|
||||
});
|
||||
|
||||
return;
|
||||
}}
|
||||
tooltipText="Move to trash"
|
||||
tooltipPosition={1}
|
||||
|
||||
@@ -198,6 +198,7 @@ const SheetProvider = ({ context = "global" }) => {
|
||||
key={data.actionText}
|
||||
title={data.actionText}
|
||||
accentColor={data.iconColor}
|
||||
accentText="light"
|
||||
type="accent"
|
||||
height={40}
|
||||
width={250}
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { FlashList } from "react-native-actions-sheet";
|
||||
import { FlatList } from "react-native-actions-sheet";
|
||||
import { db } from "../../../common/database";
|
||||
import { ListHeaderInputItem } from "./list-header-item.js";
|
||||
|
||||
@@ -54,7 +54,7 @@ export const FilteredList = ({
|
||||
}, [data, onChangeText]);
|
||||
|
||||
return (
|
||||
<FlashList
|
||||
<FlatList
|
||||
{...restProps}
|
||||
data={filtered}
|
||||
ref={listRef}
|
||||
|
||||
@@ -161,6 +161,9 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
: "deselected";
|
||||
if (itemState[notebook.id] === "selected") {
|
||||
count++;
|
||||
contextValue.select(notebook);
|
||||
} else {
|
||||
contextValue.deselect(notebook);
|
||||
}
|
||||
for (let topic of notebook.topics) {
|
||||
itemState[topic.id] = state
|
||||
@@ -173,6 +176,9 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
: "deselected";
|
||||
if (itemState[topic.id] === "selected") {
|
||||
count++;
|
||||
contextValue.select(topic);
|
||||
} else {
|
||||
contextValue.deselect(topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,7 +189,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
}
|
||||
useItemSelectionStore.getState().setItemState(itemState);
|
||||
},
|
||||
[getSelectedNotesCountInItem, selectedItemsList]
|
||||
[contextValue, getSelectedNotesCountInItem, selectedItemsList]
|
||||
);
|
||||
|
||||
const getItemsForItem = (item) => {
|
||||
@@ -208,7 +214,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
}
|
||||
|
||||
const updateItemState = useCallback(function (item, state) {
|
||||
const itemState = { ...useItemSelectionStore.getState().itemState };
|
||||
const itemState = useItemSelectionStore.getState().itemState;
|
||||
const mergeState = {
|
||||
[item.id]: state
|
||||
};
|
||||
@@ -364,93 +370,56 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
</View>
|
||||
|
||||
<SelectionProvider value={contextValue}>
|
||||
<View
|
||||
<FilteredList
|
||||
style={{
|
||||
paddingHorizontal: 12,
|
||||
maxHeight: dimensions.height * 0.85,
|
||||
height: 50 * (notebooks.length + 2)
|
||||
maxHeight: dimensions.height * 0.85
|
||||
}}
|
||||
>
|
||||
<FilteredList
|
||||
ListEmptyComponent={
|
||||
notebooks.length > 0 ? null : (
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="book-outline"
|
||||
color={colors.primary.icon}
|
||||
size={100}
|
||||
/>
|
||||
<Paragraph style={{ marginBottom: 10 }}>
|
||||
You do not have any notebooks.
|
||||
</Paragraph>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
estimatedItemSize={50}
|
||||
data={notebooks}
|
||||
hasHeaderSearch={true}
|
||||
renderItem={({ item, index }) => (
|
||||
<ListItem
|
||||
item={item}
|
||||
key={item.id}
|
||||
index={index}
|
||||
hasNotes={getSelectedNotesCountInItem(item) > 0}
|
||||
sheetRef={actionSheetRef}
|
||||
infoText={
|
||||
<>
|
||||
{item.topics.length === 1
|
||||
? item.topics.length + " topic"
|
||||
: item.topics.length + " topics"}
|
||||
</>
|
||||
}
|
||||
getListItems={getItemsForItem}
|
||||
getSublistItemProps={(topic) => ({
|
||||
hasNotes: getSelectedNotesCountInItem(topic) > 0,
|
||||
style: {
|
||||
marginBottom: 0,
|
||||
height: 40
|
||||
},
|
||||
onPress: (item) => {
|
||||
const itemState =
|
||||
useItemSelectionStore.getState().itemState;
|
||||
const currentState = itemState[item.id];
|
||||
if (currentState !== "selected") {
|
||||
resetItemState("deselected");
|
||||
contextValue.select(item);
|
||||
} else {
|
||||
contextValue.deselect(item);
|
||||
}
|
||||
},
|
||||
key: item.id,
|
||||
type: "transparent"
|
||||
})}
|
||||
icon={(expanded) => ({
|
||||
name: expanded ? "chevron-up" : "chevron-down",
|
||||
color: expanded
|
||||
? colors.primary.accent
|
||||
: colors.primary.paragraph
|
||||
})}
|
||||
onScrollEnd={() => {
|
||||
actionSheetRef.current?.handleChildScrollEnd();
|
||||
ListEmptyComponent={
|
||||
notebooks.length > 0 ? null : (
|
||||
<View
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
hasSubList={true}
|
||||
hasHeaderSearch={false}
|
||||
type="grayBg"
|
||||
sublistItemType="topic"
|
||||
onAddItem={(title) => {
|
||||
return onAddTopic(title, item);
|
||||
}}
|
||||
onAddSublistItem={(item) => {
|
||||
openAddTopicDialog(item);
|
||||
}}
|
||||
onPress={(item) => {
|
||||
>
|
||||
<Icon
|
||||
name="book-outline"
|
||||
color={colors.primary.icon}
|
||||
size={100}
|
||||
/>
|
||||
<Paragraph style={{ marginBottom: 10 }}>
|
||||
You do not have any notebooks.
|
||||
</Paragraph>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
data={notebooks}
|
||||
hasHeaderSearch={true}
|
||||
renderItem={({ item, index }) => (
|
||||
<ListItem
|
||||
item={item}
|
||||
key={item.id}
|
||||
index={index}
|
||||
hasNotes={getSelectedNotesCountInItem(item) > 0}
|
||||
sheetRef={actionSheetRef}
|
||||
infoText={
|
||||
<>
|
||||
{item.topics.length === 1
|
||||
? item.topics.length + " topic"
|
||||
: item.topics.length + " topics"}
|
||||
</>
|
||||
}
|
||||
getListItems={getItemsForItem}
|
||||
getSublistItemProps={(topic) => ({
|
||||
hasNotes: getSelectedNotesCountInItem(topic) > 0,
|
||||
style: {
|
||||
marginBottom: 0,
|
||||
height: 40
|
||||
},
|
||||
onPress: (item) => {
|
||||
const itemState =
|
||||
useItemSelectionStore.getState().itemState;
|
||||
const currentState = itemState[item.id];
|
||||
@@ -460,16 +429,47 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
|
||||
} else {
|
||||
contextValue.deselect(item);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
itemType="notebook"
|
||||
onAddItem={async (title) => {
|
||||
return await onAddNotebook(title);
|
||||
}}
|
||||
ListFooterComponent={<View style={{ height: 20 }} />}
|
||||
/>
|
||||
</View>
|
||||
},
|
||||
key: item.id,
|
||||
type: "transparent"
|
||||
})}
|
||||
icon={(expanded) => ({
|
||||
name: expanded ? "chevron-up" : "chevron-down",
|
||||
color: expanded
|
||||
? colors.primary.accent
|
||||
: colors.primary.paragraph
|
||||
})}
|
||||
onScrollEnd={() => {
|
||||
actionSheetRef.current?.handleChildScrollEnd();
|
||||
}}
|
||||
hasSubList={true}
|
||||
hasHeaderSearch={false}
|
||||
type="grayBg"
|
||||
sublistItemType="topic"
|
||||
onAddItem={(title) => {
|
||||
return onAddTopic(title, item);
|
||||
}}
|
||||
onAddSublistItem={(item) => {
|
||||
openAddTopicDialog(item);
|
||||
}}
|
||||
onPress={(item) => {
|
||||
const itemState = useItemSelectionStore.getState().itemState;
|
||||
const currentState = itemState[item.id];
|
||||
if (currentState !== "selected") {
|
||||
resetItemState("deselected");
|
||||
contextValue.select(item);
|
||||
} else {
|
||||
contextValue.deselect(item);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
itemType="notebook"
|
||||
onAddItem={async (title) => {
|
||||
return await onAddNotebook(title);
|
||||
}}
|
||||
ListFooterComponent={<View style={{ height: 20 }} />}
|
||||
/>
|
||||
</SelectionProvider>
|
||||
</View>
|
||||
</>
|
||||
|
||||
@@ -273,7 +273,6 @@ export const ListItem = ({
|
||||
alignSelf: "flex-end",
|
||||
maxHeight: 250
|
||||
}}
|
||||
estimatedItemSize={40}
|
||||
itemType={sublistItemType}
|
||||
hasHeaderSearch={hasHeaderSearch}
|
||||
renderItem={({ item, index }) => (
|
||||
|
||||
@@ -111,7 +111,7 @@ Logged in: ${user ? "yes" : "no"}`,
|
||||
} catch (e) {
|
||||
setLoading(false);
|
||||
ToastEvent.show({
|
||||
heading: "An error occurred",
|
||||
heading: "An error occured",
|
||||
message: e.message,
|
||||
type: "error"
|
||||
});
|
||||
|
||||
@@ -177,7 +177,7 @@ export default function Migrate() {
|
||||
textAlign: "center"
|
||||
}}
|
||||
>
|
||||
An error occurred while migrating your data. You can logout of your
|
||||
An error occured while migrating your data. You can logout of your
|
||||
account and try to relogin. However this is not recommended as it
|
||||
may result in some data loss if your data was not synced.
|
||||
</Paragraph>
|
||||
|
||||
@@ -40,9 +40,7 @@ import { requestInAppReview } from "../../../services/app-review";
|
||||
const PublishNoteSheet = ({ note: item, update }) => {
|
||||
const { colors } = useThemeColors();
|
||||
const actionSheetRef = useRef();
|
||||
|
||||
const attachmentDownloads = useAttachmentStore((state) => state.downloading);
|
||||
const downloading = attachmentDownloads[`monograph-${item.id}`];
|
||||
const loading = useAttachmentStore((state) => state.loading);
|
||||
const [selfDestruct, setSelfDestruct] = useState(false);
|
||||
const [isLocked, setIsLocked] = useState(false);
|
||||
const [note, setNote] = useState(item);
|
||||
@@ -139,9 +137,9 @@ const PublishNoteSheet = ({ note: item, update }) => {
|
||||
}}
|
||||
>
|
||||
Please wait...
|
||||
{downloading && downloading.current && downloading.total
|
||||
{loading && loading.current && loading.total
|
||||
? `\nDownloading attachments (${
|
||||
downloading?.current / downloading?.total
|
||||
loading?.current / loading?.total
|
||||
})`
|
||||
: ""}
|
||||
</Paragraph>
|
||||
|
||||
@@ -253,57 +253,6 @@ export default function ReminderSheet({
|
||||
marginBottom: DDS.isTab ? 25 : undefined
|
||||
}}
|
||||
>
|
||||
{reminderMode === ReminderModes.Permanent ? null : (
|
||||
<ScrollView
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
borderWidth: 1,
|
||||
marginTop: 12,
|
||||
borderRadius: 5,
|
||||
borderColor: colors.primary.border,
|
||||
paddingLeft: 12,
|
||||
height: 50
|
||||
}}
|
||||
horizontal
|
||||
>
|
||||
{Object.keys(ReminderNotificationModes).map((mode) => (
|
||||
<Button
|
||||
key={mode}
|
||||
title={mode}
|
||||
style={{
|
||||
marginRight: 12,
|
||||
borderRadius: 100
|
||||
}}
|
||||
icon={
|
||||
mode === "Silent"
|
||||
? "minus-circle"
|
||||
: mode === "Vibrate"
|
||||
? "vibrate"
|
||||
: "volume-high"
|
||||
}
|
||||
height={35}
|
||||
type={
|
||||
reminderNotificationMode ===
|
||||
ReminderNotificationModes[
|
||||
mode as keyof typeof ReminderNotificationModes
|
||||
]
|
||||
? "grayAccent"
|
||||
: "gray"
|
||||
}
|
||||
onPress={() => {
|
||||
const _mode = ReminderNotificationModes[
|
||||
mode as keyof typeof ReminderNotificationModes
|
||||
] as Reminder["priority"];
|
||||
SettingsService.set({
|
||||
reminderNotificationMode: _mode
|
||||
});
|
||||
setReminderNotificatioMode(_mode);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</ScrollView>
|
||||
)}
|
||||
|
||||
<Input
|
||||
fwdRef={titleRef}
|
||||
defaultValue={reminder?.title || referencedItem?.title}
|
||||
@@ -331,21 +280,15 @@ export default function ReminderSheet({
|
||||
}}
|
||||
height={80}
|
||||
wrapperStyle={{
|
||||
marginBottom: 12
|
||||
marginBottom: 20
|
||||
}}
|
||||
/>
|
||||
|
||||
<ScrollView
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
marginBottom: 12,
|
||||
height: 50,
|
||||
borderWidth: 1,
|
||||
borderRadius: 5,
|
||||
borderColor: colors.primary.border,
|
||||
paddingLeft: 12
|
||||
marginBottom: 12
|
||||
}}
|
||||
horizontal
|
||||
>
|
||||
{Object.keys(ReminderModes).map((mode) => (
|
||||
<Button
|
||||
@@ -384,7 +327,7 @@ export default function ReminderSheet({
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
{reminderMode === ReminderModes.Repeat ? (
|
||||
<View
|
||||
@@ -555,6 +498,53 @@ export default function ReminderSheet({
|
||||
</View>
|
||||
)}
|
||||
|
||||
{reminderMode === ReminderModes.Permanent ? null : (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
marginBottom: 12,
|
||||
paddingTop: 12,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: colors.secondary.background
|
||||
}}
|
||||
>
|
||||
{Object.keys(ReminderNotificationModes).map((mode) => (
|
||||
<Button
|
||||
key={mode}
|
||||
title={mode}
|
||||
style={{
|
||||
marginRight: 12,
|
||||
borderRadius: 100
|
||||
}}
|
||||
icon={
|
||||
mode === "Silent"
|
||||
? "minus-circle"
|
||||
: mode === "Vibrate"
|
||||
? "vibrate"
|
||||
: "volume-high"
|
||||
}
|
||||
height={35}
|
||||
type={
|
||||
reminderNotificationMode ===
|
||||
ReminderNotificationModes[
|
||||
mode as keyof typeof ReminderNotificationModes
|
||||
]
|
||||
? "grayAccent"
|
||||
: "gray"
|
||||
}
|
||||
onPress={() => {
|
||||
const _mode = ReminderNotificationModes[
|
||||
mode as keyof typeof ReminderNotificationModes
|
||||
] as Reminder["priority"];
|
||||
SettingsService.set({
|
||||
reminderNotificationMode: _mode
|
||||
});
|
||||
setReminderNotificatioMode(_mode);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
{reminderMode === ReminderModes.Once ||
|
||||
reminderMode === ReminderModes.Permanent ? null : (
|
||||
<View
|
||||
@@ -605,11 +595,6 @@ export default function ReminderSheet({
|
||||
}}
|
||||
/>
|
||||
</ScrollView>
|
||||
<View
|
||||
style={{
|
||||
height: 10
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,9 +22,7 @@ import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { ActivityIndicator, Platform, View } from "react-native";
|
||||
import { FlatList } from "react-native-actions-sheet";
|
||||
import RNFetchBlob from "react-native-blob-util";
|
||||
import DocumentPicker, {
|
||||
DocumentPickerResponse
|
||||
} from "react-native-document-picker";
|
||||
import DocumentPicker from "react-native-document-picker";
|
||||
import * as ScopedStorage from "react-native-scoped-storage";
|
||||
import { db } from "../../../common/database";
|
||||
import storage from "../../../common/database/storage";
|
||||
@@ -47,8 +45,6 @@ import Seperator from "../../ui/seperator";
|
||||
import SheetWrapper from "../../ui/sheet";
|
||||
import Paragraph from "../../ui/typography/paragraph";
|
||||
import { getFormattedDate } from "@notesnook/common";
|
||||
import { unzip } from "react-native-zip-archive";
|
||||
import { cacheDir, copyFileAsync } from "../../../common/filesystem/utils";
|
||||
|
||||
const RestoreDataSheet = () => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
@@ -133,71 +129,75 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
checkBackups();
|
||||
}, 1000);
|
||||
}, 300);
|
||||
}, []);
|
||||
|
||||
const restore = async (item) => {
|
||||
if (restoring) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const file = Platform.OS === "ios" ? item.path : item.uri;
|
||||
console.log(file);
|
||||
if (file.endsWith(".nnbackupz")) {
|
||||
setRestoring(true);
|
||||
setRestoring(true);
|
||||
let prefix = Platform.OS === "ios" ? "" : "file:/";
|
||||
let backup;
|
||||
if (Platform.OS === "android") {
|
||||
backup = await ScopedStorage.readFile(item.uri, "utf8");
|
||||
} else {
|
||||
backup = await RNFetchBlob.fs.readFile(prefix + item.path, "utf8");
|
||||
}
|
||||
backup = JSON.parse(backup);
|
||||
|
||||
if (Platform.OS === "android") {
|
||||
const cacheFile = `file://${RNFetchBlob.fs.dirs.CacheDir}/backup.zip`;
|
||||
if (await RNFetchBlob.fs.exists(cacheFile)) {
|
||||
await RNFetchBlob.fs.unlink(cacheFile);
|
||||
if (backup.data.iv && backup.data.salt) {
|
||||
withPassword(
|
||||
async (value) => {
|
||||
try {
|
||||
await restoreBackup(backup, value);
|
||||
close();
|
||||
setRestoring(false);
|
||||
return true;
|
||||
} catch (e) {
|
||||
backupError(e);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
() => {
|
||||
setRestoring(false);
|
||||
}
|
||||
await RNFetchBlob.fs.createFile(cacheFile, "", "utf8");
|
||||
console.log("copying");
|
||||
await copyFileAsync(file, cacheFile);
|
||||
console.log("copied");
|
||||
await restoreFromZip(cacheFile);
|
||||
} else {
|
||||
await restoreFromZip(file, false);
|
||||
}
|
||||
} else if (file.endsWith(".nnbackup")) {
|
||||
let backup;
|
||||
if (Platform.OS === "android") {
|
||||
backup = await ScopedStorage.readFile(file, "utf8");
|
||||
} else {
|
||||
backup = await RNFetchBlob.fs.readFile(file, "utf8");
|
||||
}
|
||||
await restoreFromNNBackup(JSON.parse(backup));
|
||||
);
|
||||
} else {
|
||||
await restoreBackup(backup);
|
||||
close();
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("error", e);
|
||||
setRestoring(false);
|
||||
backupError(e);
|
||||
}
|
||||
};
|
||||
|
||||
const withPassword = () => {
|
||||
return new Promise((resolve) => {
|
||||
let resolved = false;
|
||||
presentDialog({
|
||||
context: "local",
|
||||
title: "Encrypted backup",
|
||||
input: true,
|
||||
inputPlaceholder: "Password",
|
||||
paragraph: "Please enter password of this backup file to restore it",
|
||||
positiveText: "Restore",
|
||||
secureTextEntry: true,
|
||||
onClose: () => {
|
||||
if (resolved) return;
|
||||
resolve(undefined);
|
||||
},
|
||||
negativeText: "Cancel",
|
||||
positivePress: async (password) => {
|
||||
resolve(password);
|
||||
resolved = true;
|
||||
return true;
|
||||
const withPassword = (onsubmit, onclose = () => {}) => {
|
||||
presentDialog({
|
||||
context: "local",
|
||||
title: "Encrypted backup",
|
||||
input: true,
|
||||
inputPlaceholder: "Password",
|
||||
paragraph: "Please enter password of this backup file to restore it",
|
||||
positiveText: "Restore",
|
||||
secureTextEntry: true,
|
||||
onClose: onclose,
|
||||
negativeText: "Cancel",
|
||||
positivePress: async (password) => {
|
||||
try {
|
||||
return await onsubmit(password);
|
||||
} catch (e) {
|
||||
ToastEvent.show({
|
||||
heading: "Failed to backup data",
|
||||
message: e.message,
|
||||
type: "error",
|
||||
context: "global"
|
||||
});
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -217,164 +217,21 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
|
||||
let path = await storage.checkAndCreateDir("/backups/");
|
||||
files = await RNFetchBlob.fs.lstat(path);
|
||||
}
|
||||
files = files
|
||||
.filter((file) => {
|
||||
const name = Platform.OS === "android" ? file.name : file.filename;
|
||||
return name.endsWith(".nnbackup") || name.endsWith(".nnbackupz");
|
||||
})
|
||||
.sort(function (a, b) {
|
||||
let timeA = a.lastModified;
|
||||
let timeB = b.lastModified;
|
||||
return timeB - timeA;
|
||||
});
|
||||
files = files.sort(function (a, b) {
|
||||
let timeA = a.lastModified;
|
||||
let timeB = b.lastModified;
|
||||
return timeB - timeA;
|
||||
});
|
||||
setFiles(files);
|
||||
setLoading(false);
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
}, 1000);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const restoreBackup = async (backup, password) => {
|
||||
await db.backup.import(backup, password);
|
||||
await db.initCollections();
|
||||
initialize();
|
||||
ToastEvent.show({
|
||||
heading: "Backup restored successfully.",
|
||||
type: "success",
|
||||
context: "global"
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
const backupError = (e) => {
|
||||
ToastEvent.show({
|
||||
heading: "Restore failed",
|
||||
message:
|
||||
e.message ||
|
||||
"The selected backup data file is invalid. You must select a *.nnbackup file to restore.",
|
||||
type: "error",
|
||||
context: "local"
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} file
|
||||
*/
|
||||
async function restoreFromZip(file, remove) {
|
||||
try {
|
||||
const zipOutputFolder = `${cacheDir}/backup_extracted`;
|
||||
if (await RNFetchBlob.fs.exists(zipOutputFolder)) {
|
||||
await RNFetchBlob.fs.unlink(zipOutputFolder);
|
||||
await RNFetchBlob.fs.mkdir(zipOutputFolder);
|
||||
}
|
||||
await unzip(file, zipOutputFolder);
|
||||
console.log("Unzipped files successfully to", zipOutputFolder);
|
||||
|
||||
const backupFiles = await RNFetchBlob.fs.ls(zipOutputFolder);
|
||||
|
||||
if (backupFiles.findIndex((file) => file === ".nnbackup") === -1) {
|
||||
throw new Error("Backup file is invalid");
|
||||
}
|
||||
|
||||
let password;
|
||||
|
||||
console.log(`Found ${backupFiles?.length} files to restore from backup`);
|
||||
for (const path of backupFiles) {
|
||||
if (path === ".nnbackup") continue;
|
||||
const filePath = `${zipOutputFolder}/${path}`;
|
||||
const data = await RNFetchBlob.fs.readFile(filePath, "utf8");
|
||||
const parsed = JSON.parse(data);
|
||||
|
||||
if (parsed.encrypted && !password) {
|
||||
console.log("Backup is encrypted...", "requesting password");
|
||||
password = await withPassword();
|
||||
if (!password) throw new Error("Failed to decrypt backup");
|
||||
}
|
||||
await db.backup.import(parsed, password);
|
||||
console.log("Imported", path);
|
||||
}
|
||||
// Remove files from cache
|
||||
RNFetchBlob.fs.unlink(zipOutputFolder).catch(console.log);
|
||||
if (remove) {
|
||||
RNFetchBlob.fs.unlink(file).catch(console.log);
|
||||
}
|
||||
|
||||
await db.initCollections();
|
||||
initialize();
|
||||
setRestoring(false);
|
||||
close();
|
||||
ToastEvent.show({
|
||||
heading: "Backup restored successfully.",
|
||||
type: "success",
|
||||
context: "global"
|
||||
});
|
||||
} catch (e) {
|
||||
backupError(e);
|
||||
setRestoring(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} file
|
||||
*/
|
||||
async function restoreFromNNBackup(backup) {
|
||||
try {
|
||||
if (backup.data.iv && backup.data.salt) {
|
||||
const password = await withPassword();
|
||||
if (password) {
|
||||
try {
|
||||
await restoreBackup(backup, password);
|
||||
close();
|
||||
setRestoring(false);
|
||||
} catch (e) {
|
||||
setRestoring(false);
|
||||
backupError(e);
|
||||
}
|
||||
} else {
|
||||
setRestoring(false);
|
||||
}
|
||||
} else {
|
||||
await restoreBackup(backup);
|
||||
setRestoring(false);
|
||||
close();
|
||||
}
|
||||
} catch (e) {
|
||||
setRestoring(false);
|
||||
backupError(e);
|
||||
}
|
||||
}
|
||||
|
||||
const button = {
|
||||
title: "Restore from files",
|
||||
onPress: async () => {
|
||||
if (restoring) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const file = await DocumentPicker.pickSingle({
|
||||
copyTo: "cachesDirectory"
|
||||
});
|
||||
|
||||
if (file.name.endsWith(".nnbackupz")) {
|
||||
setRestoring(true);
|
||||
await restoreFromZip(file.fileCopyUri, true);
|
||||
} else if (file.name.endsWith(".nnbackup")) {
|
||||
RNFetchBlob.fs.unlink(file.fileCopyUri).catch(console.log);
|
||||
setRestoring(true);
|
||||
const data = await fetch(file.uri);
|
||||
await restoreFromNNBackup(await data.json());
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("error", e.stack);
|
||||
setRestoring(false);
|
||||
backupError(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const renderItem = ({ item, index }) => (
|
||||
<View
|
||||
style={{
|
||||
@@ -414,6 +271,76 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
|
||||
</View>
|
||||
);
|
||||
|
||||
const restoreBackup = async (backup, password) => {
|
||||
await db.backup.import(backup, password);
|
||||
setRestoring(false);
|
||||
initialize();
|
||||
ToastEvent.show({
|
||||
heading: "Backup restored successfully.",
|
||||
type: "success",
|
||||
context: "global"
|
||||
});
|
||||
};
|
||||
|
||||
const backupError = (e) => {
|
||||
ToastEvent.show({
|
||||
heading: "Restore failed",
|
||||
message:
|
||||
e.message ||
|
||||
"The selected backup data file is invalid. You must select a *.nnbackup file to restore.",
|
||||
type: "error",
|
||||
context: "local"
|
||||
});
|
||||
};
|
||||
|
||||
const button = {
|
||||
title: "Restore from files",
|
||||
onPress: () => {
|
||||
if (restoring) {
|
||||
return;
|
||||
}
|
||||
|
||||
DocumentPicker.pickSingle()
|
||||
.then((r) => {
|
||||
setRestoring(true);
|
||||
fetch(r.uri)
|
||||
.then(async (r) => {
|
||||
try {
|
||||
let backup = await r.json();
|
||||
if (backup.data.iv && backup.data.salt) {
|
||||
withPassword(
|
||||
async (value) => {
|
||||
try {
|
||||
restoreBackup(backup, value).then(() => {
|
||||
close();
|
||||
setRestoring(false);
|
||||
});
|
||||
return true;
|
||||
} catch (e) {
|
||||
backupError(e);
|
||||
setRestoring(false);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
() => {
|
||||
setRestoring(false);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
await restoreBackup(backup);
|
||||
close();
|
||||
}
|
||||
} catch (e) {
|
||||
setRestoring(false);
|
||||
backupError(e);
|
||||
}
|
||||
})
|
||||
.catch(console.log);
|
||||
})
|
||||
.catch(console.log);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<View>
|
||||
|
||||
@@ -16,7 +16,6 @@ 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 qclone from "qclone";
|
||||
import React, {
|
||||
createContext,
|
||||
@@ -27,7 +26,7 @@ import React, {
|
||||
useRef,
|
||||
useState
|
||||
} from "react";
|
||||
import { RefreshControl, useWindowDimensions, View } from "react-native";
|
||||
import { RefreshControl, View } from "react-native";
|
||||
import ActionSheet, {
|
||||
ActionSheetRef,
|
||||
FlatList
|
||||
@@ -46,6 +45,7 @@ import {
|
||||
import useNavigationStore, {
|
||||
NotebookScreenParams
|
||||
} from "../../../stores/use-navigation-store";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import {
|
||||
eOnNewTopicAdded,
|
||||
eOnTopicSheetUpdate,
|
||||
@@ -54,17 +54,18 @@ import {
|
||||
import { normalize, SIZE } from "../../../utils/size";
|
||||
import { GroupHeader, NotebookType, TopicType } from "../../../utils/types";
|
||||
|
||||
import { getTotalNotes } from "@notesnook/common";
|
||||
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";
|
||||
import { MMKV } from "../../../common/database/mmkv";
|
||||
import { openEditor } from "../../../screens/notes/common";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
import { getTotalNotes } from "@notesnook/common";
|
||||
import { deleteItems } from "../../../utils/functions";
|
||||
import { presentDialog } from "../../dialog/functions";
|
||||
import { Properties } from "../../properties";
|
||||
import Sort from "../sort";
|
||||
import { useSelectionStore } from "../../../stores/use-selection-store";
|
||||
|
||||
type ConfigItem = { id: string; type: string };
|
||||
class TopicSheetConfig {
|
||||
@@ -107,8 +108,7 @@ export const TopicsSheet = () => {
|
||||
)
|
||||
: []
|
||||
);
|
||||
const currentItem = useRef<string>();
|
||||
const { fontScale } = useWindowDimensions();
|
||||
|
||||
const [groupOptions, setGroupOptions] = useState(
|
||||
db.settings?.getGroupOptions("topics")
|
||||
);
|
||||
@@ -206,11 +206,6 @@ export const TopicsSheet = () => {
|
||||
if (canShow) {
|
||||
setTimeout(() => {
|
||||
const id = isTopic ? currentScreen?.notebookId : currentScreen?.id;
|
||||
if (currentItem.current !== id) {
|
||||
setSelection([]);
|
||||
setEnabled(false);
|
||||
}
|
||||
currentItem.current = id;
|
||||
const notebook = db.notebooks?.notebook(id as string)?.data;
|
||||
const snapPoint = isTopic
|
||||
? 0
|
||||
@@ -231,8 +226,6 @@ export const TopicsSheet = () => {
|
||||
}
|
||||
}, 300);
|
||||
} else {
|
||||
setSelection([]);
|
||||
setEnabled(false);
|
||||
ref.current?.hide();
|
||||
}
|
||||
}, [
|
||||
@@ -278,7 +271,7 @@ export const TopicsSheet = () => {
|
||||
backgroundColor: colors.secondary.background
|
||||
}}
|
||||
keyboardHandlerEnabled={false}
|
||||
snapPoints={Config.isTesting === "true" ? [100] : [20, 100]}
|
||||
snapPoints={Config.isTesting === "true" ? [100] : [25, 100]}
|
||||
initialSnapIndex={1}
|
||||
backgroundInteractionEnabled
|
||||
gestureEnabled
|
||||
@@ -312,8 +305,8 @@ export const TopicsSheet = () => {
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
maxHeight: 450,
|
||||
height: 450,
|
||||
maxHeight: 300,
|
||||
height: 300,
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
@@ -336,19 +329,30 @@ export const TopicsSheet = () => {
|
||||
{enabled ? (
|
||||
<IconButton
|
||||
customStyle={{
|
||||
marginLeft: 10,
|
||||
width: 40 * fontScale,
|
||||
height: 40 * fontScale
|
||||
marginLeft: 10
|
||||
}}
|
||||
onPress={async () => {
|
||||
//@ts-ignore
|
||||
useSelectionStore.setState({
|
||||
selectedItemsList: selection
|
||||
});
|
||||
await deleteItems();
|
||||
useSelectionStore.getState().clearSelection();
|
||||
setEnabled(false);
|
||||
setSelection([]);
|
||||
presentDialog({
|
||||
title: `Delete ${
|
||||
selection.length > 1 ? "topics" : "topics"
|
||||
}`,
|
||||
paragraph: `Are you sure you want to delete ${
|
||||
selection.length > 1 ? "these topics?" : "this topic?"
|
||||
}`,
|
||||
positiveText: "Delete",
|
||||
negativeText: "Cancel",
|
||||
positivePress: async () => {
|
||||
await deleteItems();
|
||||
useSelectionStore.getState().clearSelection();
|
||||
setEnabled(false);
|
||||
setSelection([]);
|
||||
},
|
||||
positiveType: "errorShade"
|
||||
});
|
||||
return;
|
||||
}}
|
||||
color={colors.primary.icon}
|
||||
@@ -374,8 +378,8 @@ export const TopicsSheet = () => {
|
||||
color={colors.primary.icon}
|
||||
size={22}
|
||||
customStyle={{
|
||||
width: 40 * fontScale,
|
||||
height: 40 * fontScale
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
@@ -385,8 +389,8 @@ export const TopicsSheet = () => {
|
||||
color={colors.primary.icon}
|
||||
size={22}
|
||||
customStyle={{
|
||||
width: 40 * fontScale,
|
||||
height: 40 * fontScale
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -404,8 +408,8 @@ export const TopicsSheet = () => {
|
||||
color={colors.primary.icon}
|
||||
size={22}
|
||||
customStyle={{
|
||||
width: 40 * fontScale,
|
||||
height: 40 * fontScale
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
@@ -479,7 +483,6 @@ const TopicItem = ({
|
||||
selection.selection.findIndex((selected) => selected.id === item.id) > -1;
|
||||
const isFocused = screen.id === item.id;
|
||||
const notesCount = getTotalNotes(item);
|
||||
const { fontScale } = useWindowDimensions();
|
||||
|
||||
return (
|
||||
<PressableButton
|
||||
@@ -516,10 +519,6 @@ const TopicItem = ({
|
||||
<IconButton
|
||||
size={SIZE.lg}
|
||||
color={isSelected ? colors.selected.icon : colors.primary.icon}
|
||||
top={0}
|
||||
left={0}
|
||||
bottom={0}
|
||||
right={0}
|
||||
name={
|
||||
isSelected
|
||||
? "check-circle-outline"
|
||||
@@ -539,8 +538,8 @@ const TopicItem = ({
|
||||
<IconButton
|
||||
name="dots-horizontal"
|
||||
customStyle={{
|
||||
width: 40 * fontScale,
|
||||
height: 40 * fontScale
|
||||
width: 40,
|
||||
height: 40
|
||||
}}
|
||||
testID={notesnook.ids.notebook.menu}
|
||||
onPress={() => {
|
||||
|
||||
@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { Dimensions, View } from "react-native";
|
||||
import { View } from "react-native";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import ToggleSwitch from "toggle-switch-react-native";
|
||||
import Navigation from "../../services/navigation";
|
||||
@@ -109,7 +109,6 @@ export const MenuItem = React.memo(
|
||||
textAlignVertical: "center",
|
||||
textAlign: "left"
|
||||
}}
|
||||
allowFontScaling
|
||||
name={item.icon}
|
||||
color={
|
||||
item.icon === "crown"
|
||||
|
||||
@@ -209,19 +209,13 @@ export const PinItem = React.memo(
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
allowFontScaling
|
||||
color={color}
|
||||
size={SIZE.lg - 2}
|
||||
name={icons[item.type]}
|
||||
/>
|
||||
<Icon color={color} size={SIZE.lg - 2} name={icons[item.type]} />
|
||||
<Icon
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: -6,
|
||||
left: -6
|
||||
}}
|
||||
allowFontScaling
|
||||
color={color}
|
||||
size={SIZE.xs}
|
||||
name="arrow-top-right-thick"
|
||||
|
||||
@@ -113,7 +113,6 @@ export const UserStatus = () => {
|
||||
<Icon
|
||||
name="checkbox-blank-circle"
|
||||
size={11}
|
||||
allowFontScaling
|
||||
color={
|
||||
!user || lastSyncStatus === SyncStatus.Failed
|
||||
? colors.error.icon
|
||||
@@ -150,15 +149,9 @@ export const UserStatus = () => {
|
||||
color={colors.error.icon}
|
||||
name="sync-alert"
|
||||
size={SIZE.lg}
|
||||
allowFontScaling
|
||||
/>
|
||||
) : (
|
||||
<Icon
|
||||
allowFontScaling
|
||||
color={colors.primary.accent}
|
||||
name="sync"
|
||||
size={SIZE.lg}
|
||||
/>
|
||||
<Icon color={colors.primary.accent} name="sync" size={SIZE.lg} />
|
||||
)
|
||||
) : null}
|
||||
</PressableButton>
|
||||
|
||||
@@ -134,7 +134,6 @@ export const Button = ({
|
||||
{icon && !loading && iconPosition === "left" ? (
|
||||
<Icon
|
||||
name={icon}
|
||||
allowFontScaling
|
||||
style={[{ marginRight: 0 }, iconStyle as any]}
|
||||
color={iconColor || buttonType?.text || textColor}
|
||||
size={iconSize}
|
||||
@@ -171,7 +170,6 @@ export const Button = ({
|
||||
{icon && !loading && iconPosition === "right" ? (
|
||||
<Icon
|
||||
name={icon}
|
||||
allowFontScaling
|
||||
style={[{ marginLeft: 0 }, iconStyle as any]}
|
||||
color={iconColor || buttonType?.text || textColor}
|
||||
size={iconSize}
|
||||
|
||||
@@ -40,6 +40,7 @@ interface IconButtonProps extends PressableButtonProps {
|
||||
iconStyle?: TextStyle;
|
||||
}
|
||||
|
||||
const AnimatedIcon = Animated.createAnimatedComponent(Icon);
|
||||
export const IconButton = ({
|
||||
onPress,
|
||||
name,
|
||||
@@ -87,10 +88,10 @@ export const IconButton = ({
|
||||
...customStyle
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
<AnimatedIcon
|
||||
layout={Layout}
|
||||
name={name}
|
||||
style={iconStyle as any}
|
||||
allowFontScaling
|
||||
color={
|
||||
restProps.disabled
|
||||
? RGB_Linear_Shade(-0.05, hexToRGBA(colors.secondary.background))
|
||||
|
||||
@@ -17,17 +17,15 @@ 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 { ScopedThemeProvider, useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import React from "react";
|
||||
import { Platform, View } from "react-native";
|
||||
import ActionSheet from "react-native-actions-sheet";
|
||||
import useGlobalSafeAreaInsets from "../../../hooks/use-global-safe-area-insets";
|
||||
import { useSettingStore } from "../../../stores/use-setting-store";
|
||||
import { ScopedThemeProvider, useThemeColors } from "@notesnook/theme";
|
||||
import { PremiumToast } from "../../premium/premium-toast";
|
||||
import { Toast } from "../../toast";
|
||||
import { useAppState } from "../../../hooks/use-app-state";
|
||||
import SettingsService from "../../../services/settings";
|
||||
import { useUserStore } from "../../../stores/use-user-store";
|
||||
import { BouncingView } from "../transitions/bouncing-view";
|
||||
|
||||
const SheetWrapper = ({
|
||||
children,
|
||||
@@ -43,7 +41,6 @@ const SheetWrapper = ({
|
||||
enableGesturesInScrollView = false,
|
||||
bottomPadding = true
|
||||
}) => {
|
||||
const localRef = useRef(null);
|
||||
const { colors } = useThemeColors("sheet");
|
||||
const deviceMode = useSettingStore((state) => state.deviceMode);
|
||||
const sheetKeyboardHandler = useSettingStore(
|
||||
@@ -53,8 +50,6 @@ const SheetWrapper = ({
|
||||
const smallTablet = deviceMode === "smallTablet";
|
||||
const dimensions = useSettingStore((state) => state.dimensions);
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const appState = useAppState();
|
||||
const lockEvents = useRef(false);
|
||||
let width = dimensions.width > 600 ? 600 : 500;
|
||||
|
||||
const style = React.useMemo(() => {
|
||||
@@ -73,40 +68,19 @@ const SheetWrapper = ({
|
||||
}, [colors.primary.background, largeTablet, smallTablet, width]);
|
||||
|
||||
const _onOpen = () => {
|
||||
if (lockEvents.current) return;
|
||||
onOpen && onOpen();
|
||||
};
|
||||
|
||||
const _onClose = async () => {
|
||||
if (lockEvents.current) return;
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (SettingsService.get().appLockMode === "background") {
|
||||
if (appState === "background") {
|
||||
const ref = fwdRef || localRef;
|
||||
ref?.current?.hide();
|
||||
if (useUserStore.getState().appLocked) {
|
||||
lockEvents.current = true;
|
||||
const unsub = useUserStore.subscribe((state) => {
|
||||
if (!state.appLocked) {
|
||||
ref?.current?.show();
|
||||
unsub();
|
||||
lockEvents.current = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [appState, fwdRef]);
|
||||
|
||||
return (
|
||||
<ScopedThemeProvider value="sheet">
|
||||
<ActionSheet
|
||||
ref={fwdRef || localRef}
|
||||
ref={fwdRef}
|
||||
testIDs={{
|
||||
backdrop: "sheet-backdrop"
|
||||
}}
|
||||
|
||||
@@ -59,6 +59,7 @@ const Heading = ({
|
||||
<Component
|
||||
layout={restProps.layout || Layout}
|
||||
allowFontScaling={true}
|
||||
maxFontSizeMultiplier={1}
|
||||
{...restProps}
|
||||
style={[
|
||||
{
|
||||
|
||||
@@ -45,6 +45,8 @@ const Paragraph = ({
|
||||
return (
|
||||
<Component
|
||||
layout={restProps.layout || Layout}
|
||||
allowFontScaling
|
||||
maxFontSizeMultiplier={1}
|
||||
{...restProps}
|
||||
style={[
|
||||
{
|
||||
|
||||
@@ -70,8 +70,8 @@ const NotebookWelcome = () => {
|
||||
count: 5
|
||||
},
|
||||
{
|
||||
title: "Recipes",
|
||||
description: "I love cooking and collecting recipes",
|
||||
title: "Recipies",
|
||||
description: "I love cooking and collecting recipies",
|
||||
count: 10
|
||||
}
|
||||
]);
|
||||
@@ -195,7 +195,7 @@ const notebooks: { id: string; steps: TStep[] } = {
|
||||
>
|
||||
<Paragraph size={SIZE.xs}>
|
||||
<Icon color={colors.primary.icon} size={SIZE.sm} name="note" />{" "}
|
||||
February 2022 Week 2
|
||||
Feburary 2022 Week 2
|
||||
</Paragraph>
|
||||
</View>
|
||||
<View
|
||||
@@ -211,7 +211,7 @@ const notebooks: { id: string; steps: TStep[] } = {
|
||||
>
|
||||
<Paragraph size={SIZE.xs}>
|
||||
<Icon color={colors.primary.icon} size={SIZE.sm} name="note" />{" "}
|
||||
February 2022 Week 1
|
||||
Feburary 2022 Week 1
|
||||
</Paragraph>
|
||||
</View>
|
||||
<View
|
||||
@@ -257,7 +257,7 @@ const notebooks: { id: string; steps: TStep[] } = {
|
||||
title: "Tasks",
|
||||
type: "topic"
|
||||
}}
|
||||
onPress={() => { }}
|
||||
onPress={() => {}}
|
||||
/>
|
||||
|
||||
<PinItem
|
||||
@@ -267,7 +267,7 @@ const notebooks: { id: string; steps: TStep[] } = {
|
||||
title: "Work and office",
|
||||
type: "notebook"
|
||||
}}
|
||||
onPress={() => { }}
|
||||
onPress={() => {}}
|
||||
/>
|
||||
</View>
|
||||
),
|
||||
|
||||
@@ -520,7 +520,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
await sleep(300);
|
||||
presentDialog({
|
||||
title: "Permanent delete",
|
||||
paragraph: `Are you sure you want to delete this ${item.itemType} permanently from trash?`,
|
||||
paragraph: `Are you sure you want to delete this ${item.itemType} permanantly from trash?`,
|
||||
positiveText: "Delete",
|
||||
negativeText: "Cancel",
|
||||
positivePress: async () => {
|
||||
@@ -529,7 +529,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
Navigation.queueRoutesForUpdate();
|
||||
useSelectionStore.getState().setSelectionMode(false);
|
||||
ToastEvent.show({
|
||||
heading: "Permanently deleted items",
|
||||
heading: "Permanantly deleted items",
|
||||
type: "success",
|
||||
context: "local"
|
||||
});
|
||||
@@ -649,7 +649,7 @@ export const useActions = ({ close = () => null, item }) => {
|
||||
},
|
||||
{
|
||||
id: "favorite",
|
||||
title: item.favorite ? "Unfav" : "Fav",
|
||||
title: item.favorite ? "Unfavorite" : "Favorite",
|
||||
icon: item.favorite ? "star-off" : "star-outline",
|
||||
func: addToFavorites,
|
||||
close: false,
|
||||
|
||||
@@ -83,6 +83,7 @@ import {
|
||||
import { getGithubVersion } from "../utils/github-version";
|
||||
import { tabBarRef } from "../utils/global-refs";
|
||||
import { sleep } from "../utils/time";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
|
||||
const onCheckSyncStatus = async (type) => {
|
||||
const { disableSync, disableAutoSync } = SettingsService.get();
|
||||
@@ -106,20 +107,10 @@ const onFileEncryptionProgress = ({ total, progress }) => {
|
||||
.setEncryptionProgress((progress / total).toFixed(2));
|
||||
};
|
||||
|
||||
const onDownloadingAttachmentProgress = (data) => {
|
||||
useAttachmentStore.getState().setDownloading(data);
|
||||
};
|
||||
|
||||
const onUploadingAttachmentProgress = (data) => {
|
||||
useAttachmentStore.getState().setUploading(data);
|
||||
};
|
||||
|
||||
const onDownloadedAttachmentProgress = (data) => {
|
||||
useAttachmentStore.getState().setDownloading(data);
|
||||
};
|
||||
|
||||
const onUploadedAttachmentProgress = (data) => {
|
||||
useAttachmentStore.getState().setUploading(data);
|
||||
const onLoadingAttachmentProgress = (data) => {
|
||||
useAttachmentStore
|
||||
.getState()
|
||||
.setLoading(data.total === data.current ? null : data);
|
||||
};
|
||||
|
||||
const onUserSessionExpired = async () => {
|
||||
@@ -310,16 +301,7 @@ export const useAppEvents = () => {
|
||||
EVENTS.userSubscriptionUpdated,
|
||||
onUserSubscriptionStatusChanged
|
||||
),
|
||||
EV.subscribe(EVENTS.fileDownload, onDownloadingAttachmentProgress),
|
||||
EV.subscribe(EVENTS.fileUpload, onUploadingAttachmentProgress),
|
||||
EV.subscribe(EVENTS.fileDownloaded, onDownloadedAttachmentProgress),
|
||||
EV.subscribe(EVENTS.fileUploaded, onUploadedAttachmentProgress),
|
||||
EV.subscribe(EVENTS.downloadCanceled, (data) => {
|
||||
useAttachmentStore.getState().setDownloading(data);
|
||||
}),
|
||||
EV.subscribe(EVENTS.uploadCanceled, (data) => {
|
||||
useAttachmentStore.getState().setUploading(data);
|
||||
}),
|
||||
EV.subscribe(EVENTS.attachmentsLoading, onLoadingAttachmentProgress),
|
||||
eSubscribeEvent(eUserLoggedIn, onUserUpdated)
|
||||
];
|
||||
|
||||
@@ -467,7 +449,7 @@ export const useAppEvents = () => {
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
ToastEvent.error(e, "An error occurred", "global");
|
||||
ToastEvent.error(e, "An error occured", "global");
|
||||
}
|
||||
|
||||
user = await db.user.getUser();
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { useEffect, useState } from "react";
|
||||
import { db } from "../common/database";
|
||||
import { eSubscribeEvent, eUnSubscribeEvent } from "../services/event-manager";
|
||||
import Navigation from "../services/navigation";
|
||||
|
||||
export function useGroupOptions(type: any) {
|
||||
const [groupOptions, setGroupOptions] = useState(
|
||||
db.settings?.getGroupOptions(type)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const onUpdate = () => {
|
||||
const options = db.settings?.getGroupOptions(type) as any;
|
||||
if (
|
||||
groupOptions?.groupBy !== options.groupBy ||
|
||||
groupOptions?.sortBy !== options.sortBy ||
|
||||
groupOptions?.sortDirection !== groupOptions?.sortDirection
|
||||
) {
|
||||
setGroupOptions({ ...options });
|
||||
Navigation.queueRoutesForUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
eSubscribeEvent("groupOptionsUpdate", onUpdate);
|
||||
return () => {
|
||||
eUnSubscribeEvent("groupOptionsUpdate", onUpdate);
|
||||
};
|
||||
}, [type, groupOptions]);
|
||||
|
||||
return groupOptions;
|
||||
}
|
||||
@@ -384,16 +384,7 @@ const _TabsHolder = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
function updateSystemBars() {
|
||||
changeNavigationBarColor(colors.primary.background, isDark, true);
|
||||
StatusBar.setBackgroundColor("transparent");
|
||||
StatusBar.setTranslucent(true);
|
||||
StatusBar.setBarStyle(isDark ? "light-content" : "dark-content");
|
||||
}
|
||||
updateSystemBars();
|
||||
setTimeout(() => {
|
||||
updateSystemBars();
|
||||
}, 1000);
|
||||
changeNavigationBarColor(colors.primary.background, isDark, true);
|
||||
}, [colors.primary.background, isDark]);
|
||||
|
||||
return (
|
||||
@@ -415,7 +406,11 @@ const _TabsHolder = () => {
|
||||
: 0
|
||||
}}
|
||||
>
|
||||
<StatusBar translucent={true} backgroundColor="transparent" />
|
||||
<StatusBar
|
||||
barStyle={isDark ? "light-content" : "dark-content"}
|
||||
translucent={true}
|
||||
backgroundColor="transparent"
|
||||
/>
|
||||
|
||||
{!introCompleted ? (
|
||||
<NavigationStack />
|
||||
|
||||
@@ -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.24",
|
||||
"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",
|
||||
|
||||
81
apps/mobile/app/screens/editor/progress.js
Normal file
81
apps/mobile/app/screens/editor/progress.js
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { ProgressBarComponent } from "../../components/ui/svg/lazy";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { useAttachmentStore } from "../../stores/use-attachment-store";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { SIZE } from "../../utils/size";
|
||||
export const ProgressBar = () => {
|
||||
const { colors } = useThemeColors();
|
||||
const loading = useAttachmentStore((state) => state.loading);
|
||||
const [prog, setProg] = useState(0);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const timer = useRef();
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const [width, setWidth] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
if (loading.current !== loading.total) {
|
||||
setVisible(true);
|
||||
setProg(loading.current / loading.total);
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
}, [loading]);
|
||||
|
||||
const clear = () => {
|
||||
clearTimeout(timer.current);
|
||||
timer.current = null;
|
||||
timer.current = setTimeout(() => {
|
||||
setProg(1);
|
||||
setTimeout(() => {
|
||||
setVisible(false);
|
||||
}, 1000);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
return visible ? (
|
||||
<View
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
position: "absolute",
|
||||
zIndex: 1,
|
||||
marginTop: insets.top + 45,
|
||||
width: "100%"
|
||||
}}
|
||||
onLayout={(event) => setWidth(event.nativeEvent.layout.width)}
|
||||
>
|
||||
<ProgressBarComponent
|
||||
size={SIZE.xxl}
|
||||
progress={prog}
|
||||
color={colors.primary.accent}
|
||||
borderWidth={0}
|
||||
height={1}
|
||||
width={width || 400}
|
||||
/>
|
||||
</View>
|
||||
) : null;
|
||||
};
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import { ProgressBarComponent } from "../../components/ui/svg/lazy";
|
||||
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
|
||||
import { useAttachmentStore } from "../../stores/use-attachment-store";
|
||||
import { useEditorStore } from "../../stores/use-editor-store";
|
||||
|
||||
export const ProgressBar = () => {
|
||||
const { colors } = useThemeColors();
|
||||
const currentlyEditingNote = useEditorStore(
|
||||
(state) => state.currentEditingNote
|
||||
);
|
||||
const downloading = useAttachmentStore((state) => state.downloading);
|
||||
|
||||
const loading = currentlyEditingNote
|
||||
? downloading?.[currentlyEditingNote]
|
||||
: undefined;
|
||||
|
||||
const attachmentProgress = useAttachmentStore((state) => state.progress);
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const timer = useRef<NodeJS.Timeout>();
|
||||
const insets = useGlobalSafeAreaInsets();
|
||||
const [width, setWidth] = useState(400);
|
||||
|
||||
const groupProgressInfo = useRef<{
|
||||
[name: string]: {
|
||||
total: number;
|
||||
current: number;
|
||||
};
|
||||
}>({});
|
||||
|
||||
const currentItemProgress = loading?.filename
|
||||
? attachmentProgress?.[loading?.filename]
|
||||
: undefined;
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
console.log(loading);
|
||||
if (
|
||||
loading.current === loading.total &&
|
||||
typeof loading.success === "boolean"
|
||||
) {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
setVisible(true);
|
||||
if (!loading.filename) return;
|
||||
|
||||
if (!groupProgressInfo.current[loading.filename]) {
|
||||
groupProgressInfo.current[loading.filename] = {
|
||||
total: 1,
|
||||
current: 0
|
||||
};
|
||||
}
|
||||
|
||||
const itemProgressCurrent = groupProgressInfo.current[loading.filename];
|
||||
|
||||
const itemTotalSize =
|
||||
currentItemProgress?.total || itemProgressCurrent.total;
|
||||
|
||||
const itemCurrentProgress =
|
||||
currentItemProgress?.recieved ||
|
||||
currentItemProgress?.sent ||
|
||||
itemProgressCurrent.current;
|
||||
|
||||
groupProgressInfo.current[loading.filename] = {
|
||||
current: itemCurrentProgress,
|
||||
total: itemTotalSize
|
||||
};
|
||||
|
||||
const itemProgressPercent = itemCurrentProgress / itemTotalSize;
|
||||
|
||||
setProgress(
|
||||
((loading.current || 0) + itemProgressPercent) / (loading.total || 1)
|
||||
);
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
}, [currentItemProgress, loading]);
|
||||
|
||||
const clear = () => {
|
||||
clearTimeout(timer.current);
|
||||
timer.current = undefined;
|
||||
timer.current = setTimeout(() => {
|
||||
setProgress(1);
|
||||
setTimeout(() => {
|
||||
setVisible(false);
|
||||
setProgress(0);
|
||||
groupProgressInfo.current = {};
|
||||
}, 1000);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
position: "absolute",
|
||||
zIndex: visible ? 1 : -1,
|
||||
marginTop: insets.top + 45,
|
||||
width: "100%"
|
||||
}}
|
||||
onLayout={(event) => setWidth(event.nativeEvent.layout.width)}
|
||||
>
|
||||
<ProgressBarComponent
|
||||
progress={progress}
|
||||
color={colors.primary.accent}
|
||||
borderWidth={0}
|
||||
borderRadius={0}
|
||||
height={2}
|
||||
width={width || 400}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -52,7 +52,6 @@ export type Settings = {
|
||||
fontFamily: string;
|
||||
dateFormat: string;
|
||||
timeFormat: string;
|
||||
fontScale: number;
|
||||
};
|
||||
|
||||
export type EditorProps = {
|
||||
|
||||
@@ -28,8 +28,7 @@ import {
|
||||
InteractionManager,
|
||||
Keyboard,
|
||||
KeyboardEventListener,
|
||||
NativeEventSubscription,
|
||||
useWindowDimensions
|
||||
NativeEventSubscription
|
||||
} from "react-native";
|
||||
import { WebViewMessageEvent } from "react-native-webview";
|
||||
import { db } from "../../../common/database";
|
||||
@@ -143,8 +142,6 @@ export const useEditorEvents = (
|
||||
const handleBack = useRef<NativeEventSubscription>();
|
||||
const readonly = useEditorStore((state) => state.readonly);
|
||||
const isPremium = useUserStore((state) => state.premium);
|
||||
const { fontScale } = useWindowDimensions();
|
||||
|
||||
const doubleSpacedLines = useSettingStore(
|
||||
(state) => state.settings?.doubleSpacedLines
|
||||
);
|
||||
@@ -198,8 +195,7 @@ export const useEditorEvents = (
|
||||
: defaultFontSize,
|
||||
fontFamily: SettingsService.get().defaultFontFamily,
|
||||
dateFormat: db.settings?.getDateFormat(),
|
||||
timeFormat: db.settings?.getTimeFormat(),
|
||||
fontScale
|
||||
timeFormat: db.settings?.getTimeFormat()
|
||||
});
|
||||
}, [
|
||||
fullscreen,
|
||||
@@ -219,8 +215,7 @@ export const useEditorEvents = (
|
||||
defaultFontFamily,
|
||||
dateFormat,
|
||||
timeFormat,
|
||||
loading,
|
||||
fontScale
|
||||
loading
|
||||
]);
|
||||
|
||||
const onBackPress = useCallback(async () => {
|
||||
|
||||
@@ -22,7 +22,7 @@ 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";
|
||||
import { DatabaseLogger, db } from "../../../common/database";
|
||||
import { db } from "../../../common/database";
|
||||
import useGlobalSafeAreaInsets from "../../../hooks/use-global-safe-area-insets";
|
||||
import { DDS } from "../../../services/device-detection";
|
||||
import {
|
||||
@@ -134,8 +134,7 @@ export const useEditor = (
|
||||
|
||||
const reset = useCallback(
|
||||
async (resetState = true, resetContent = true) => {
|
||||
currentNote.current?.id &&
|
||||
db.fs?.cancel(currentNote.current.id, "download");
|
||||
currentNote.current?.id && db.fs?.cancel(currentNote.current.id, null);
|
||||
currentNote.current = null;
|
||||
loadedImages.current = {};
|
||||
currentContent.current = null;
|
||||
@@ -148,12 +147,7 @@ export const useEditor = (
|
||||
lastContentChangeTime.current = 0;
|
||||
resetContent && (await commands.clearContent());
|
||||
resetContent && (await commands.clearTags());
|
||||
|
||||
if (resetState) {
|
||||
const newSessionId = makeSessionId();
|
||||
await commands.setSessionId(newSessionId);
|
||||
setSessionId(newSessionId);
|
||||
|
||||
isDefaultEditor &&
|
||||
useEditorStore.getState().setCurrentlyEditingNote(null);
|
||||
placeholderTip.current = TipManager.placeholderTip();
|
||||
@@ -270,8 +264,7 @@ export const useEditor = (
|
||||
saveCount.current++;
|
||||
return id;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
DatabaseLogger.error(e as Error);
|
||||
console.log("Error saving note: ", e);
|
||||
}
|
||||
},
|
||||
[commands, isDefaultEditor, postMessage, readonly, reset]
|
||||
@@ -286,7 +279,6 @@ export const useEditor = (
|
||||
noteId: currentNote.current?.id as string
|
||||
};
|
||||
} else {
|
||||
if (!note.contentId) return;
|
||||
currentContent.current = await db.content?.raw(note.contentId);
|
||||
}
|
||||
}, []);
|
||||
@@ -374,23 +366,12 @@ export const useEditor = (
|
||||
if (!item.forced && currentNote.current?.id === item.id) return;
|
||||
state.current.movedAway = false;
|
||||
state.current.currentlyEditing = true;
|
||||
|
||||
if (currentNote.current?.id !== item.id) {
|
||||
currentNote.current && (await reset(false, false));
|
||||
isDefaultEditor && editorState.setCurrentlyEditingNote(item.id);
|
||||
}
|
||||
|
||||
isDefaultEditor && editorState.setCurrentlyEditingNote(item.id);
|
||||
currentNote.current && (await reset(false, false));
|
||||
await loadContent(item as NoteType);
|
||||
|
||||
if (
|
||||
currentNote.current?.id === item.id &&
|
||||
loadingState.current &&
|
||||
currentContent.current?.data &&
|
||||
loadingState.current === currentContent.current?.data
|
||||
) {
|
||||
if (loadingState.current === currentContent.current?.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!currentContent.current?.data ||
|
||||
currentContent.current?.data.length < 50000
|
||||
@@ -408,20 +389,17 @@ export const useEditor = (
|
||||
lockedSessionId.current = nextSessionId;
|
||||
sessionHistoryId.current = Date.now();
|
||||
setSessionId(nextSessionId);
|
||||
|
||||
commands.setSessionId(nextSessionId);
|
||||
sessionIdRef.current = nextSessionId;
|
||||
currentNote.current = item as NoteType;
|
||||
await commands.setStatus(getFormattedDate(item.dateEdited), "Saved");
|
||||
await postMessage(EditorEvents.title, item.title);
|
||||
loadingState.current = currentContent.current?.data;
|
||||
if (currentContent.current?.data) {
|
||||
await postMessage(
|
||||
EditorEvents.html,
|
||||
currentContent.current?.data,
|
||||
10000
|
||||
);
|
||||
}
|
||||
await postMessage(
|
||||
EditorEvents.html,
|
||||
currentContent.current?.data,
|
||||
10000
|
||||
);
|
||||
loadingState.current = undefined;
|
||||
useEditorStore.getState().setReadonly(item.readonly);
|
||||
await commands.setTags(currentNote.current);
|
||||
@@ -547,14 +525,6 @@ export const useEditor = (
|
||||
}) => {
|
||||
if (lock.current || lockedSessionId.current === forSessionId) return;
|
||||
lastContentChangeTime.current = Date.now();
|
||||
|
||||
if (
|
||||
sessionHistoryId.current &&
|
||||
Date.now() - sessionHistoryId.current > 5 * 60 * 1000
|
||||
) {
|
||||
sessionHistoryId.current = Date.now();
|
||||
}
|
||||
|
||||
if (type === EditorEvents.content) {
|
||||
currentContent.current = {
|
||||
data: content,
|
||||
|
||||
@@ -71,7 +71,7 @@ export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
|
||||
<List
|
||||
listData={notes}
|
||||
type="notes"
|
||||
screen="Home"
|
||||
screen="Notes"
|
||||
loading={loading || !isFocused}
|
||||
headerProps={{
|
||||
heading: "Notes"
|
||||
|
||||
@@ -71,7 +71,7 @@ export const SearchBar = () => {
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
ToastEvent.show({
|
||||
heading: "Error occurred while searching",
|
||||
heading: "Error occured while searching",
|
||||
message: e.message,
|
||||
type: "error"
|
||||
});
|
||||
|
||||
@@ -88,8 +88,8 @@ export const settingsGroups: SettingSection[] = [
|
||||
return isBasic || !user.subscription?.type
|
||||
? "Subscribe to Pro"
|
||||
: isTrial
|
||||
? "Your free trial has started"
|
||||
: "Subscription details";
|
||||
? "Your free trial has started"
|
||||
: "Subscription details";
|
||||
},
|
||||
type: "component",
|
||||
component: "subscription",
|
||||
@@ -111,16 +111,16 @@ export const settingsGroups: SettingSection[] = [
|
||||
return user.subscription?.type === 2
|
||||
? "You signed up on " + startDate
|
||||
: user.subscription?.type === 1
|
||||
? "Your free trial will end on " + expiryDate
|
||||
: user.subscription?.type === 6
|
||||
? subscriptionDaysLeft.time < -3
|
||||
? "Your subscription has ended"
|
||||
: "Your account will be downgraded to Basic in 3 days"
|
||||
: user.subscription?.type === 7
|
||||
? `Your subscription will end on ${expiryDate}.`
|
||||
: user.subscription?.type === 5
|
||||
? `Your subscription will renew on ${expiryDate}.`
|
||||
: "Never hesitate to choose privacy";
|
||||
? "Your free trial will end on " + expiryDate
|
||||
: user.subscription?.type === 6
|
||||
? subscriptionDaysLeft.time < -3
|
||||
? "Your subscription has ended"
|
||||
: "Your account will be downgraded to Basic in 3 days"
|
||||
: user.subscription?.type === 7
|
||||
? `Your subscription will end on ${expiryDate}.`
|
||||
: user.subscription?.type === 5
|
||||
? `Your subscription will renew on ${expiryDate}.`
|
||||
: "Never hesitate to choose privacy";
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -320,13 +320,13 @@ export const settingsGroups: SettingSection[] = [
|
||||
name: "Delete account",
|
||||
icon: "alert",
|
||||
description:
|
||||
"All your data will be removed permanently. Make sure you have saved backup of your notes. This action is IRREVERSIBLE.",
|
||||
"All your data will be removed permanantly. Make sure you have saved backup of your notes. This action is IRREVERSIBLE.",
|
||||
modifer: () => {
|
||||
presentDialog({
|
||||
title: "Delete account",
|
||||
paragraphColor: "red",
|
||||
paragraph:
|
||||
"All your data will be removed permanently. Make sure you have saved backup of your notes. This action is IRREVERSIBLE.",
|
||||
"All your data will be removed permanantly. Make sure you have saved backup of your notes. This action is IRREVERSIBLE.",
|
||||
positiveType: "errorShade",
|
||||
input: true,
|
||||
inputPlaceholder: "Enter account password",
|
||||
@@ -628,9 +628,7 @@ export const settingsGroups: SettingSection[] = [
|
||||
name: "CORS bypass proxy",
|
||||
description: "You can set a custom proxy URL to increase your privacy.",
|
||||
inputProperties: {
|
||||
defaultValue: "https://cors.notesnook.com",
|
||||
autoCorrect: false,
|
||||
keyboardType: "url"
|
||||
defaultValue: "https://cors.notesnook.com"
|
||||
},
|
||||
property: "corsProxy",
|
||||
icon: "arrow-decision-outline"
|
||||
|
||||
@@ -52,7 +52,7 @@ const onPressFloatingButton = () => {
|
||||
ToastEvent.show({
|
||||
heading: "Trash cleared",
|
||||
message:
|
||||
"All notes and notebooks in the trash have been removed permanently.",
|
||||
"All notes and notebooks in the trash have been removed permanantly.",
|
||||
type: "success",
|
||||
context: "local"
|
||||
});
|
||||
|
||||
@@ -17,6 +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 { sanitizeFilename } from "@notesnook/common";
|
||||
import { Platform } from "react-native";
|
||||
import RNFetchBlob from "react-native-blob-util";
|
||||
import FileViewer from "react-native-file-viewer";
|
||||
@@ -29,8 +30,6 @@ import { eCloseSheet } from "../utils/events";
|
||||
import { sleep } from "../utils/time";
|
||||
import { ToastEvent, eSendEvent, presentSheet } from "./event-manager";
|
||||
import SettingsService from "./settings";
|
||||
import { cacheDir, copyFileAsync } from "../common/filesystem/utils";
|
||||
import { zip } from "react-native-zip-archive";
|
||||
|
||||
const MS_DAY = 86400000;
|
||||
const MS_WEEK = MS_DAY * 7;
|
||||
@@ -153,6 +152,8 @@ async function run(progress, context) {
|
||||
let androidBackupDirectory = await checkBackupDirExists(false, context);
|
||||
if (!androidBackupDirectory) return;
|
||||
|
||||
let backup;
|
||||
|
||||
if (progress) {
|
||||
presentSheet({
|
||||
title: "Backing up your data",
|
||||
@@ -162,67 +163,43 @@ async function run(progress, context) {
|
||||
});
|
||||
}
|
||||
|
||||
let path;
|
||||
let backupFilePath;
|
||||
let backupFileName = "notesnook_backup_" + Date.now();
|
||||
|
||||
if (Platform.OS === "ios") {
|
||||
path = await storage.checkAndCreateDir("/backups/");
|
||||
}
|
||||
|
||||
const zipSourceFolder = `${cacheDir}/${backupFileName}`;
|
||||
const zipOutputFile =
|
||||
Platform.OS === "ios"
|
||||
? `${path}/${backupFileName}.nnbackupz`
|
||||
: `${cacheDir}/${backupFileName}.nnbackupz`;
|
||||
|
||||
if (await RNFetchBlob.fs.exists(zipSourceFolder))
|
||||
await RNFetchBlob.fs.unlink(zipSourceFolder);
|
||||
|
||||
await RNFetchBlob.fs.mkdir(zipSourceFolder);
|
||||
|
||||
try {
|
||||
for await (const file of db.backup.export(
|
||||
backup = await db.backup.export(
|
||||
"mobile",
|
||||
SettingsService.get().encryptedBackup
|
||||
)) {
|
||||
console.log("Writing backup chunk of size...", file?.data?.length);
|
||||
await RNFetchBlob.fs.writeFile(
|
||||
`${zipSourceFolder}/${file.path}`,
|
||||
file.data,
|
||||
"utf8"
|
||||
);
|
||||
}
|
||||
|
||||
await zip(zipSourceFolder, zipOutputFile);
|
||||
|
||||
console.log("Final zip:", await RNFetchBlob.fs.stat(zipOutputFile));
|
||||
|
||||
if (Platform.OS === "android") {
|
||||
// Move the zip to user selected directory.
|
||||
const file = await ScopedStorage.createFile(
|
||||
androidBackupDirectory.uri,
|
||||
`${backupFileName}.nnbackupz`,
|
||||
"application/nnbackupz"
|
||||
);
|
||||
await copyFileAsync(`file://${zipOutputFile}`, file.uri);
|
||||
path = file.uri;
|
||||
} else {
|
||||
path = zipOutputFile;
|
||||
}
|
||||
RNFetchBlob.fs.unlink(zipSourceFolder).catch(console.log);
|
||||
updateNextBackupTime();
|
||||
|
||||
let showBackupCompleteSheet = SettingsService.get().showBackupCompleteSheet;
|
||||
|
||||
if (context) return path;
|
||||
);
|
||||
if (!backup) throw new Error("Backup returned empty.");
|
||||
} catch (e) {
|
||||
await sleep(300);
|
||||
if (showBackupCompleteSheet) {
|
||||
presentBackupCompleteSheet(backupFilePath);
|
||||
eSendEvent(eCloseSheet);
|
||||
ToastEvent.error(e, "Backup failed!");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
let backupName = "notesnook_backup_" + Date.now();
|
||||
backupName =
|
||||
sanitizeFilename(backupName, { replacement: "_" }) + ".nnbackup";
|
||||
let path;
|
||||
let backupFilePath;
|
||||
|
||||
if (Platform.OS === "ios") {
|
||||
path = await storage.checkAndCreateDir("/backups/");
|
||||
await RNFetchBlob.fs.writeFile(path + backupName, backup, "utf8");
|
||||
backupFilePath = path + backupName;
|
||||
} else {
|
||||
progress && eSendEvent(eCloseSheet);
|
||||
backupFilePath = await ScopedStorage.writeFile(
|
||||
androidBackupDirectory.uri,
|
||||
backup,
|
||||
backupName,
|
||||
"nnbackup/json",
|
||||
"utf8",
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
updateNextBackupTime();
|
||||
|
||||
ToastEvent.show({
|
||||
heading: "Backup successful",
|
||||
message: "Your backup is stored in Notesnook folder on your phone.",
|
||||
@@ -230,10 +207,18 @@ async function run(progress, context) {
|
||||
context: "global"
|
||||
});
|
||||
|
||||
return path;
|
||||
} catch (e) {
|
||||
let showBackupCompleteSheet = SettingsService.get().showBackupCompleteSheet;
|
||||
|
||||
if (context) return backupFilePath;
|
||||
await sleep(300);
|
||||
eSendEvent(eCloseSheet);
|
||||
if (showBackupCompleteSheet) {
|
||||
presentBackupCompleteSheet(backupFilePath);
|
||||
} else {
|
||||
progress && eSendEvent(eCloseSheet);
|
||||
}
|
||||
return backupFilePath;
|
||||
} catch (e) {
|
||||
progress && eSendEvent(eCloseSheet);
|
||||
ToastEvent.error(e, "Backup failed!");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ export function setRateAppMessage() {
|
||||
|
||||
const recoveryKeyMessage = {
|
||||
visible: true,
|
||||
message: "Keep your data safe",
|
||||
message: "Keep your data safe if you lose password",
|
||||
actionText: "Save your account recovery key",
|
||||
onPress: () => {
|
||||
verifyUser(
|
||||
|
||||
@@ -362,7 +362,7 @@ const features_list = [
|
||||
content: "Instantly sync to unlimited devices"
|
||||
},
|
||||
{
|
||||
content: "A private vault to keep everything important always locked"
|
||||
content: "A private vault to keep everything imporant always locked"
|
||||
},
|
||||
{
|
||||
content:
|
||||
@@ -382,7 +382,7 @@ const sheet = (context, promo, trial) => {
|
||||
centered
|
||||
title="Upgrade to Notesnook"
|
||||
titlePart="Pro"
|
||||
paragraph="Manage your work on another level, enjoy seamless sync and keep all notes in one place."
|
||||
paragraph="Manage your work on another level, enjoy seemless sync and keep all notes in one place."
|
||||
padding={12}
|
||||
/>
|
||||
<Seperator />
|
||||
|
||||
@@ -17,14 +17,15 @@ 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/dist/common";
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
import { db } from "../common/database";
|
||||
import { DatabaseLogger } from "../common/database/index";
|
||||
import { initAfterSync } from "../stores/index";
|
||||
import { SyncStatus, useUserStore } from "../stores/use-user-store";
|
||||
import BackgroundSync from "./background-sync";
|
||||
import { ToastEvent } from "./event-manager";
|
||||
import SettingsService from "./settings";
|
||||
import BackgroundSync from "./background-sync";
|
||||
|
||||
export const ignoredMessages = [
|
||||
"Sync already running",
|
||||
@@ -32,7 +33,7 @@ export const ignoredMessages = [
|
||||
"WebSocket failed to connect",
|
||||
"Failed to start the HttpConnection before"
|
||||
];
|
||||
let pendingSync = undefined;
|
||||
|
||||
let syncTimer = 0;
|
||||
const run = async (
|
||||
context = "global",
|
||||
@@ -42,18 +43,10 @@ const run = async (
|
||||
lastSyncTime
|
||||
) => {
|
||||
if (useUserStore.getState().syncing) {
|
||||
DatabaseLogger.info("Sync in progress");
|
||||
pendingSync = {
|
||||
full: full
|
||||
};
|
||||
DatabaseLogger.log("Sync in progress");
|
||||
console.log("Sync in progress");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingSync) {
|
||||
pendingSync = undefined;
|
||||
DatabaseLogger.info("Running pending sync...");
|
||||
}
|
||||
|
||||
clearTimeout(syncTimer);
|
||||
syncTimer = setTimeout(async () => {
|
||||
const status = await NetInfo.fetch();
|
||||
@@ -62,54 +55,56 @@ const run = async (
|
||||
if (!status.isInternetReachable) {
|
||||
DatabaseLogger.warn("Internet not reachable");
|
||||
}
|
||||
|
||||
if (
|
||||
!user ||
|
||||
!status.isInternetReachable ||
|
||||
SettingsService.get().disableSync
|
||||
) {
|
||||
initAfterSync();
|
||||
pendingSync = undefined;
|
||||
return onCompleted?.(false);
|
||||
}
|
||||
userstore.setSyncing(true);
|
||||
|
||||
let error = null;
|
||||
|
||||
try {
|
||||
await BackgroundSync.doInBackground(async () => {
|
||||
let res = await BackgroundSync.doInBackground(async () => {
|
||||
try {
|
||||
await db.sync(full, forced, lastSyncTime);
|
||||
return true;
|
||||
} catch (e) {
|
||||
error = e;
|
||||
return e.message;
|
||||
}
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
if (!res) {
|
||||
initAfterSync();
|
||||
userstore.setSyncing(false, SyncStatus.Failed);
|
||||
return onCompleted?.(false);
|
||||
}
|
||||
if (typeof res === "string") throw error;
|
||||
userstore.setSyncing(false);
|
||||
return onCompleted?.(true);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
if (
|
||||
!ignoredMessages.find((message) => e.message?.includes(message)) &&
|
||||
userstore.user &&
|
||||
status.isConnected &&
|
||||
status.isInternetReachable
|
||||
!ignoredMessages.find((im) => e.message?.includes(im)) &&
|
||||
userstore.user
|
||||
) {
|
||||
ToastEvent.error(e, "Sync failed", context);
|
||||
userstore.setSyncing(false, SyncStatus.Failed);
|
||||
if (status.isConnected && status.isInternetReachable) {
|
||||
ToastEvent.error(e, "Sync failed", context);
|
||||
}
|
||||
}
|
||||
|
||||
DatabaseLogger.error(e, "[Client] Failed to sync");
|
||||
onCompleted?.(false);
|
||||
} finally {
|
||||
initAfterSync();
|
||||
userstore.setSyncing(
|
||||
false,
|
||||
error ? SyncStatus.Failed : SyncStatus.Passed
|
||||
);
|
||||
onCompleted?.(error ? SyncStatus.Failed : SyncStatus.Passed);
|
||||
setImmediate(() => {
|
||||
if (pendingSync) Sync.run("global", false, pendingSync.full);
|
||||
});
|
||||
if (full || forced) {
|
||||
db.eventManager.publish(EVENTS.syncCompleted);
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
};
|
||||
|
||||
@@ -215,7 +215,7 @@ const placeholderTips = [
|
||||
"If you read someone else's diary, you get what you deserve. - David Sedaris",
|
||||
"Take quick notes from notifications. Enable the option in Settings to try",
|
||||
"Get Notesnook on all your devices. Or even open it in browser by going to https://app.notesnook.com to access all your notes",
|
||||
"With note history, you can restore back to an older version of the note if you accidentally deleted something.",
|
||||
"With note history, you can restore back to an older version of the note if you accidently deleted something.",
|
||||
"When your heart speaks, take good notes. - Judith Campbell",
|
||||
"You can publish a note and share it with anyone. Even if they don't use Notesnook!",
|
||||
"Published notes can be encrypted. Which means only you and the person you share the password with can read them.",
|
||||
|
||||
@@ -20,16 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import create from "zustand";
|
||||
import { editorController } from "../screens/editor/tiptap/utils";
|
||||
|
||||
export type AttachmentGroupProgress = {
|
||||
total: number;
|
||||
current: number;
|
||||
groupId: string;
|
||||
filename: string;
|
||||
canceled?: boolean;
|
||||
success?: boolean;
|
||||
error?: any;
|
||||
};
|
||||
|
||||
interface AttachmentStore {
|
||||
progress?: {
|
||||
[name: string]: {
|
||||
@@ -40,7 +30,6 @@ interface AttachmentStore {
|
||||
type: "upload" | "download";
|
||||
} | null;
|
||||
};
|
||||
|
||||
encryptionProgress: number;
|
||||
setEncryptionProgress: (encryptionProgress: number) => void;
|
||||
remove: (hash: string) => void;
|
||||
@@ -51,14 +40,8 @@ interface AttachmentStore {
|
||||
recieved: number,
|
||||
type: "upload" | "download"
|
||||
) => void;
|
||||
downloading?: {
|
||||
[groupId: string]: AttachmentGroupProgress | undefined;
|
||||
};
|
||||
setDownloading: (data: AttachmentGroupProgress) => void;
|
||||
uploading?: {
|
||||
[groupId: string]: AttachmentGroupProgress | undefined;
|
||||
};
|
||||
setUploading: (data: AttachmentGroupProgress) => void;
|
||||
loading: { total: number; current: number };
|
||||
setLoading: (data: { total: number; current: number }) => void;
|
||||
}
|
||||
|
||||
export const useAttachmentStore = create<AttachmentStore>((set, get) => ({
|
||||
@@ -90,21 +73,6 @@ export const useAttachmentStore = create<AttachmentStore>((set, get) => ({
|
||||
encryptionProgress: 0,
|
||||
setEncryptionProgress: (encryptionProgress) =>
|
||||
set({ encryptionProgress: encryptionProgress }),
|
||||
|
||||
downloading: {},
|
||||
setDownloading: (data) =>
|
||||
set({
|
||||
downloading: {
|
||||
...get().downloading,
|
||||
[data.groupId]: data?.canceled ? undefined : data
|
||||
}
|
||||
}),
|
||||
uploading: {},
|
||||
setUploading: (data) =>
|
||||
set({
|
||||
uploading: {
|
||||
...get().uploading,
|
||||
[data.groupId]: data?.canceled ? undefined : data
|
||||
}
|
||||
})
|
||||
loading: { total: 0, current: 0 },
|
||||
setLoading: (data) => set({ loading: data ? { ...data } : data })
|
||||
}));
|
||||
|
||||
@@ -56,7 +56,7 @@ export const useSelectionStore = create<SelectionStore>((set, get) => ({
|
||||
|
||||
set({
|
||||
selectedItemsList: selectedItems,
|
||||
selectionMode: selectedItems.length === 0 ? false : get().selectionMode
|
||||
selectionMode: get().selectionMode
|
||||
});
|
||||
},
|
||||
clearSelection: () => {
|
||||
|
||||
@@ -107,43 +107,46 @@ export const deleteItems = async (item, context) => {
|
||||
|
||||
if (topics?.length > 0) {
|
||||
const result = await confirmDeleteAllNotes(topics, "topic", context);
|
||||
if (!result.delete) return;
|
||||
for (const topic of topics) {
|
||||
if (result.deleteNotes) {
|
||||
const notes = db.notebooks
|
||||
.notebook(topic.notebookId)
|
||||
.topics.topic(topic.id).all;
|
||||
await db.notes.delete(...notes.map((note) => note.id));
|
||||
}
|
||||
await db.notebooks.notebook(topic.notebookId).topics.delete(topic.id);
|
||||
}
|
||||
useMenuStore.getState().setMenuPins();
|
||||
ToastEvent.show({
|
||||
heading: `${topics.length > 1 ? "Topics" : "Topic"} deleted`,
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
|
||||
if (notebooks?.length > 0) {
|
||||
const result = await confirmDeleteAllNotes(notebooks, "notebook", context);
|
||||
if (!result.delete) return;
|
||||
let ids = notebooks.map((i) => i.id);
|
||||
if (result.deleteNotes) {
|
||||
for (let id of ids) {
|
||||
const notebook = db.notebooks.notebook(id);
|
||||
const topics = notebook.topics.all;
|
||||
for (let topic of topics) {
|
||||
if (result.delete) {
|
||||
for (const topic of topics) {
|
||||
if (result.deleteNotes) {
|
||||
const notes = db.notebooks
|
||||
.notebook(topic.notebookId)
|
||||
.topics.topic(topic.id).all;
|
||||
await db.notes.delete(...notes.map((note) => note.id));
|
||||
}
|
||||
const notes = db.relations.from(notebook.data, "note");
|
||||
await db.notes.delete(...notes.map((note) => note.id));
|
||||
await db.notebooks.notebook(topic.notebookId).topics.delete(topic.id);
|
||||
}
|
||||
useMenuStore.getState().setMenuPins();
|
||||
ToastEvent.show({
|
||||
heading: `${topics.length > 1 ? "Topics" : "Topic"} deleted`,
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (notebooks?.length > 0) {
|
||||
const result = await confirmDeleteAllNotes(notebooks, "notebook", context);
|
||||
|
||||
if (result.delete) {
|
||||
let ids = notebooks.map((i) => i.id);
|
||||
if (result.deleteNotes) {
|
||||
for (let id of ids) {
|
||||
const notebook = db.notebooks.notebook(id);
|
||||
const topics = notebook.topics.all;
|
||||
for (let topic of topics) {
|
||||
const notes = db.notebooks
|
||||
.notebook(topic.notebookId)
|
||||
.topics.topic(topic.id).all;
|
||||
await db.notes.delete(...notes.map((note) => note.id));
|
||||
}
|
||||
const notes = db.relations.from(notebook.data, "note");
|
||||
await db.notes.delete(...notes.map((note) => note.id));
|
||||
}
|
||||
}
|
||||
await db.notebooks.delete(...ids);
|
||||
useMenuStore.getState().setMenuPins();
|
||||
}
|
||||
await db.notebooks.delete(...ids);
|
||||
useMenuStore.getState().setMenuPins();
|
||||
}
|
||||
|
||||
Navigation.queueRoutesForUpdate();
|
||||
|
||||
@@ -24,12 +24,6 @@ import RNFetchBlob from "react-native-blob-util";
|
||||
import { db } from "../common/database";
|
||||
import { IOS_APPGROUPID } from "./constants";
|
||||
|
||||
const santizeUri = (uri) => {
|
||||
uri = decodeURI(uri);
|
||||
uri = Platform.OS === "ios" ? uri.replace("file:///", "/") : uri;
|
||||
return uri;
|
||||
};
|
||||
|
||||
export async function attachFile(uri, hash, type, filename, options) {
|
||||
try {
|
||||
let exists = db.attachments.exists(hash);
|
||||
@@ -82,7 +76,9 @@ async function createNotes(bundle) {
|
||||
|
||||
for (const file of bundle.files) {
|
||||
const uri =
|
||||
Platform.OS === "ios" ? santizeUri(file.value) : `${file.value}`;
|
||||
Platform.OS === "ios"
|
||||
? `${file.value.replace("file://", "")}`
|
||||
: `${file.value}`;
|
||||
const hash = await Sodium.hashFile({
|
||||
uri: uri,
|
||||
type: "cache"
|
||||
|
||||
@@ -30,10 +30,36 @@ import {
|
||||
elementByText,
|
||||
sleep,
|
||||
notVisibleByText,
|
||||
visibleById,
|
||||
createNotebook
|
||||
visibleById
|
||||
} from "./utils";
|
||||
|
||||
async function createNotebook(
|
||||
title = "Notebook 1",
|
||||
description = true,
|
||||
topic = true,
|
||||
topicCount = 1
|
||||
) {
|
||||
await tapByText("Add your first notebook");
|
||||
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
|
||||
title
|
||||
);
|
||||
if (description) {
|
||||
await elementById(
|
||||
notesnook.ids.dialogs.notebook.inputs.description
|
||||
).typeText(`Description of ${title}`);
|
||||
}
|
||||
if (topic) {
|
||||
for (let i = 1; i <= topicCount; i++) {
|
||||
await elementById(notesnook.ids.dialogs.notebook.inputs.topic).typeText(
|
||||
i === 1 ? "Topic" : "Topic " + i
|
||||
);
|
||||
await tapById("topic-add-button");
|
||||
}
|
||||
}
|
||||
await tapByText("Save");
|
||||
await sleep(500);
|
||||
}
|
||||
|
||||
// async function addTopic(title = "Topic") {
|
||||
// await tapById(notesnook.buttons.add);
|
||||
// await elementById("input-title").typeText(title);
|
||||
|
||||
@@ -25,9 +25,7 @@ import {
|
||||
prepare,
|
||||
tapByText,
|
||||
notVisibleByText,
|
||||
sleep,
|
||||
navigate,
|
||||
createNotebook
|
||||
sleep
|
||||
} from "./utils";
|
||||
|
||||
async function sortBy(sorting, elementText = "Default") {
|
||||
@@ -106,23 +104,6 @@ describe("Sort & filter", () => {
|
||||
await visibleByText("Month");
|
||||
});
|
||||
|
||||
it("Sort notes in topic", async () => {
|
||||
await prepare();
|
||||
await navigate("Notebooks");
|
||||
await sleep(500);
|
||||
await createNotebook("Notebook 1", true, true);
|
||||
await sleep(500);
|
||||
await device.pressBack();
|
||||
await sleep(500);
|
||||
await tapByText("Topic");
|
||||
await createNote("A", "A letter");
|
||||
await sleep(500);
|
||||
await createNote("B", "B letter");
|
||||
await sortBy("Abc");
|
||||
await sleep(300);
|
||||
await visibleByText("N");
|
||||
});
|
||||
|
||||
it("Compact mode", async () => {
|
||||
await prepare();
|
||||
await createNote("Note 1", "Note 1");
|
||||
|
||||
@@ -129,36 +129,8 @@ async function matchSnapshot(element, name) {
|
||||
});
|
||||
}
|
||||
|
||||
async function createNotebook(
|
||||
title = "Notebook 1",
|
||||
description = true,
|
||||
topic = true,
|
||||
topicCount = 1
|
||||
) {
|
||||
await tapByText("Add your first notebook");
|
||||
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
|
||||
title
|
||||
);
|
||||
if (description) {
|
||||
await elementById(
|
||||
notesnook.ids.dialogs.notebook.inputs.description
|
||||
).typeText(`Description of ${title}`);
|
||||
}
|
||||
if (topic) {
|
||||
for (let i = 1; i <= topicCount; i++) {
|
||||
await elementById(notesnook.ids.dialogs.notebook.inputs.topic).typeText(
|
||||
i === 1 ? "Topic" : "Topic " + i
|
||||
);
|
||||
await tapById("topic-add-button");
|
||||
}
|
||||
}
|
||||
await tapByText("Save");
|
||||
await sleep(500);
|
||||
}
|
||||
|
||||
export {
|
||||
matchSnapshot,
|
||||
createNotebook,
|
||||
prepare,
|
||||
LaunchApp,
|
||||
createNote,
|
||||
|
||||
@@ -111,7 +111,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 2065
|
||||
versionCode 2059
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
- Bug fixes and performance improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
- Much improved and faster sync
|
||||
- Note pinned in notifications will update automatically when content changes
|
||||
- Quick notes from notifications now respect paragraph spacing settings.
|
||||
- Many bug fixes and small improvements
|
||||
Binary file not shown.
@@ -997,7 +997,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2058;
|
||||
CURRENT_PROJECT_VERSION = 2053;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1071,7 +1071,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.7;
|
||||
MARKETING_VERSION = 2.6.3;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -1102,7 +1102,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2058;
|
||||
CURRENT_PROJECT_VERSION = 2053;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1176,7 +1176,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.7;
|
||||
MARKETING_VERSION = 2.6.3;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1335,7 +1335,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2058;
|
||||
CURRENT_PROJECT_VERSION = 2053;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1347,7 +1347,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.7;
|
||||
MARKETING_VERSION = 2.6.3;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
@@ -1378,7 +1378,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2058;
|
||||
CURRENT_PROJECT_VERSION = 2053;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1391,7 +1391,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.7;
|
||||
MARKETING_VERSION = 2.6.3;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1421,7 +1421,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2058;
|
||||
CURRENT_PROJECT_VERSION = 2053;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1495,7 +1495,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.7;
|
||||
MARKETING_VERSION = 2.6.3;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
@@ -1526,7 +1526,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2058;
|
||||
CURRENT_PROJECT_VERSION = 2053;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1601,7 +1601,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.7;
|
||||
MARKETING_VERSION = 2.6.3;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
@@ -327,7 +327,7 @@ PODS:
|
||||
- React-Core
|
||||
- react-native-keep-awake (1.2.0):
|
||||
- React-Core
|
||||
- react-native-mmkv-storage (0.10.0-alpha.9):
|
||||
- react-native-mmkv-storage (0.10.0-alpha.7):
|
||||
- MMKV (~> 1.3.1)
|
||||
- React
|
||||
- React-Core
|
||||
@@ -347,7 +347,7 @@ PODS:
|
||||
- ReactCommon/turbomodule/core
|
||||
- react-native-share-extension (2.5.6):
|
||||
- React
|
||||
- react-native-sodium (1.5.4):
|
||||
- react-native-sodium (1.5.2):
|
||||
- React
|
||||
- react-native-webview (11.26.1):
|
||||
- React-Core
|
||||
@@ -875,14 +875,14 @@ SPEC CHECKSUMS:
|
||||
react-native-image-resizer: 00ceb0e05586c7aadf061eea676957a6c2ec60fa
|
||||
react-native-in-app-review: db8bb167a5f238e7ceca5c242d6b36ce8c4404a4
|
||||
react-native-keep-awake: caee3ff89eaa21dfe29010f0d143566874a04441
|
||||
react-native-mmkv-storage: d4ad55ab411b7f0d4e6269d801b31821ef48970b
|
||||
react-native-mmkv-storage: ec32fd68168716755e1b30f09b476c236c6c68a8
|
||||
react-native-netinfo: ccbe1085dffd16592791d550189772e13bf479e2
|
||||
react-native-notification-sounds: da78c828fe1bcbb92d8b505d5261890ed315ff39
|
||||
react-native-orientation: f1caf84d65f1a4fd4511a18f2b924e634ad7a628
|
||||
react-native-pdf: 33c622cbdf776a649929e8b9d1ce2d313347c4fa
|
||||
react-native-safe-area-context: 36cc67648134e89465663b8172336a19eeda493d
|
||||
react-native-share-extension: df66a2ee48a62277d79898375e2142bde0782063
|
||||
react-native-sodium: 955bb0dc3ea05f8ea06d5e96cb89d1be7b5d7681
|
||||
react-native-sodium: 7d2bbb9ca840dc58d63f661b2ff733a38170fec5
|
||||
react-native-webview: 9f111dfbcfc826084d6c507f569e5e03342ee1c1
|
||||
React-NativeModulesApple: 1d81d927ef1a67a3545a01e14c2e98500bf9b199
|
||||
React-perflogger: 684a11499a0589cc42135d6d5cc04d0e4e0e261a
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -21,12 +21,6 @@
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
background-color: transparent !important;
|
||||
height: unset !important;
|
||||
}
|
||||
|
||||
.editor {
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"dependencies": {
|
||||
"@ammarahmed/notifee-react-native": "7.4.4",
|
||||
"@ammarahmed/react-native-share-extension": "^2.5.5",
|
||||
"@ammarahmed/react-native-sodium": "1.5.4",
|
||||
"@ammarahmed/react-native-sodium": "1.5.2",
|
||||
"@bam.tech/react-native-image-resizer": "3.0.5",
|
||||
"@callstack/repack": "^3.2.0",
|
||||
"@react-native-clipboard/clipboard": "^1.9.0",
|
||||
|
||||
@@ -2087,7 +2087,7 @@ function getEscaper(regex, map) {
|
||||
if (lastIdx !== match.index) {
|
||||
result += data.substring(lastIdx, match.index);
|
||||
}
|
||||
// We know that this character will be in the map.
|
||||
// We know that this chararcter will be in the map.
|
||||
result += map.get(match[0].charCodeAt(0));
|
||||
// Every match will be of length 1
|
||||
lastIdx = match.index + 1;
|
||||
@@ -2996,7 +2996,7 @@ function removeSubsets$1(nodes) {
|
||||
/*
|
||||
* Remove the node if it is not unique.
|
||||
* We are going through the array from the end, so we only
|
||||
* have to check nodes that precede the node under consideration in the array.
|
||||
* have to check nodes that preceed the node under consideration in the array.
|
||||
*/
|
||||
if (idx > 0 && nodes.lastIndexOf(node, idx - 1) >= 0) {
|
||||
nodes.splice(idx, 1);
|
||||
@@ -9883,7 +9883,7 @@ CSSOM$2.CSSValueExpression.prototype._findMatchedIdx = function (token, idx, sep
|
||||
}
|
||||
}
|
||||
|
||||
// boundary must be in the same line(js string or regexp)
|
||||
// boundary must be in the same line(js sting or regexp)
|
||||
var nextNewLineIdx = token.indexOf('\n', idx + 1);
|
||||
if (nextNewLineIdx < endIdx) {
|
||||
endIdx = NOT_FOUND;
|
||||
|
||||
184
apps/mobile/package-lock.json
generated
184
apps/mobile/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "2.6.7",
|
||||
"version": "2.6.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "2.6.7",
|
||||
"version": "2.6.2",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
@@ -22,9 +22,9 @@
|
||||
"@notesnook/logger": "file:../../packages/logger",
|
||||
"@notesnook/themes-server": "file:../../servers/themes",
|
||||
"@tanstack/react-query": "^4.29.19",
|
||||
"@trpc/client": "10.38.3",
|
||||
"@trpc/react-query": "10.38.3",
|
||||
"@trpc/server": "10.38.3",
|
||||
"@trpc/client": "10.31.0",
|
||||
"@trpc/react-query": "10.31.0",
|
||||
"@trpc/server": "^10.31.0",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.72.0"
|
||||
},
|
||||
@@ -85,9 +85,7 @@
|
||||
"abortcontroller-polyfill": "^1.7.3",
|
||||
"cross-env": "^7.0.3",
|
||||
"dotenv": "^16.0.1",
|
||||
"event-source-polyfill": "^1.0.31",
|
||||
"eventsource": "^2.0.2",
|
||||
"hash-wasm": "^4.9.0",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"jsdom": "^22.1.0",
|
||||
"mockdate": "^3.0.5",
|
||||
@@ -110,28 +108,28 @@
|
||||
"@social-embed/lib": "^0.0.2-next.1",
|
||||
"@theme-ui/components": ">=0.14.0",
|
||||
"@theme-ui/core": ">=0.14.0",
|
||||
"@tiptap/core": "2.1.11",
|
||||
"@tiptap/extension-character-count": "2.1.11",
|
||||
"@tiptap/extension-color": "2.1.11",
|
||||
"@tiptap/extension-font-family": "2.1.11",
|
||||
"@tiptap/extension-history": "2.1.11",
|
||||
"@tiptap/extension-horizontal-rule": "2.1.11",
|
||||
"@tiptap/extension-link": "2.1.11",
|
||||
"@tiptap/extension-list-keymap": "2.1.11",
|
||||
"@tiptap/extension-placeholder": "2.1.11",
|
||||
"@tiptap/extension-subscript": "2.1.11",
|
||||
"@tiptap/extension-superscript": "2.1.11",
|
||||
"@tiptap/extension-table": "2.1.11",
|
||||
"@tiptap/extension-table-cell": "2.1.11",
|
||||
"@tiptap/extension-table-header": "2.1.11",
|
||||
"@tiptap/extension-table-row": "2.1.11",
|
||||
"@tiptap/extension-task-item": "2.1.11",
|
||||
"@tiptap/extension-task-list": "2.1.11",
|
||||
"@tiptap/extension-text-align": "2.1.11",
|
||||
"@tiptap/extension-text-style": "2.1.11",
|
||||
"@tiptap/extension-underline": "2.1.11",
|
||||
"@tiptap/pm": "2.1.11",
|
||||
"@tiptap/starter-kit": "2.1.11",
|
||||
"@tiptap/core": "2.1.7",
|
||||
"@tiptap/extension-character-count": "2.1.7",
|
||||
"@tiptap/extension-color": "2.1.7",
|
||||
"@tiptap/extension-font-family": "2.1.7",
|
||||
"@tiptap/extension-history": "2.1.7",
|
||||
"@tiptap/extension-horizontal-rule": "2.1.7",
|
||||
"@tiptap/extension-link": "2.1.7",
|
||||
"@tiptap/extension-list-keymap": "2.1.7",
|
||||
"@tiptap/extension-placeholder": "2.1.7",
|
||||
"@tiptap/extension-subscript": "2.1.7",
|
||||
"@tiptap/extension-superscript": "2.1.7",
|
||||
"@tiptap/extension-table": "2.1.7",
|
||||
"@tiptap/extension-table-cell": "2.1.7",
|
||||
"@tiptap/extension-table-header": "2.1.7",
|
||||
"@tiptap/extension-table-row": "2.1.7",
|
||||
"@tiptap/extension-task-item": "2.1.7",
|
||||
"@tiptap/extension-task-list": "2.1.7",
|
||||
"@tiptap/extension-text-align": "2.1.7",
|
||||
"@tiptap/extension-text-style": "2.1.7",
|
||||
"@tiptap/extension-underline": "2.1.7",
|
||||
"@tiptap/pm": "2.1.7",
|
||||
"@tiptap/starter-kit": "2.1.7",
|
||||
"clipboard-polyfill": "4.0.0",
|
||||
"detect-indent": "^7.0.0",
|
||||
"entities": "^4.5.0",
|
||||
@@ -139,7 +137,7 @@
|
||||
"nanoid": "^4.0.1",
|
||||
"prism-themes": "^1.9.0",
|
||||
"prosemirror-codemark": "^0.4.2",
|
||||
"prosemirror-view": "^1.31.8",
|
||||
"prosemirror-view": "^1.31.7",
|
||||
"re-resizable": "^6.9.9",
|
||||
"react-colorful": "^5.5.1",
|
||||
"react-modal": "3.13.1",
|
||||
@@ -224,7 +222,7 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@orama/orama": "^1.0.8",
|
||||
"@trpc/server": "10.38.3",
|
||||
"@trpc/server": "10.31.0",
|
||||
"async-mutex": "^0.4.0",
|
||||
"cors": "^2.8.5",
|
||||
"util": "^0.12.5",
|
||||
@@ -256,7 +254,7 @@
|
||||
"katex": "0.16.2",
|
||||
"phone": "^3.1.14",
|
||||
"qclone": "^1.2.0",
|
||||
"react-native-actions-sheet": "0.9.0-alpha.24",
|
||||
"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",
|
||||
@@ -281,7 +279,7 @@
|
||||
"@ammarahmed/notifee-react-native": "7.4.4",
|
||||
"@ammarahmed/react-native-eventsource": "1.1.0",
|
||||
"@ammarahmed/react-native-share-extension": "^2.5.5",
|
||||
"@ammarahmed/react-native-sodium": "1.5.4",
|
||||
"@ammarahmed/react-native-sodium": "1.5.2",
|
||||
"@bam.tech/react-native-image-resizer": "3.0.5",
|
||||
"@callstack/repack": "^3.2.0",
|
||||
"@react-native-clipboard/clipboard": "^1.9.0",
|
||||
@@ -2419,9 +2417,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@ammarahmed/react-native-sodium": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-sodium/-/react-native-sodium-1.5.4.tgz",
|
||||
"integrity": "sha512-+JwYnaSJxnwpI1947sUacFXxRxK1f9ykuxDJaGkCUKvh5CkDYC5VyZFnq2BbcuIcx4OTMOG8a/a46hWJbFKQPQ=="
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-sodium/-/react-native-sodium-1.5.2.tgz",
|
||||
"integrity": "sha512-M309uV2DCK3EPNfsgWYthOApJhQgQKOOxYF19VerUeX7UYnm9VggW073oO0NNqqZqg5GS6bTlrtYMAz7MAKvUw=="
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
"version": "2.2.1",
|
||||
@@ -7014,35 +7012,35 @@
|
||||
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
|
||||
},
|
||||
"node_modules/@trpc/client": {
|
||||
"version": "10.38.3",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/client/-/client-10.38.3.tgz",
|
||||
"integrity": "sha512-hHPsElTL4sB+UGzuAZ4iwRVHjELrk/Nsl2owsYw9ITJz3EY7VWRsQ6vK48kJvlHPYPhP+efID5UOgyZMs4bFXA==",
|
||||
"version": "10.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/client/-/client-10.31.0.tgz",
|
||||
"integrity": "sha512-VCqbJEvFJb8C4hQFw7AD+dkQTjgEdV/QAzO4D+/cX5e93u5NpfNXI+PKS0QFXwG/zqgwQwVV6OkYc/D/MFwA6g==",
|
||||
"funding": [
|
||||
"https://trpc.io/sponsor"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"@trpc/server": "10.38.3"
|
||||
"@trpc/server": "10.31.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@trpc/react-query": {
|
||||
"version": "10.38.3",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/react-query/-/react-query-10.38.3.tgz",
|
||||
"integrity": "sha512-j4xxg/y+vssGinZCrNQy0SMJNL2Nv8k3HcqMKa2mYt3jxOpaHuwxgarWvLWVqrUJqvp1JpGJ9sIjlOAZam5FeQ==",
|
||||
"version": "10.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/react-query/-/react-query-10.31.0.tgz",
|
||||
"integrity": "sha512-+M8sIsbf6e4H5XYvHlzDqhaf+ybfUigA/9OL3wXRp2vXhCedEiIERCnwNuHWFDRASl9vjOcM33AuJ4sbOOINEA==",
|
||||
"funding": [
|
||||
"https://trpc.io/sponsor"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"@tanstack/react-query": "^4.18.0",
|
||||
"@trpc/client": "10.38.3",
|
||||
"@trpc/server": "10.38.3",
|
||||
"@trpc/client": "10.31.0",
|
||||
"@trpc/server": "10.31.0",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@trpc/server": {
|
||||
"version": "10.38.3",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.38.3.tgz",
|
||||
"integrity": "sha512-9s8/kwo2IDB5hwB2SKZZrfevRhdb1f9fdXtIYd3lbQuf2jQaC/LyQuHaIQjDQoUx9updBfsHXcFFPiCP1DL6pg==",
|
||||
"version": "10.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.31.0.tgz",
|
||||
"integrity": "sha512-9EnRTSDE9nF11LZsvSOqNKqkRYzHqFX4ch5AJ6VIu8uta2vxVTN4FxxsNRSOluTzVYZDeaCISbwmOJ5iihCCIg==",
|
||||
"funding": [
|
||||
"https://trpc.io/sponsor"
|
||||
]
|
||||
@@ -17921,9 +17919,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-actions-sheet": {
|
||||
"version": "0.9.0-alpha.24",
|
||||
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.24.tgz",
|
||||
"integrity": "sha512-NF0oO0vf3DrFsqI+pbQ+HTG2q+VEotkVwhtFr8aLiUFJT876icVOSurCaeqD1t4zHtH2U5qa8vKQxbA/j5Mu8w==",
|
||||
"version": "0.9.0-alpha.23",
|
||||
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.23.tgz",
|
||||
"integrity": "sha512-d8sZJHKjme33i34zQ8+oIsGs8wY+GCDtxDboExgpO1OoB9np1y4tGOf3ZHyfIpClITpuy+LT7r9Uz+KfAJTMHA==",
|
||||
"peerDependencies": {
|
||||
"react-native": "*",
|
||||
"react-native-gesture-handler": "*"
|
||||
@@ -23703,9 +23701,9 @@
|
||||
}
|
||||
},
|
||||
"@ammarahmed/react-native-sodium": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-sodium/-/react-native-sodium-1.5.4.tgz",
|
||||
"integrity": "sha512-+JwYnaSJxnwpI1947sUacFXxRxK1f9ykuxDJaGkCUKvh5CkDYC5VyZFnq2BbcuIcx4OTMOG8a/a46hWJbFKQPQ=="
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@ammarahmed/react-native-sodium/-/react-native-sodium-1.5.2.tgz",
|
||||
"integrity": "sha512-M309uV2DCK3EPNfsgWYthOApJhQgQKOOxYF19VerUeX7UYnm9VggW073oO0NNqqZqg5GS6bTlrtYMAz7MAKvUw=="
|
||||
},
|
||||
"@ampproject/remapping": {
|
||||
"version": "2.2.1",
|
||||
@@ -26440,9 +26438,7 @@
|
||||
"dayjs": "1.11.9",
|
||||
"dotenv": "^16.0.1",
|
||||
"entities": "^4.3.1",
|
||||
"event-source-polyfill": "^1.0.31",
|
||||
"eventsource": "^2.0.2",
|
||||
"hash-wasm": "^4.9.0",
|
||||
"html-to-text": "^9.0.5",
|
||||
"htmlparser2": "^8.0.1",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
@@ -26473,28 +26469,28 @@
|
||||
"@social-embed/lib": "^0.0.2-next.1",
|
||||
"@theme-ui/components": ">=0.14.0",
|
||||
"@theme-ui/core": ">=0.14.0",
|
||||
"@tiptap/core": "2.1.11",
|
||||
"@tiptap/extension-character-count": "2.1.11",
|
||||
"@tiptap/extension-color": "2.1.11",
|
||||
"@tiptap/extension-font-family": "2.1.11",
|
||||
"@tiptap/extension-history": "2.1.11",
|
||||
"@tiptap/extension-horizontal-rule": "2.1.11",
|
||||
"@tiptap/extension-link": "2.1.11",
|
||||
"@tiptap/extension-list-keymap": "2.1.11",
|
||||
"@tiptap/extension-placeholder": "2.1.11",
|
||||
"@tiptap/extension-subscript": "2.1.11",
|
||||
"@tiptap/extension-superscript": "2.1.11",
|
||||
"@tiptap/extension-table": "2.1.11",
|
||||
"@tiptap/extension-table-cell": "2.1.11",
|
||||
"@tiptap/extension-table-header": "2.1.11",
|
||||
"@tiptap/extension-table-row": "2.1.11",
|
||||
"@tiptap/extension-task-item": "2.1.11",
|
||||
"@tiptap/extension-task-list": "2.1.11",
|
||||
"@tiptap/extension-text-align": "2.1.11",
|
||||
"@tiptap/extension-text-style": "2.1.11",
|
||||
"@tiptap/extension-underline": "2.1.11",
|
||||
"@tiptap/pm": "2.1.11",
|
||||
"@tiptap/starter-kit": "2.1.11",
|
||||
"@tiptap/core": "2.1.7",
|
||||
"@tiptap/extension-character-count": "2.1.7",
|
||||
"@tiptap/extension-color": "2.1.7",
|
||||
"@tiptap/extension-font-family": "2.1.7",
|
||||
"@tiptap/extension-history": "2.1.7",
|
||||
"@tiptap/extension-horizontal-rule": "2.1.7",
|
||||
"@tiptap/extension-link": "2.1.7",
|
||||
"@tiptap/extension-list-keymap": "2.1.7",
|
||||
"@tiptap/extension-placeholder": "2.1.7",
|
||||
"@tiptap/extension-subscript": "2.1.7",
|
||||
"@tiptap/extension-superscript": "2.1.7",
|
||||
"@tiptap/extension-table": "2.1.7",
|
||||
"@tiptap/extension-table-cell": "2.1.7",
|
||||
"@tiptap/extension-table-header": "2.1.7",
|
||||
"@tiptap/extension-table-row": "2.1.7",
|
||||
"@tiptap/extension-task-item": "2.1.7",
|
||||
"@tiptap/extension-task-list": "2.1.7",
|
||||
"@tiptap/extension-text-align": "2.1.7",
|
||||
"@tiptap/extension-text-style": "2.1.7",
|
||||
"@tiptap/extension-underline": "2.1.7",
|
||||
"@tiptap/pm": "2.1.7",
|
||||
"@tiptap/starter-kit": "2.1.7",
|
||||
"@types/katex": "^0.14.0",
|
||||
"@types/prismjs": "^1.26.0",
|
||||
"@types/react": "17.0.2",
|
||||
@@ -26513,7 +26509,7 @@
|
||||
"prism-themes": "^1.9.0",
|
||||
"prosemirror-codemark": "^0.4.2",
|
||||
"prosemirror-test-builder": "^1.1.0",
|
||||
"prosemirror-view": "^1.31.8",
|
||||
"prosemirror-view": "^1.31.7",
|
||||
"re-resizable": "^6.9.9",
|
||||
"react": "17.0.2",
|
||||
"react-colorful": "^5.5.1",
|
||||
@@ -26577,7 +26573,7 @@
|
||||
"katex": "0.16.2",
|
||||
"phone": "^3.1.14",
|
||||
"qclone": "^1.2.0",
|
||||
"react-native-actions-sheet": "0.9.0-alpha.24",
|
||||
"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",
|
||||
@@ -26600,7 +26596,7 @@
|
||||
"@ammarahmed/notifee-react-native": "7.4.4",
|
||||
"@ammarahmed/react-native-eventsource": "1.1.0",
|
||||
"@ammarahmed/react-native-share-extension": "^2.5.5",
|
||||
"@ammarahmed/react-native-sodium": "1.5.4",
|
||||
"@ammarahmed/react-native-sodium": "1.5.2",
|
||||
"@babel/core": "^7.20.0",
|
||||
"@babel/eslint-parser": "^7.16.5",
|
||||
"@babel/plugin-transform-named-capturing-groups-regex": "^7.16.5",
|
||||
@@ -26720,7 +26716,7 @@
|
||||
"requires": {
|
||||
"@notesnook/theme": "file:../../packages/theme",
|
||||
"@orama/orama": "^1.0.8",
|
||||
"@trpc/server": "10.38.3",
|
||||
"@trpc/server": "10.31.0",
|
||||
"@types/cors": "^2.8.13",
|
||||
"@vitejs/plugin-react": "^4.0.3",
|
||||
"async-mutex": "^0.4.0",
|
||||
@@ -27329,21 +27325,21 @@
|
||||
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
|
||||
},
|
||||
"@trpc/client": {
|
||||
"version": "10.38.3",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/client/-/client-10.38.3.tgz",
|
||||
"integrity": "sha512-hHPsElTL4sB+UGzuAZ4iwRVHjELrk/Nsl2owsYw9ITJz3EY7VWRsQ6vK48kJvlHPYPhP+efID5UOgyZMs4bFXA==",
|
||||
"version": "10.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/client/-/client-10.31.0.tgz",
|
||||
"integrity": "sha512-VCqbJEvFJb8C4hQFw7AD+dkQTjgEdV/QAzO4D+/cX5e93u5NpfNXI+PKS0QFXwG/zqgwQwVV6OkYc/D/MFwA6g==",
|
||||
"requires": {}
|
||||
},
|
||||
"@trpc/react-query": {
|
||||
"version": "10.38.3",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/react-query/-/react-query-10.38.3.tgz",
|
||||
"integrity": "sha512-j4xxg/y+vssGinZCrNQy0SMJNL2Nv8k3HcqMKa2mYt3jxOpaHuwxgarWvLWVqrUJqvp1JpGJ9sIjlOAZam5FeQ==",
|
||||
"version": "10.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/react-query/-/react-query-10.31.0.tgz",
|
||||
"integrity": "sha512-+M8sIsbf6e4H5XYvHlzDqhaf+ybfUigA/9OL3wXRp2vXhCedEiIERCnwNuHWFDRASl9vjOcM33AuJ4sbOOINEA==",
|
||||
"requires": {}
|
||||
},
|
||||
"@trpc/server": {
|
||||
"version": "10.38.3",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.38.3.tgz",
|
||||
"integrity": "sha512-9s8/kwo2IDB5hwB2SKZZrfevRhdb1f9fdXtIYd3lbQuf2jQaC/LyQuHaIQjDQoUx9updBfsHXcFFPiCP1DL6pg=="
|
||||
"version": "10.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.31.0.tgz",
|
||||
"integrity": "sha512-9EnRTSDE9nF11LZsvSOqNKqkRYzHqFX4ch5AJ6VIu8uta2vxVTN4FxxsNRSOluTzVYZDeaCISbwmOJ5iihCCIg=="
|
||||
},
|
||||
"@tsconfig/react-native": {
|
||||
"version": "3.0.2",
|
||||
@@ -35563,9 +35559,9 @@
|
||||
}
|
||||
},
|
||||
"react-native-actions-sheet": {
|
||||
"version": "0.9.0-alpha.24",
|
||||
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.24.tgz",
|
||||
"integrity": "sha512-NF0oO0vf3DrFsqI+pbQ+HTG2q+VEotkVwhtFr8aLiUFJT876icVOSurCaeqD1t4zHtH2U5qa8vKQxbA/j5Mu8w==",
|
||||
"version": "0.9.0-alpha.23",
|
||||
"resolved": "https://registry.npmjs.org/react-native-actions-sheet/-/react-native-actions-sheet-0.9.0-alpha.23.tgz",
|
||||
"integrity": "sha512-d8sZJHKjme33i34zQ8+oIsGs8wY+GCDtxDboExgpO1OoB9np1y4tGOf3ZHyfIpClITpuy+LT7r9Uz+KfAJTMHA==",
|
||||
"requires": {}
|
||||
},
|
||||
"react-native-actions-shortcuts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "2.6.7",
|
||||
"version": "2.6.3",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
@@ -37,9 +37,9 @@
|
||||
"@notesnook/logger": "file:../../packages/logger",
|
||||
"@notesnook/themes-server": "file:../../servers/themes",
|
||||
"@tanstack/react-query": "^4.29.19",
|
||||
"@trpc/client": "10.38.3",
|
||||
"@trpc/react-query": "10.38.3",
|
||||
"@trpc/server": "10.38.3",
|
||||
"@trpc/client": "10.31.0",
|
||||
"@trpc/react-query": "10.31.0",
|
||||
"@trpc/server": "^10.31.0",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.72.0"
|
||||
}
|
||||
|
||||
@@ -1,19 +1,3 @@
|
||||
diff --git a/node_modules/react-native-blob-util/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFS.java b/node_modules/react-native-blob-util/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFS.java
|
||||
index c75347f..76d9b9e 100644
|
||||
--- a/node_modules/react-native-blob-util/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFS.java
|
||||
+++ b/node_modules/react-native-blob-util/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFS.java
|
||||
@@ -257,9 +257,9 @@ class ReactNativeBlobUtilFS {
|
||||
if (resolved != null && resolved.startsWith(ReactNativeBlobUtilConst.FILE_PREFIX_BUNDLE_ASSET)) {
|
||||
String assetName = path.replace(ReactNativeBlobUtilConst.FILE_PREFIX_BUNDLE_ASSET, "");
|
||||
// This fails should an asset file be >2GB
|
||||
- length = (int) ReactNativeBlobUtilImpl.RCTContext.getAssets().openFd(assetName).getLength();
|
||||
- bytes = new byte[length];
|
||||
InputStream in = ReactNativeBlobUtilImpl.RCTContext.getAssets().open(assetName);
|
||||
+ length = in.available();
|
||||
+ bytes = new byte[length];
|
||||
bytesRead = in.read(bytes, 0, length);
|
||||
in.close();
|
||||
}
|
||||
diff --git a/node_modules/react-native-blob-util/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilReq.java b/node_modules/react-native-blob-util/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilReq.java
|
||||
index 9aee829..0ecc59b 100644
|
||||
--- a/node_modules/react-native-blob-util/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilReq.java
|
||||
@@ -125,7 +109,7 @@ index 97e5263..640aaea 100644
|
||||
}
|
||||
|
||||
diff --git a/node_modules/react-native-blob-util/index.js b/node_modules/react-native-blob-util/index.js
|
||||
index ecaddf9..70d6ba5 100644
|
||||
index ecaddf9..40a5c37 100644
|
||||
--- a/node_modules/react-native-blob-util/index.js
|
||||
+++ b/node_modules/react-native-blob-util/index.js
|
||||
@@ -14,6 +14,7 @@ import ios from './ios';
|
||||
|
||||
@@ -7,8 +7,7 @@ index 0000000..0d259dd
|
||||
+o/classes
|
||||
diff --git a/node_modules/react-native-vector-icons/android/build/.transforms/f1314fc28eaff1170375cbb76465cb24/transformed/classes/classes.dex b/node_modules/react-native-vector-icons/android/build/.transforms/f1314fc28eaff1170375cbb76465cb24/transformed/classes/classes.dex
|
||||
new file mode 100644
|
||||
index 0000000..d4bdd72
|
||||
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/.transforms/f1314fc28eaff1170375cbb76465cb24/transformed/classes/classes.dex differ
|
||||
index 0000000..e69de29
|
||||
diff --git a/node_modules/react-native-vector-icons/android/build/generated/source/buildConfig/debug/com/oblador/vectoricons/BuildConfig.java b/node_modules/react-native-vector-icons/android/build/generated/source/buildConfig/debug/com/oblador/vectoricons/BuildConfig.java
|
||||
new file mode 100644
|
||||
index 0000000..52a02e0
|
||||
@@ -85,12 +84,10 @@ index 0000000..9e26dfe
|
||||
\ No newline at end of file
|
||||
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/compile_library_classes_jar/debug/classes.jar b/node_modules/react-native-vector-icons/android/build/intermediates/compile_library_classes_jar/debug/classes.jar
|
||||
new file mode 100644
|
||||
index 0000000..77c2a69
|
||||
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/intermediates/compile_library_classes_jar/debug/classes.jar differ
|
||||
index 0000000..e69de29
|
||||
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/compile_r_class_jar/debug/R.jar b/node_modules/react-native-vector-icons/android/build/intermediates/compile_r_class_jar/debug/R.jar
|
||||
new file mode 100644
|
||||
index 0000000..bd4434a
|
||||
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/intermediates/compile_r_class_jar/debug/R.jar differ
|
||||
index 0000000..e69de29
|
||||
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/compile_symbol_list/debug/R.txt b/node_modules/react-native-vector-icons/android/build/intermediates/compile_symbol_list/debug/R.txt
|
||||
new file mode 100644
|
||||
index 0000000..5347f17
|
||||
@@ -2053,11 +2050,11 @@ index 0000000..5347f17
|
||||
+int xml rn_dev_preferences 0x0
|
||||
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties b/node_modules/react-native-vector-icons/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties
|
||||
new file mode 100644
|
||||
index 0000000..c671bf3
|
||||
index 0000000..2dd0b63
|
||||
--- /dev/null
|
||||
+++ b/node_modules/react-native-vector-icons/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties
|
||||
@@ -0,0 +1 @@
|
||||
+#Mon Oct 02 09:47:12 PKT 2023
|
||||
+#Wed Aug 02 11:28:45 PKT 2023
|
||||
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml b/node_modules/react-native-vector-icons/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml
|
||||
new file mode 100644
|
||||
index 0000000..08d62cc
|
||||
@@ -2096,20 +2093,16 @@ index 0000000..ef96fe3
|
||||
\ No newline at end of file
|
||||
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/BuildConfig.class b/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/BuildConfig.class
|
||||
new file mode 100644
|
||||
index 0000000..1276d6a
|
||||
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/BuildConfig.class differ
|
||||
index 0000000..e69de29
|
||||
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsModule$Errors.class b/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsModule$Errors.class
|
||||
new file mode 100644
|
||||
index 0000000..3348b06
|
||||
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsModule$Errors.class differ
|
||||
index 0000000..e69de29
|
||||
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsModule.class b/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsModule.class
|
||||
new file mode 100644
|
||||
index 0000000..de0d974
|
||||
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsModule.class differ
|
||||
index 0000000..e69de29
|
||||
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsPackage.class b/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsPackage.class
|
||||
new file mode 100644
|
||||
index 0000000..07a935e
|
||||
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/intermediates/javac/debug/classes/com/oblador/vectoricons/VectorIconsPackage.class differ
|
||||
index 0000000..e69de29
|
||||
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/local_only_symbol_list/debug/R-def.txt b/node_modules/react-native-vector-icons/android/build/intermediates/local_only_symbol_list/debug/R-def.txt
|
||||
new file mode 100644
|
||||
index 0000000..78ac5b8
|
||||
@@ -2181,8 +2174,7 @@ index 0000000..0be2a09
|
||||
\ No newline at end of file
|
||||
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/runtime_library_classes_jar/debug/classes.jar b/node_modules/react-native-vector-icons/android/build/intermediates/runtime_library_classes_jar/debug/classes.jar
|
||||
new file mode 100644
|
||||
index 0000000..f0a7660
|
||||
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/intermediates/runtime_library_classes_jar/debug/classes.jar differ
|
||||
index 0000000..e69de29
|
||||
diff --git a/node_modules/react-native-vector-icons/android/build/intermediates/symbol_list_with_package_name/debug/package-aware-r.txt b/node_modules/react-native-vector-icons/android/build/intermediates/symbol_list_with_package_name/debug/package-aware-r.txt
|
||||
new file mode 100644
|
||||
index 0000000..873e0a9
|
||||
@@ -3669,8 +3661,7 @@ index 0000000..f4f75c1
|
||||
+ INJECTED from /Users/ammarahmed/Repos/apps/notesnook/apps/mobile/node_modules/react-native-vector-icons/android/src/main/AndroidManifest.xml
|
||||
diff --git a/node_modules/react-native-vector-icons/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin b/node_modules/react-native-vector-icons/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin
|
||||
new file mode 100644
|
||||
index 0000000..26da0e3
|
||||
Binary files /dev/null and b/node_modules/react-native-vector-icons/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin differ
|
||||
index 0000000..e69de29
|
||||
diff --git a/node_modules/react-native-vector-icons/fonts.gradle b/node_modules/react-native-vector-icons/fonts.gradle
|
||||
index 5485fac..ef3ccb3 100644
|
||||
--- a/node_modules/react-native-vector-icons/fonts.gradle
|
||||
@@ -3687,7 +3678,7 @@ index 5485fac..ef3ccb3 100644
|
||||
generateAssetsTask.dependsOn(fontCopyTask)
|
||||
}
|
||||
diff --git a/node_modules/react-native-vector-icons/glyphmaps/MaterialCommunityIcons.json b/node_modules/react-native-vector-icons/glyphmaps/MaterialCommunityIcons.json
|
||||
index c06a819..13c76cc 100644
|
||||
index c06a819..0962de1 100644
|
||||
--- a/node_modules/react-native-vector-icons/glyphmaps/MaterialCommunityIcons.json
|
||||
+++ b/node_modules/react-native-vector-icons/glyphmaps/MaterialCommunityIcons.json
|
||||
@@ -1,6598 +1 @@
|
||||
@@ -10290,7 +10281,7 @@ index c06a819..13c76cc 100644
|
||||
- "blank": 63116
|
||||
-}
|
||||
\ No newline at end of file
|
||||
+{"menu":983900,"lock-open-outline":983872,"key-outline":986582,"pin-off-outline":985392,"pin-outline":985393,"star-off":984273,"star-outline":984274,"link-variant-remove":987393,"link-variant":983865,"bell":983194,"bell-off-outline":985745,"check":983340,"magnify":983881,"plus":984085,"view-list-outline":988299,"view-list":984434,"play":984074,"pause":984036,"notebook-outline":986815,"text-short":985513,"radiobox-marked":984126,"radiobox-blank":984125,"sort-descending":984253,"information":983804,"sort-ascending":984252,"alert":983078,"arrow-right":983124,"bookmark-outline":983235,"checkbox-marked":983346,"checkbox-blank-outline":983345,"unfold-less-horizontal":984398,"minus-circle":983926,"vibrate":984422,"volume-high":984446,"checkbox-blank-circle-outline":983344,"check-circle-outline":984545,"chevron-up":983363,"chevron-down":983360,"calendar":983277,"minus-circle-outline":983927,"close-circle-outline":983386,"qrcode":984114,"text":985512,"cloud":983391,"book-outline":985956,"pound":984099,"clock-plus-outline":989282,"cloud-upload-outline":985982,"export":983559,"message-badge-outline":989506,"square-edit-outline":985356,"content-copy":983439,"delete-restore":985113,"share-variant":984215,"pencil-lock":984046,"sync-off":984296,"content-duplicate":983441,"theme-light-dark":984334,"pencil":984043,"clock-outline":983376,"attachment":983142,"history":983770,"notebook":985134,"bookmark":983232,"delete-outline":985575,"delete":983476,"link":983863,"cloud-upload":983399,"star":984270,"key":983814,"account-outline":983059,"email":983534,"backup-restore":983151,"update":984752,"android":983090,"web":984479,"home-variant-outline":986023,"text-box-multiple-outline":985784,"download":983514,"arrow-left":983117,"dots-horizontal":983512,"chevron-right":983362,"close":983382,"cellphone-key":985422,"message-plus-outline":987323,"email-outline":983536,"shield-lock":985501,"chevron-left":983361,"minus":983924,"crown":983461,"account-cog":988016,"two-factor-authentication":985519,"information-outline":983805,"logout":983875,"autorenew":983146,"sync-alert":984295,"shape":985137,"circle-half":988053,"brightness-6":983263,"note-edit-outline":989058,"format-line-spacing":983672,"format-size":983679,"format-font":984790,"radar":984119,"arrow-decision-outline":985534,"fingerprint":983607,"eye-off-outline":984785,"folder":983627,"lock":983870,"form-textbox":984590,"bell-outline":983196,"bell-ring":983198,"bug":983268,"mail":986811,"file-document":983577,"mastodon":985809,"twitter":984388,"discord":984687,"open-source-initiative":986030,"monitor":983929,"chart-timeline":984686,"cellphone-arrow-down":985557,"alpha-v":985859,"circle-small":985567,"upload":984402,"file-check":983574,"file":983572,"alert-circle-outline":984534,"check-all":983341,"shield-key-outline":986053,"dots-vertical":983513,"emoticon-wink":986232,"sync":984294,"shield":984216,"billboard":987152,"cellphone":983324,"cellphone-lock":985423,"file-lock":983585,"image-multiple":983801,"harddisk":983754,"file-cabinet":985782,"refresh":984144,"emoticon":986216,"palette":984024,"pin":984067,"format-bold":983652,"table":984299,"language-markdown":983892,"gesture-tap-button":987816,"send-lock":985069,"bomb":984721,"file-pdf-box":983590,"language-html5":983837,"clipboard-text-outline":985656,"invert-colors":983809,"select-all":984198,"cog-outline":985275,"arrow-top-right-thick":985542,"checkbox-blank-circle":983343,"note":983962,"drag":983515,"menu-down":983901,"alert-circle":983080,"reload":984147,"card-text":985975,"open-in-new":984012,"eye":983560}
|
||||
+{"menu":983900,"lock-open-outline":983872,"key-outline":986582,"pin-off-outline":985392,"pin-outline":985393,"star-off":984273,"star-outline":984274,"link-variant-remove":987393,"link-variant":983865,"bell":983194,"bell-off-outline":985745,"check":983340,"magnify":983881,"plus":984085,"view-list-outline":988299,"view-list":984434,"play":984074,"pause":984036,"notebook-outline":986815,"text-short":985513,"radiobox-marked":984126,"radiobox-blank":984125,"sort-descending":984253,"information":983804,"sort-ascending":984252,"alert":983078,"arrow-right":983124,"bookmark-outline":983235,"checkbox-marked":983346,"checkbox-blank-outline":983345,"unfold-less-horizontal":984398,"minus-circle":983926,"vibrate":984422,"volume-high":984446,"checkbox-blank-circle-outline":983344,"check-circle-outline":984545,"chevron-up":983363,"chevron-down":983360,"calendar":983277,"book-outline":985956,"pound":984099,"clock-plus-outline":989282,"cloud-upload-outline":985982,"export":983559,"message-badge-outline":989506,"square-edit-outline":985356,"content-copy":983439,"delete-restore":985113,"share-variant":984215,"pencil-lock":984046,"sync-off":984296,"content-duplicate":983441,"theme-light-dark":984334,"pencil":984043,"clock-outline":983376,"attachment":983142,"history":983770,"notebook":985134,"bookmark":983232,"minus-circle-outline":983927,"delete-outline":985575,"delete":983476,"link":983863,"cloud-upload":983399,"star":984270,"key":983814,"account-outline":983059,"email":983534,"backup-restore":983151,"update":984752,"android":983090,"web":984479,"home-variant-outline":986023,"text-box-multiple-outline":985784,"download":983514,"circle-small":985567,"upload":984402,"file-check":983574,"form-textbox":984590,"fingerprint":983607,"file":983572,"alert-circle-outline":984534,"check-all":983341,"shield-key-outline":986053,"arrow-left":983117,"message-plus-outline":987323,"email-outline":983536,"cellphone-key":985422,"dots-vertical":983513,"close":983382,"chevron-right":983362,"emoticon-wink":986232,"sync":984294,"shield":984216,"billboard":987152,"cellphone":983324,"cellphone-lock":985423,"lock":983870,"file-lock":983585,"image-multiple":983801,"harddisk":983754,"file-cabinet":985782,"refresh":984144,"emoticon":986216,"palette":984024,"pin":984067,"format-bold":983652,"table":984299,"language-markdown":983892,"gesture-tap-button":987816,"send-lock":985069,"bomb":984721,"file-pdf-box":983590,"language-html5":983837,"clipboard-text-outline":985656,"invert-colors":983809,"crown":983461,"select-all":984198,"minus":983924,"information-outline":983805,"cog-outline":985275,"arrow-top-right-thick":985542,"checkbox-blank-circle":983343,"sync-alert":984295,"note":983962,"dots-horizontal":983512,"shield-lock":985501,"chevron-left":983361,"account-cog":988016,"two-factor-authentication":985519,"logout":983875,"autorenew":983146,"shape":985137,"circle-half":988053,"brightness-6":983263,"note-edit-outline":989058,"format-line-spacing":983672,"format-size":983679,"format-font":984790,"radar":984119,"arrow-decision-outline":985534,"eye-off-outline":984785,"folder":983627,"bell-outline":983196,"bell-ring":983198,"bug":983268,"mail":986811,"file-document":983577,"mastodon":985809,"twitter":984388,"discord":984687,"open-source-initiative":986030,"monitor":983929,"chart-timeline":984686,"cellphone-arrow-down":985557,"alpha-v":985859,"alert-circle":983080,"reload":984147,"card-text":985975,"open-in-new":984012,"eye":983560,"drag":983515,"menu-down":983901}
|
||||
\ No newline at end of file
|
||||
diff --git a/node_modules/react-native-vector-icons/glyphmaps/MaterialCommunityIcons.old.json b/node_modules/react-native-vector-icons/glyphmaps/MaterialCommunityIcons.old.json
|
||||
new file mode 100644
|
||||
|
||||
@@ -65,12 +65,7 @@ const EXTRA_ICON_NAMES = [
|
||||
"check-circle-outline",
|
||||
"chevron-up",
|
||||
"chevron-down",
|
||||
"calendar",
|
||||
"minus-circle-outline",
|
||||
"close-circle-outline",
|
||||
"qrcode",
|
||||
"text",
|
||||
"cloud"
|
||||
"calendar"
|
||||
];
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
|
||||
@@ -167,9 +167,10 @@ export const Editor = ({ onChange, onLoad }) => {
|
||||
}}
|
||||
nestedScrollEnabled
|
||||
javaScriptEnabled={true}
|
||||
focusable={true}
|
||||
setSupportMultipleWindows={false}
|
||||
overScrollMode="never"
|
||||
scrollEnabled={Platform.OS === "ios"}
|
||||
scrollEnabled={false}
|
||||
keyboardDisplayRequiresUserAction={false}
|
||||
cacheMode="LOAD_DEFAULT"
|
||||
cacheEnabled={true}
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import React, {
|
||||
createRef,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
useState
|
||||
} from "react";
|
||||
import { Platform } from "react-native";
|
||||
import RNFetchBlob from "react-native-blob-util";
|
||||
import WebView from "react-native-webview";
|
||||
import { Config } from "./store";
|
||||
import { db } from "../app/common/database";
|
||||
import { SUBSCRIPTION_STATUS } from "../app/utils/constants";
|
||||
|
||||
export const fetchHandle = createRef();
|
||||
export const HtmlLoadingWebViewAgent = React.memo(
|
||||
() => {
|
||||
const [source, setSource] = useState(null);
|
||||
const [clipper, setClipper] = useState(null);
|
||||
const loadHandler = useRef();
|
||||
const htmlHandler = useRef();
|
||||
const webview = useRef();
|
||||
const premium = useRef(false);
|
||||
const corsProxy = Config.corsProxy;
|
||||
|
||||
useImperativeHandle(
|
||||
fetchHandle,
|
||||
() => ({
|
||||
processUrl: (url) => {
|
||||
return new Promise((resolve) => {
|
||||
setSource(url);
|
||||
let resolved = false;
|
||||
htmlHandler.current = (html) => {
|
||||
if (resolved) return;
|
||||
resolved = true;
|
||||
setSource(null);
|
||||
resolve(html);
|
||||
};
|
||||
loadHandler.current = (result) => {
|
||||
if (resolved) return;
|
||||
if (!result) {
|
||||
resolved = true;
|
||||
setSource(null);
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const user = await db.user.getUser();
|
||||
const subscriptionStatus =
|
||||
user?.subscription?.type || SUBSCRIPTION_STATUS.BASIC;
|
||||
premium.current =
|
||||
user && subscriptionStatus !== SUBSCRIPTION_STATUS.BASIC;
|
||||
|
||||
const clipperPath =
|
||||
Platform.OS === "ios"
|
||||
? RNFetchBlob.fs.dirs.MainBundleDir +
|
||||
"/extension.bundle/clipper.bundle.js"
|
||||
: "bundle-assets://clipper.bundle.js";
|
||||
|
||||
RNFetchBlob.fs
|
||||
.readFile(clipperPath, "utf8")
|
||||
.then((clipper) => {
|
||||
setClipper(clipper);
|
||||
})
|
||||
.catch((e) => console.log(e));
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return !source || !clipper ? null : (
|
||||
<WebView
|
||||
ref={webview}
|
||||
onLoad={() => {
|
||||
loadHandler.current?.(true);
|
||||
}}
|
||||
style={{
|
||||
width: 100,
|
||||
height: 100,
|
||||
position: "absolute",
|
||||
opacity: 0,
|
||||
zIndex: -1
|
||||
}}
|
||||
useSharedProcessPool={false}
|
||||
pointerEvents="none"
|
||||
onMessage={(event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.nativeEvent.data);
|
||||
if (data && data.type === "html") {
|
||||
console.log("message recieved page loaded");
|
||||
htmlHandler.current?.(data.value);
|
||||
} else {
|
||||
if (data.type === "error") {
|
||||
console.log("error", data.value);
|
||||
htmlHandler.current?.(null);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Error handling webview message", e);
|
||||
}
|
||||
}}
|
||||
injectedJavaScriptBeforeContentLoaded={script(clipper, premium.current)}
|
||||
onError={() => {
|
||||
console.log("Error loading page");
|
||||
loadHandler.current?.();
|
||||
}}
|
||||
source={{
|
||||
uri: source
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
() => true
|
||||
);
|
||||
|
||||
const script = (clipper, pro) => `
|
||||
${clipper}
|
||||
|
||||
function postMessage(type, value) {
|
||||
if (window.ReactNativeWebView) {
|
||||
window.ReactNativeWebView.postMessage(
|
||||
JSON.stringify({
|
||||
type: type,
|
||||
value: value
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
(() => {
|
||||
try {
|
||||
const loadFn = () => {
|
||||
if (!globalThis.Clipper.clipPage) {
|
||||
postMessage("error", globalThis.Clipper.clipPage);
|
||||
} else {
|
||||
globalThis.Clipper.clipPage(document,false, {
|
||||
images: ${pro},
|
||||
inlineImages: false,
|
||||
styles: false,
|
||||
corsProxy: undefined
|
||||
}).then(result => {
|
||||
postMessage("html", result);
|
||||
}).catch(e => {
|
||||
postMessage("error");
|
||||
});
|
||||
}
|
||||
};
|
||||
window.addEventListener("load",loadFn, false);
|
||||
} catch(e) {
|
||||
postMessage("error", e.message);
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
`;
|
||||
|
||||
HtmlLoadingWebViewAgent.displayName = "HtmlLoadingWebViewAgent";
|
||||
@@ -56,15 +56,15 @@ import { Editor } from "./editor";
|
||||
import { Search } from "./search";
|
||||
import { initDatabase, useShareStore } from "./store";
|
||||
import { useThemeColors } from "@notesnook/theme";
|
||||
import { HtmlLoadingWebViewAgent, fetchHandle } from "./fetch-webview";
|
||||
|
||||
const getLinkPreview = (url) => {
|
||||
return getPreviewData(url, 5000);
|
||||
};
|
||||
async function sanitizeHtml(site) {
|
||||
try {
|
||||
let html = await fetchHandle.current?.processUrl(site);
|
||||
return html;
|
||||
let html = await fetch(site);
|
||||
html = await html.text();
|
||||
return sanitize(html, site);
|
||||
} catch (e) {
|
||||
return "";
|
||||
}
|
||||
@@ -82,6 +82,93 @@ function makeHtmlFromPlainText(text) {
|
||||
.replace(/(?:\r\n|\r|\n)/g, "</p><p>")}</p>`;
|
||||
}
|
||||
|
||||
function getBaseUrl(site) {
|
||||
var url = site.split("/").slice(0, 3).join("/");
|
||||
return url;
|
||||
}
|
||||
|
||||
function wrapTablesWithDiv(document) {
|
||||
const tables = document.getElementsByTagName("table");
|
||||
for (let table of tables) {
|
||||
table.setAttribute("contenteditable", "true");
|
||||
const div = document.createElement("div");
|
||||
div.setAttribute("contenteditable", "false");
|
||||
div.innerHTML = table.outerHTML;
|
||||
div.classList.add("table-container");
|
||||
table.replaceWith(div);
|
||||
}
|
||||
return document;
|
||||
}
|
||||
|
||||
let elementBlacklist = [
|
||||
"script",
|
||||
"button",
|
||||
"input",
|
||||
"textarea",
|
||||
"style",
|
||||
"form",
|
||||
"link",
|
||||
"head",
|
||||
"nav",
|
||||
"iframe",
|
||||
"canvas",
|
||||
"select",
|
||||
"dialog",
|
||||
"footer"
|
||||
];
|
||||
|
||||
function removeInvalidElements(document) {
|
||||
let elements = document.querySelectorAll(elementBlacklist.join(","));
|
||||
for (let element of elements) {
|
||||
element.remove();
|
||||
}
|
||||
return document;
|
||||
}
|
||||
|
||||
function replaceSrcWithAbsoluteUrls(document, baseUrl) {
|
||||
let images = document.querySelectorAll("img");
|
||||
|
||||
for (var i = 0; i < images.length; i++) {
|
||||
let img = images[i];
|
||||
let url = getBaseUrl(baseUrl);
|
||||
let src = img.getAttribute("src");
|
||||
if (src.startsWith("/")) {
|
||||
if (src.startsWith("//")) {
|
||||
src = src.replace("//", "https://");
|
||||
} else {
|
||||
src = url + src;
|
||||
}
|
||||
}
|
||||
if (src.startsWith("data:")) {
|
||||
img.remove();
|
||||
} else {
|
||||
img.setAttribute("src", src);
|
||||
}
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
function fixCodeBlocks(document) {
|
||||
let elements = document.querySelectorAll("code,pre");
|
||||
|
||||
for (let element of elements) {
|
||||
element.classList.add(".hljs");
|
||||
}
|
||||
return document;
|
||||
}
|
||||
|
||||
function sanitize(html, baseUrl) {
|
||||
let parser = parseHTML(html);
|
||||
parser = wrapTablesWithDiv(parser);
|
||||
parser = removeInvalidElements(parser);
|
||||
parser = replaceSrcWithAbsoluteUrls(parser, baseUrl);
|
||||
parser = fixCodeBlocks(parser);
|
||||
let htmlString = parser.body.outerHTML;
|
||||
htmlString = htmlString + `<hr>${makeHtmlFromUrl(baseUrl)}`;
|
||||
return htmlString;
|
||||
}
|
||||
|
||||
let defaultNote = {
|
||||
title: null,
|
||||
id: null,
|
||||
@@ -123,7 +210,6 @@ const ShareView = ({ quicknote = false }) => {
|
||||
const [mode, setMode] = useState(1);
|
||||
const keyboardHeight = useRef(0);
|
||||
const { width, height } = useWindowDimensions();
|
||||
const [loadingPage, setLoadingPage] = useState(false);
|
||||
const insets =
|
||||
Platform.OS === "android"
|
||||
? { top: StatusBar.currentHeight }
|
||||
@@ -174,10 +260,6 @@ const ShareView = ({ quicknote = false }) => {
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
try {
|
||||
if (noteContent.current) {
|
||||
onLoad();
|
||||
return;
|
||||
}
|
||||
defaultNote.content.data = null;
|
||||
setNote({ ...defaultNote });
|
||||
const data = await ShareExtension.data();
|
||||
@@ -315,19 +397,14 @@ const ShareView = ({ quicknote = false }) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
if (m === 2) {
|
||||
setLoadingPage(true);
|
||||
setTimeout(async () => {
|
||||
let html = await sanitizeHtml(rawData.value);
|
||||
let html = await sanitizeHtml(rawData.value);
|
||||
setNote((note) => {
|
||||
note.content.data = html;
|
||||
noteContent.current = html;
|
||||
setLoadingPage(false);
|
||||
onLoad();
|
||||
setNote((note) => {
|
||||
note.content.data = html;
|
||||
return { ...note };
|
||||
});
|
||||
}, 300);
|
||||
return { ...note };
|
||||
});
|
||||
} else {
|
||||
setLoadingPage(false);
|
||||
let html = isURL(rawData.value)
|
||||
? makeHtmlFromUrl(rawData.value)
|
||||
: makeHtmlFromPlainText(rawData.value);
|
||||
@@ -373,8 +450,6 @@ const ShareView = ({ quicknote = false }) => {
|
||||
justifyContent: quicknote ? "flex-start" : "flex-end"
|
||||
}}
|
||||
>
|
||||
{loadingPage ? <HtmlLoadingWebViewAgent /> : null}
|
||||
|
||||
{quicknote && !searchMode ? (
|
||||
<View
|
||||
style={{
|
||||
@@ -641,27 +716,16 @@ const ShareView = ({ quicknote = false }) => {
|
||||
<SafeAreaProvider
|
||||
style={{
|
||||
flex: 1,
|
||||
paddingTop: 10,
|
||||
justifyContent: loadingPage ? "center" : undefined,
|
||||
alignItems: loadingPage ? "center" : undefined
|
||||
paddingTop: 10
|
||||
}}
|
||||
>
|
||||
{!loadingExtension && !loadingPage ? (
|
||||
{!loadingExtension && (
|
||||
<Editor
|
||||
onLoad={onLoadEditor}
|
||||
onChange={(html) => {
|
||||
noteContent.current = html;
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{loadingPage ? (
|
||||
<>
|
||||
<ActivityIndicator color={colors.primary.accent} />
|
||||
<Text>Preparing web clip...</Text>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</SafeAreaProvider>
|
||||
</View>
|
||||
|
||||
@@ -90,7 +90,3 @@ export const useShareStore = create((set) => ({
|
||||
set({ selectedTags });
|
||||
}
|
||||
}));
|
||||
|
||||
export const Config = {
|
||||
corsProxy: appSettings?.corsProxy
|
||||
};
|
||||
|
||||
29
apps/theme-builder/package-lock.json
generated
29
apps/theme-builder/package-lock.json
generated
@@ -17,7 +17,6 @@
|
||||
"@notesnook/web": "file:../web",
|
||||
"@theme-ui/components": "^0.14.7",
|
||||
"@theme-ui/core": "^0.14.7",
|
||||
"@trpc/client": "^10.38.3",
|
||||
"clipboard-polyfill": "^4.0.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"katex": "^0.16.2",
|
||||
@@ -1377,7 +1376,7 @@
|
||||
},
|
||||
"../web": {
|
||||
"name": "@notesnook/web",
|
||||
"version": "2.6.5",
|
||||
"version": "2.6.1",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@aws-sdk/util-base64-browser": "^3.208.0",
|
||||
@@ -1406,8 +1405,8 @@
|
||||
"@theme-ui/color": "^0.14.7",
|
||||
"@theme-ui/components": "^0.14.7",
|
||||
"@theme-ui/core": "^0.14.7",
|
||||
"@trpc/client": "10.38.3",
|
||||
"@trpc/react-query": "10.38.3",
|
||||
"@trpc/client": "10.31.0",
|
||||
"@trpc/react-query": "10.31.0",
|
||||
"allotment": "^1.19.0",
|
||||
"axios": "^1.3.4",
|
||||
"clipboard-polyfill": "4.0.0",
|
||||
@@ -1446,7 +1445,7 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.5",
|
||||
"@playwright/test": "^1.36.2",
|
||||
"@trpc/server": "10.38.3",
|
||||
"@trpc/server": "10.31.0",
|
||||
"@types/babel__core": "^7.20.1",
|
||||
"@types/file-saver": "^2.0.5",
|
||||
"@types/marked": "^4.0.7",
|
||||
@@ -2395,26 +2394,6 @@
|
||||
"react": ">16"
|
||||
}
|
||||
},
|
||||
"node_modules/@trpc/client": {
|
||||
"version": "10.38.3",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/client/-/client-10.38.3.tgz",
|
||||
"integrity": "sha512-hHPsElTL4sB+UGzuAZ4iwRVHjELrk/Nsl2owsYw9ITJz3EY7VWRsQ6vK48kJvlHPYPhP+efID5UOgyZMs4bFXA==",
|
||||
"funding": [
|
||||
"https://trpc.io/sponsor"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"@trpc/server": "10.38.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@trpc/server": {
|
||||
"version": "10.38.3",
|
||||
"resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.38.3.tgz",
|
||||
"integrity": "sha512-9s8/kwo2IDB5hwB2SKZZrfevRhdb1f9fdXtIYd3lbQuf2jQaC/LyQuHaIQjDQoUx9updBfsHXcFFPiCP1DL6pg==",
|
||||
"funding": [
|
||||
"https://trpc.io/sponsor"
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.1",
|
||||
"dev": true,
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"@notesnook/web": "file:../web",
|
||||
"@theme-ui/components": "^0.14.7",
|
||||
"@theme-ui/core": "^0.14.7",
|
||||
"@trpc/client": "^10.38.3",
|
||||
"clipboard-polyfill": "^4.0.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"katex": "^0.16.2",
|
||||
|
||||
@@ -643,25 +643,13 @@ function exportTheme(theme: ThemeDefinition) {
|
||||
}),
|
||||
`${theme.id}.json`
|
||||
);
|
||||
const confirmed = window.confirm(
|
||||
"Do you also want to download code-block.css for this theme?"
|
||||
);
|
||||
if (confirmed) {
|
||||
FileSaver.saveAs(
|
||||
new Blob([theme.codeBlockCSS], {
|
||||
type: "text/plain"
|
||||
}),
|
||||
`code-block.css`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function themeToJSON(theme: ThemeDefinition) {
|
||||
return JSON.stringify(
|
||||
{
|
||||
$schema: JSON_SCHEMA_URL,
|
||||
...theme,
|
||||
codeBlockCSS: undefined
|
||||
...theme
|
||||
},
|
||||
undefined,
|
||||
2
|
||||
|
||||
@@ -18,21 +18,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { AppModel } from "./models/app.model";
|
||||
import { USER } from "./utils";
|
||||
import { NOTE, USER } from "./utils";
|
||||
|
||||
test("create an unencrypted backup", async ({ page }) => {
|
||||
test("create a backup", async ({ page }) => {
|
||||
const app = new AppModel(page);
|
||||
await app.auth.goto();
|
||||
await app.auth.login(USER.CURRENT);
|
||||
await app.goto();
|
||||
const notes = await app.goToNotes();
|
||||
await notes.createNote(NOTE);
|
||||
const settings = await app.goToSettings();
|
||||
await settings.toggleBackupEncryption(USER.CURRENT.password);
|
||||
|
||||
const backup = await settings.createBackup(USER.CURRENT.password);
|
||||
const backup = await settings.createBackup();
|
||||
|
||||
expect(backup.length > 0).toBeTruthy();
|
||||
});
|
||||
|
||||
test("restore an unencrypted backup", async ({ page }) => {
|
||||
test("restore a backup", async ({ page }) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const settings = await app.goToSettings();
|
||||
@@ -56,7 +56,7 @@ test("create an encrypted backup", async ({ page }, info) => {
|
||||
await app.auth.login(USER.CURRENT);
|
||||
const settings = await app.goToSettings();
|
||||
|
||||
const backup = await settings.createBackup();
|
||||
const backup = await settings.createBackup(USER.CURRENT.password);
|
||||
|
||||
expect(backup.length > 0).toBeTruthy();
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user