Compare commits

...

14 Commits

Author SHA1 Message Date
alihamuh
53baa1a852 web: changed isChecked to checked 2024-03-28 00:01:15 +05:00
Licaon_Kter
c54759e766 Remove gradle property unused in Java 17
Signed-off-by: Licaon_Kter <licaon-kter@users.noreply.github.com>
2024-03-22 23:17:45 +05:00
Ammar Ahmed
f15446b1d6 mobile: bump version code 2024-03-21 11:45:03 +05:00
Ammar Ahmed
aa2a8ab6f4 mobile: ignore beta updates on gh release 2024-03-21 10:17:34 +05:00
Ammar Ahmed
8e59ff1318 mobile: release v2.6.18 2024-03-21 10:11:37 +05:00
Abdullah Atta
ad99c87f72 web: bump version to 2.6.17 2024-03-21 10:07:11 +05:00
Abdullah Atta
ba776b5854 global: update lockfiles 2024-03-21 10:07:02 +05:00
Abdullah Atta
e526145e48 web: fix cannot read property integration of undefined 2024-03-21 10:02:08 +05:00
Abdullah Atta
45c5e65bf6 core: add more sanity checks 2024-03-21 09:42:38 +05:00
Abdullah Atta
87bec4e2ab core: add sanity checks to handle cannot read properties of undefined (reading 'hash') error 2024-03-21 09:37:18 +05:00
Abdullah Atta
433e67a6dc web: fix pull failed error on downloading attachments 2024-03-21 09:12:55 +05:00
Ammar Ahmed
6acba51b88 mobile: fix app lock shows on session expired dialog 2024-03-21 07:46:49 +05:00
Abdullah Atta
cf1322952d web: handle errors during attachment downloading 2024-03-15 09:25:10 +05:00
Abdullah Atta
b5ac9dd430 web: make opfs safer by allowing only 1 operation at a time 2024-03-15 09:01:35 +05:00
33 changed files with 249 additions and 160 deletions

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/desktop",
"version": "2.6.16",
"version": "2.6.17",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/desktop",
"version": "2.6.16",
"version": "2.6.17",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {

View File

@@ -2,7 +2,7 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "2.6.16",
"version": "2.6.17",
"appAppleId": "1544027013",
"private": true,
"main": "./dist/index.js",

View File

@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useState } from "react";
import { Modal, View } from "react-native";
import { View } from "react-native";
import { db } from "../../common/database";
import { MMKV } from "../../common/database/mmkv";
import BiometricService from "../../services/biometrics";
@@ -37,6 +37,7 @@ import { eLoginSessionExpired, eUserLoggedIn } from "../../utils/events";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
import { Dialog } from "../dialog";
import BaseDialog from "../dialog/base-dialog";
import { presentDialog } from "../dialog/functions";
import SheetProvider from "../sheet-provider";
import { Toast } from "../toast";
@@ -46,7 +47,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 "";
@@ -69,6 +69,9 @@ export const SessionExpired = () => {
eSendEvent(eUserLoggedIn, true);
setVisible(false);
setFocused(false);
useUserStore.setState({
disableAppLockRequests: false
});
},
true
);
@@ -84,6 +87,9 @@ export const SessionExpired = () => {
MMKV.clearStore();
clearAllStores();
setVisible(false);
useUserStore.setState({
disableAppLockRequests: false
});
} catch (e) {
ToastEvent.show({
heading: e.message,
@@ -127,6 +133,9 @@ export const SessionExpired = () => {
email.current = user.email;
setFocused(false);
setVisible(true);
useUserStore.setState({
disableAppLockRequests: true
});
}
}, [email]);
@@ -142,6 +151,9 @@ export const SessionExpired = () => {
await sleep(300);
passwordInputRef.current?.focus();
setFocused(true);
useUserStore.setState({
disableAppLockRequests: true
});
}}
enableSheetKeyboardHandler={true}
visible={true}

View File

@@ -61,7 +61,10 @@ export const getGithubVersion = async () => {
if (!res?.ok) return null;
const data = (await res?.json()) as GithubRelease[];
const versions = data?.filter((tag) => tag.tag_name.endsWith("android"));
const versions = data?.filter(
(tag) =>
tag.tag_name.endsWith("android") && !tag.tag_name.endsWith("beta-android")
);
const latestVersion = versions[0];
const version = latestVersion.tag_name.replace("-android", "");
return {

View File

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

View File

@@ -10,7 +10,7 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
@@ -47,4 +47,4 @@ hermesEnabled=true
# v8.android.tools.dir=/home/ammarahm-ed/Repos/notesnook-mobile/node_modules/v8-android-jit-nointl/dist/tools/android
# fdroid
fdroidBuild=false
fdroidBuild=false

View File

@@ -1,10 +1,3 @@
- Support for exporting locked notes
- Maintain directory structure in exports based on Notebooks/Topics
- Match color of navigation bar in sheets with app theme
- Improved image loading and show progress when loading images
- Support for setting trash interval to daily
- Fix android app UI issues with RTL languages
- Fix pasting links in editor
- Other Bug fixes and performance improvements
- Minor bug fixes
Thank you for using Notesnook!

View File

@@ -997,7 +997,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2085;
CURRENT_PROJECT_VERSION = 2090;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1071,7 +1071,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.6.17;
MARKETING_VERSION = 2.6.18;
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 = 2085;
CURRENT_PROJECT_VERSION = 2090;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1176,7 +1176,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.6.17;
MARKETING_VERSION = 2.6.18;
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 = 2085;
CURRENT_PROJECT_VERSION = 2090;
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.17;
MARKETING_VERSION = 2.6.18;
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 = 2085;
CURRENT_PROJECT_VERSION = 2090;
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.17;
MARKETING_VERSION = 2.6.18;
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 = 2085;
CURRENT_PROJECT_VERSION = 2090;
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.17;
MARKETING_VERSION = 2.6.18;
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 = 2085;
CURRENT_PROJECT_VERSION = 2090;
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.17;
MARKETING_VERSION = 2.6.18;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/mobile",
"version": "2.6.15",
"version": "2.6.17",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@notesnook/mobile",
"version": "2.6.15",
"version": "2.6.17",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -57,13 +57,13 @@
"../../packages/core": {
"name": "@notesnook/core",
"version": "7.4.1",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {
"@microsoft/signalr": "^7.0.10",
"@microsoft/signalr-protocol-msgpack": "^7.0.10",
"@microsoft/signalr": "^8.0.0",
"@notesnook/logger": "file:../logger",
"@readme/data-urls": "^3.0.0",
"@streetwriters/showdown": "^3.0.4-alpha",
"@streetwriters/showdown": "^3.0.5-alpha",
"async-mutex": "^0.3.2",
"dayjs": "1.11.9",
"entities": "^4.3.1",
@@ -26837,12 +26837,11 @@
"@notesnook/core": {
"version": "file:../../packages/core",
"requires": {
"@microsoft/signalr": "^7.0.10",
"@microsoft/signalr-protocol-msgpack": "^7.0.10",
"@microsoft/signalr": "^8.0.0",
"@notesnook/crypto": "file:../crypto",
"@notesnook/logger": "file:../logger",
"@readme/data-urls": "^3.0.0",
"@streetwriters/showdown": "^3.0.4-alpha",
"@streetwriters/showdown": "^3.0.5-alpha",
"@types/html-to-text": "^9.0.0",
"@types/katex": "^0.16.1",
"@types/prismjs": "^1.26.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "2.6.17",
"version": "2.6.18",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -45,4 +45,4 @@
"react-native": "0.72.0",
"react-native-iap": "12.11.0"
}
}
}

View File

@@ -1377,7 +1377,7 @@
},
"../web": {
"name": "@notesnook/web",
"version": "2.6.14",
"version": "2.6.17",
"license": "GPL-3.0-or-later",
"dependencies": {
"@aws-sdk/util-base64-browser": "^3.208.0",

View File

@@ -26,20 +26,25 @@ import { consumeReadableStream } from "../src/utils/stream";
import { xxhash64 } from "hash-wasm";
import path from "path";
const CHUNK_SIZE = 512 * 1024;
const CHUNK_SIZE = 512 * 1024 + 17;
test("chunked stream should create equal sized chunks", async (t) => {
const chunks = await consumeReadableStream(
(
Readable.toWeb(
createReadStream(
path.join(__dirname, "..", "__e2e__", "data", "importer-data.zip")
)
createReadStream(path.join(__dirname, "data", "35a4b0a78dbb9260"))
) as ReadableStream<Uint8Array>
).pipeThrough(new ChunkedStream(CHUNK_SIZE))
).pipeThrough(new ChunkedStream(CHUNK_SIZE, "copy"))
);
t.expect(await Promise.all(chunks.map((a) => xxhash64(a)))).toMatchObject([
"6234b76401d9eb97",
"338834da3f6500b2"
"9a3fa91d341b245d",
"c6b5d3ec17f14a5e",
"5163243faf462ce4",
"63aca6b8a7f68476",
"cd9d082fa3015bd3"
]);
t.expect(
await Promise.all(chunks.map((a) => a.byteOffset === 0))
).toMatchObject([true, true, true, true, true]);
});

Binary file not shown.

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/web",
"version": "2.6.16",
"version": "2.6.17",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/web",
"version": "2.6.16",
"version": "2.6.17",
"license": "GPL-3.0-or-later",
"dependencies": {
"@aws-sdk/util-base64-browser": "^3.208.0",
@@ -210,13 +210,13 @@
"../../packages/core": {
"name": "@notesnook/core",
"version": "7.4.1",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {
"@microsoft/signalr": "^7.0.10",
"@microsoft/signalr-protocol-msgpack": "^7.0.10",
"@microsoft/signalr": "^8.0.0",
"@notesnook/logger": "file:../logger",
"@readme/data-urls": "^3.0.0",
"@streetwriters/showdown": "^3.0.4-alpha",
"@streetwriters/showdown": "^3.0.5-alpha",
"async-mutex": "^0.3.2",
"dayjs": "1.11.9",
"entities": "^4.3.1",
@@ -439,7 +439,7 @@
},
"../desktop": {
"name": "@notesnook/desktop",
"version": "2.6.15",
"version": "2.6.17",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {

View File

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

View File

@@ -74,7 +74,7 @@ const menuItems: (topic: any, items?: any[]) => MenuItem[] = (
type: "button",
key: "set-as-default",
title: "Set as default",
checked:
isChecked:
defaultNotebook?.id === topic.notebookId &&
defaultNotebook?.topic === topic.id,
icon: TopicIcon.path,

View File

@@ -36,4 +36,9 @@ declare global {
os?: () => NodeJS.Platform | "mas";
NativeNNCrypto?: new () => import("@notesnook/crypto").NNCrypto;
}
interface FileSystemFileHandle {
createSyncAccessHandle(options?: {
mode: "read-only" | "readwrite" | "readwrite-unsafe";
}): Promise<FileSystemSyncAccessHandle>;
}
}

View File

@@ -446,18 +446,19 @@ function reportProgress(
}
async function downloadFile(filename: string, requestOptions: RequestOptions) {
const { url, headers, chunkSize, signal } = requestOptions;
const handle = await streamablefs.readFile(filename);
if (
handle &&
handle.file.size === (await handle.size()) - handle.file.chunks * ABYTES
)
return true;
else if (handle) await handle.delete();
const attachment = db.attachments?.attachment(filename);
try {
const { url, headers, chunkSize, signal } = requestOptions;
const handle = await streamablefs.readFile(filename);
if (
handle &&
handle.file.size === (await handle.size()) - handle.file.chunks * ABYTES
)
return true;
else if (handle) await handle.delete();
const attachment = db.attachments?.attachment(filename);
reportProgress(
{ total: 100, loaded: 0 },
{ type: "download", hash: filename }

View File

@@ -24,6 +24,7 @@ import { expose, transfer } from "comlink";
class OriginPrivateFileStore implements IFileStorage {
private storage: IndexedDBKVStore;
private locks: Map<string, Promise<any>> = new Map();
constructor(
name: string,
private readonly directory: FileSystemDirectoryHandle
@@ -33,7 +34,9 @@ class OriginPrivateFileStore implements IFileStorage {
async clear(): Promise<void> {
for await (const [name] of this.directory) {
await this.directory.removeEntry(name, { recursive: true });
await this.safeOp(name, () =>
this.directory.removeEntry(name, { recursive: true })
);
}
await this.storage.clear();
}
@@ -47,33 +50,59 @@ class OriginPrivateFileStore implements IFileStorage {
return this.storage.delete(filename);
}
async writeChunk(chunkName: string, data: Uint8Array): Promise<void> {
const file = await this.directory.getFileHandle(chunkName, {
create: true
});
const syncHandle = await file.createSyncAccessHandle();
syncHandle.write(data);
syncHandle.close();
try {
await this.safeOp(chunkName, () =>
this.directory
.getFileHandle(chunkName, {
create: true
})
.then((file) => file.createSyncAccessHandle())
.then((handle) => {
handle.write(data);
handle.close();
})
);
} catch (e) {
console.error("Failed to write chunk", e);
}
}
async deleteChunk(chunkName: string) {
try {
await this.directory.removeEntry(chunkName);
await this.safeOp(chunkName, () => this.directory.removeEntry(chunkName));
} catch (e) {
console.error("Failed to delete chunk", e);
}
}
async readChunk(chunkName: string): Promise<Uint8Array | undefined> {
try {
const file = await this.directory.getFileHandle(chunkName);
const syncHandle = await file.createSyncAccessHandle();
const buffer = new Uint8Array(syncHandle.getSize());
syncHandle.read(buffer);
syncHandle.close();
return buffer;
if (Object.hasOwn(FileSystemSyncAccessHandle.prototype, "mode")) {
return readFile(this.directory, chunkName);
}
// OPFS currently does not support multiple readers on a single file
// on all browsers so we wait for the file handle to be released before
// continuing. This is temporary until all browsers start supporting
// the read-only mode.
return await this.safeOp(chunkName, () =>
readFile(this.directory, chunkName)
);
} catch (e) {
console.error("Failed to read chunk", e);
return;
}
}
private async safeOp<T>(chunkName: string, createPromise: () => Promise<T>) {
const lock = this.locks.get(chunkName);
if (lock) await lock;
const promise = createPromise();
this.locks.set(chunkName, promise);
return await promise.finally(() => this.locks.delete(chunkName));
}
}
const fileStores: Map<string, OriginPrivateFileStore> = new Map();
@@ -117,3 +146,12 @@ const workerModule = {
expose(workerModule);
export type OriginPrivateFileStoreWorkerType = typeof workerModule;
async function readFile(directory: FileSystemDirectoryHandle, name: string) {
const file = await directory.getFileHandle(name);
const handle = await file.createSyncAccessHandle({ mode: "read-only" });
const buffer = new Uint8Array(handle.getSize());
handle.read(buffer);
handle.close();
return buffer;
}

View File

@@ -72,9 +72,8 @@ class AttachmentStore extends BaseStore {
await createWriteStream("attachments.zip", {
signal: abortController.signal
})
);
this.set((state) => (state.status = undefined));
)
.finally(() => this.set((state) => (state.status = undefined)));
};
cancel = async () => {

View File

@@ -76,7 +76,7 @@ class SettingStore extends BaseStore {
privacyMode: await desktop?.integration.privacyMode.query(),
zoomFactor: await desktop?.integration.zoomFactor.query(),
autoUpdates: await desktop?.updater.autoUpdates.query(),
proxyRules: await desktop.integration.proxyRules.query()
proxyRules: await desktop?.integration.proxyRules.query()
});
};

View File

@@ -40,46 +40,51 @@ export class AttachmentStream extends ReadableStream<ZipFile> {
super({
start() {},
async pull(controller) {
if (signal?.aborted) {
controller.close();
return;
}
try {
if (signal?.aborted) {
controller.close();
return;
}
onProgress && onProgress(index);
const attachment = attachments[index++];
onProgress && onProgress(index);
const attachment = attachments[index++];
await db.fs?.downloadFile(
GROUP_ID,
attachment.metadata.hash,
attachment.chunkSize,
attachment.metadata
);
await db.fs?.downloadFile(
GROUP_ID,
attachment.metadata.hash,
attachment.chunkSize,
attachment.metadata
);
const key = await db.attachments?.decryptKey(attachment.key);
const file = await lazify(
import("../../interfaces/fs"),
({ decryptFile }) =>
decryptFile(attachment.metadata.hash, {
key,
iv: attachment.iv,
name: attachment.metadata.filename,
type: attachment.metadata.type,
isUploaded: !!attachment.dateUploaded
})
);
const key = await db.attachments?.decryptKey(attachment.key);
const file = await lazify(
import("../../interfaces/fs"),
({ decryptFile }) =>
decryptFile(attachment.metadata.hash, {
key,
iv: attachment.iv,
name: attachment.metadata.filename,
type: attachment.metadata.type,
isUploaded: !!attachment.dateUploaded
})
);
if (file) {
const filePath: string = attachment.metadata.filename;
controller.enqueue({
path: makeUniqueFilename(filePath, counters),
data: new Uint8Array(await file.arrayBuffer())
});
} else {
controller.error(new Error("Failed to decrypt file."));
}
if (index === attachments.length) {
controller.close();
if (file) {
const filePath: string = attachment.metadata.filename;
controller.enqueue({
path: makeUniqueFilename(filePath, counters),
data: new Uint8Array(await file.arrayBuffer())
});
} else {
controller.error(new Error("Failed to decrypt file."));
}
} catch (e) {
console.error(e);
controller.error(e);
} finally {
if (index === attachments.length) {
controller.close();
}
}
}
});

View File

@@ -55,7 +55,11 @@ export class ChunkedStream extends TransformStream<Uint8Array, Uint8Array> {
}
},
flush(controller) {
if (backBuffer) controller.enqueue(backBuffer);
if (backBuffer) {
const buffer =
mode === "copy" ? new Uint8Array(backBuffer) : backBuffer;
controller.enqueue(buffer);
}
}
});
}

View File

@@ -0,0 +1,3 @@
- Minor bug fixes
Thank you for using Notesnook!

View File

@@ -26,13 +26,13 @@
"name": "@notesnook/core",
"version": "7.4.1",
"dev": true,
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {
"@microsoft/signalr": "^7.0.10",
"@microsoft/signalr-protocol-msgpack": "^7.0.10",
"@microsoft/signalr": "^8.0.0",
"@notesnook/logger": "file:../logger",
"@readme/data-urls": "^3.0.0",
"@streetwriters/showdown": "^3.0.4-alpha",
"@streetwriters/showdown": "^3.0.5-alpha",
"async-mutex": "^0.3.2",
"dayjs": "1.11.9",
"entities": "^4.3.1",
@@ -144,12 +144,11 @@
"@notesnook/core": {
"version": "file:../core",
"requires": {
"@microsoft/signalr": "^7.0.10",
"@microsoft/signalr-protocol-msgpack": "^7.0.10",
"@microsoft/signalr": "^8.0.0",
"@notesnook/crypto": "file:../crypto",
"@notesnook/logger": "file:../logger",
"@readme/data-urls": "^3.0.0",
"@streetwriters/showdown": "^3.0.4-alpha",
"@streetwriters/showdown": "^3.0.5-alpha",
"@types/html-to-text": "^9.0.0",
"@types/katex": "^0.16.1",
"@types/prismjs": "^1.26.0",

View File

@@ -4158,8 +4158,7 @@
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
"dev": true,
"requires": {}
"dev": true
}
}
},
@@ -4944,8 +4943,7 @@
"ws": {
"version": "7.5.9",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
"requires": {}
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q=="
},
"xml-name-validator": {
"version": "4.0.0",

View File

@@ -113,7 +113,7 @@ class Database {
this
);
EV.subscribe(EVENTS.attachmentDeleted, async (attachment) => {
await this.fs.cancel(attachment.metadata.hash);
await this.fs.cancel(attachment.metadata?.hash);
});
EV.subscribe(EVENTS.userLoggedOut, async () => {
await this.monographs.deinit();

View File

@@ -67,10 +67,10 @@ export default class Lookup {
}
attachments(array, query) {
return search(
array,
query,
(n) => `${n.metadata.filename} ${n.metadata.type} ${n.metadata.hash}`
return search(array, query, (n) =>
n.metadata
? `${n.metadata.filename} ${n.metadata.type} ${n.metadata.hash}`
: ""
);
}

View File

@@ -169,9 +169,13 @@ class Merger {
if (!remoteItem.metadata) break;
const localItem = this._db.attachments.attachment(
remoteItem.metadata.hash
remoteItem.metadata?.hash
);
if (localItem && localItem.dateUploaded !== remoteItem.dateUploaded) {
if (
localItem &&
localItem.metadata &&
localItem.dateUploaded !== remoteItem.dateUploaded
) {
const noteIds = localItem.noteIds.slice();
const isRemoved = await this._db.attachments.remove(
localItem.metadata.hash,

View File

@@ -40,7 +40,7 @@ export default class Attachments extends Collection {
async ({ success, filename, groupId, eventData }) => {
if (!success || !eventData || !eventData.readOnDownload) return;
const attachment = this.attachment(filename);
if (!attachment) return;
if (!attachment || !attachment.metadata) return;
const src = await this.read(filename, getOutputType(attachment));
if (!src) return;
@@ -108,7 +108,7 @@ export default class Attachments extends Collection {
if (!attachmentArg.hash) throw new Error("Please provide attachment hash.");
const oldAttachment =
this.all.find((a) => a.metadata.hash === attachmentArg.hash) || {};
this.all.find((a) => a.metadata?.hash === attachmentArg.hash) || {};
let id = oldAttachment.id || getId();
const noteIds = oldAttachment.noteIds || [];
@@ -205,7 +205,7 @@ export default class Attachments extends Collection {
async remove(hashOrId, localOnly) {
const attachment = this.attachment(hashOrId);
if (!attachment) return false;
if (!attachment || !attachment.metadata) return false;
if (!localOnly && !(await this._canDetach(attachment)))
throw new Error("This attachment is inside a locked note.");
@@ -267,7 +267,7 @@ export default class Attachments extends Collection {
}
exists(hash) {
const attachment = this.all.find((a) => a.metadata.hash === hash);
const attachment = this.all.find((a) => a.metadata?.hash === hash);
return !!attachment;
}
@@ -277,7 +277,7 @@ export default class Attachments extends Collection {
* @returns {Promise<string>} dataurl formatted string
*/
async read(hash, outputType) {
const attachment = this.all.find((a) => a.metadata.hash === hash);
const attachment = this.all.find((a) => a.metadata?.hash === hash);
if (!attachment) return;
const key = await this.decryptKey(attachment.key);
@@ -302,7 +302,7 @@ export default class Attachments extends Collection {
attachment(hashOrId) {
return this.all.find(
(a) => a.id === hashOrId || a.metadata.hash === hashOrId
(a) => a.id === hashOrId || a.metadata?.hash === hashOrId
);
}
@@ -348,8 +348,9 @@ export default class Attachments extends Collection {
async downloadMedia(noteId, hashesToLoad) {
const attachments = this.media.filter(
(attachment) =>
!!attachment.metadata &&
hasItem(attachment.noteIds, noteId) &&
(!hashesToLoad || hasItem(hashesToLoad, attachment.metadata.hash))
(!hashesToLoad || hasItem(hashesToLoad, attachment.metadata?.hash))
);
await this._db.fs.queueDownloads(
@@ -366,7 +367,11 @@ export default class Attachments extends Collection {
async cleanup() {
const now = dayjs().unix();
for (const attachment of this.deleted) {
if (dayjs(attachment.dateDeleted).add(7, "days").unix() < now) continue;
if (
!attachment.metadata ||
dayjs(attachment.dateDeleted).add(7, "days").unix() < now
)
continue;
const isDeleted = await this._db.fs.deleteFile(attachment.metadata.hash);
if (!isDeleted) continue;
@@ -377,7 +382,9 @@ export default class Attachments extends Collection {
get pending() {
return this.all.filter(
(attachment) => attachment.dateUploaded <= 0 || !attachment.dateUploaded
(attachment) =>
attachment.metadata &&
(attachment.dateUploaded <= 0 || !attachment.dateUploaded)
);
}
@@ -398,23 +405,30 @@ export default class Attachments extends Collection {
}
get images() {
return this.all.filter((attachment) => isImage(attachment.metadata.type));
return this.all.filter(
(attachment) => attachment.metadata && isImage(attachment.metadata.type)
);
}
get webclips() {
return this.all.filter((attachment) => isWebClip(attachment.metadata.type));
return this.all.filter(
(attachment) => attachment.metadata && isWebClip(attachment.metadata.type)
);
}
get media() {
return this.all.filter(
(attachment) =>
isImage(attachment.metadata.type) || isWebClip(attachment.metadata.type)
attachment.metadata &&
(isImage(attachment.metadata.type) ||
isWebClip(attachment.metadata.type))
);
}
get files() {
return this.all.filter(
(attachment) =>
attachment.metadata &&
!isImage(attachment.metadata.type) &&
!isWebClip(attachment.metadata.type)
);

View File

@@ -122,7 +122,9 @@ export default class Content extends Collection {
const content = getContentFromData(contentItem.type, contentItem.data);
if (!content) console.log(contentItem);
contentItem.data = await content.insertMedia(async (hashes) => {
const attachments = hashes.map((h) => this._db.attachments.attachment(h));
const attachments = hashes
.map((h) => this._db.attachments.attachment(h))
.filter((a) => !!a && !!a.metadata);
await this._db.fs.queueDownloads(
attachments.map((a) => ({
filename: a.metadata.hash,
@@ -134,6 +136,8 @@ export default class Content extends Collection {
);
const sources = {};
for (const attachment of attachments) {
if (!attachment.metadata) continue;
const src = await this._db.attachments.read(
attachment.metadata.hash,
getOutputType(attachment)
@@ -188,20 +192,23 @@ export default class Content extends Collection {
const toDelete = noteAttachments.filter((attachment) => {
return attachments.every(
(a) => a.hash && a.hash !== attachment.metadata.hash
(a) => a && a.hash && a.hash !== attachment.metadata?.hash
);
});
const toAdd = attachments.filter((attachment) => {
return (
attachment &&
attachment.hash &&
noteAttachments.every((a) => attachment.hash !== a.metadata.hash)
noteAttachments.every((a) => attachment.hash !== a.metadata?.hash)
);
});
for (let attachment of toDelete) {
if (!attachment.metadata) continue;
await this._db.attachments.delete(
attachment.metadata.hash,
attachment.metadata?.hash,
contentItem.noteId
);
}

View File

@@ -251,6 +251,7 @@ export default class Notes extends Collection {
const attachments = this._db.attachments.ofNote(itemData.id, "all");
for (let attachment of attachments) {
if (!attachment || !attachment.metadata) continue;
await this._db.attachments.delete(
attachment.metadata.hash,
itemData.id

View File

@@ -90,13 +90,13 @@
"../core": {
"name": "@notesnook/core",
"version": "7.4.1",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {
"@microsoft/signalr": "^7.0.10",
"@microsoft/signalr-protocol-msgpack": "^7.0.10",
"@microsoft/signalr": "^8.0.0",
"@notesnook/logger": "file:../logger",
"@readme/data-urls": "^3.0.0",
"@streetwriters/showdown": "^3.0.4-alpha",
"@streetwriters/showdown": "^3.0.5-alpha",
"async-mutex": "^0.3.2",
"dayjs": "1.11.9",
"entities": "^4.3.1",
@@ -4343,12 +4343,11 @@
"@notesnook/core": {
"version": "file:../core",
"requires": {
"@microsoft/signalr": "^7.0.10",
"@microsoft/signalr-protocol-msgpack": "^7.0.10",
"@microsoft/signalr": "^8.0.0",
"@notesnook/crypto": "file:../crypto",
"@notesnook/logger": "file:../logger",
"@readme/data-urls": "^3.0.0",
"@streetwriters/showdown": "^3.0.4-alpha",
"@streetwriters/showdown": "^3.0.5-alpha",
"@types/html-to-text": "^9.0.0",
"@types/katex": "^0.16.1",
"@types/prismjs": "^1.26.0",