diff --git a/apps/mobile/native/android/app/build.gradle b/apps/mobile/native/android/app/build.gradle index 46ef55133..6b2da688b 100644 --- a/apps/mobile/native/android/app/build.gradle +++ b/apps/mobile/native/android/app/build.gradle @@ -124,7 +124,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled true - versionCode 3077 + versionCode 3078 versionName getNpmVersion() testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/apps/mobile/native/ios/Notesnook.xcodeproj/project.pbxproj b/apps/mobile/native/ios/Notesnook.xcodeproj/project.pbxproj index f1588cec0..5d0894cbb 100644 --- a/apps/mobile/native/ios/Notesnook.xcodeproj/project.pbxproj +++ b/apps/mobile/native/ios/Notesnook.xcodeproj/project.pbxproj @@ -1095,7 +1095,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2156; + CURRENT_PROJECT_VERSION = 2157; DEVELOPMENT_TEAM = 53CWBG3QUC; ENABLE_BITCODE = NO; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; @@ -1169,7 +1169,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.3.8; + MARKETING_VERSION = 3.3.9; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -1200,7 +1200,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2156; + CURRENT_PROJECT_VERSION = 2157; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; @@ -1274,7 +1274,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 3.3.8; + MARKETING_VERSION = 3.3.9; ONLY_ACTIVE_ARCH = NO; OTHER_LDFLAGS = ( "$(inherited)", @@ -1433,7 +1433,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2156; + CURRENT_PROJECT_VERSION = 2157; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 53CWBG3QUC; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; @@ -1445,7 +1445,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.3.8; + MARKETING_VERSION = 3.3.9; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget; @@ -1476,7 +1476,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 2156; + CURRENT_PROJECT_VERSION = 2157; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC; @@ -1489,7 +1489,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 3.3.8; + MARKETING_VERSION = 3.3.9; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1519,7 +1519,7 @@ CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2156; + CURRENT_PROJECT_VERSION = 2157; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 53CWBG3QUC; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; @@ -1600,7 +1600,7 @@ "@executable_path/../../Frameworks", ); LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)"; - MARKETING_VERSION = 3.3.8; + MARKETING_VERSION = 3.3.9; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share; @@ -1631,7 +1631,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 2156; + CURRENT_PROJECT_VERSION = 2157; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC; @@ -1713,7 +1713,7 @@ "@executable_path/../../Frameworks", ); LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)"; - MARKETING_VERSION = 3.3.8; + MARKETING_VERSION = 3.3.9; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/apps/mobile/package.json b/apps/mobile/package.json index dcb426cf6..fb155e7cc 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@notesnook/mobile", - "version": "3.3.8", + "version": "3.3.9", "private": true, "license": "GPL-3.0-or-later", "workspaces": [ @@ -56,4 +56,4 @@ "react": "18.2.0", "react-native": "0.77.2" } -} \ No newline at end of file +} diff --git a/apps/web/src/common/drop-handler.ts b/apps/web/src/common/drop-handler.ts index 89048061f..183c8ab0a 100644 --- a/apps/web/src/common/drop-handler.ts +++ b/apps/web/src/common/drop-handler.ts @@ -30,13 +30,18 @@ export async function handleDrop( item: | ItemReference | Context - | { type: "trash" | "notebooks" | "favorites" | undefined } + | { type: "trash" | "notebooks" | "favorites" | "archive" | undefined } ) { if (!item.type) return; const noteIds = getDragData(dataTransfer, "note"); const notebookIds = getDragData(dataTransfer, "notebook"); - const { setColor, favorite, delete: trashNotes } = useNoteStore.getState(); + const { + setColor, + favorite, + delete: trashNotes, + archive + } = useNoteStore.getState(); switch (item.type) { case "notebook": if (noteIds.length > 0) { @@ -83,5 +88,8 @@ export async function handleDrop( await useNoteStore.getState().refresh(); } break; + case "archive": + archive(true, ...noteIds); + break; } } diff --git a/apps/web/src/common/export.ts b/apps/web/src/common/export.ts index 21d3e4822..df9804f31 100644 --- a/apps/web/src/common/export.ts +++ b/apps/web/src/common/export.ts @@ -92,7 +92,11 @@ export async function exportNotes( const { createZipStream } = await import("../utils/streams/zip-stream"); const errors: Error[] = []; - const exportStream = new ExportStream(report, (e) => errors.push(e)); + const exportStream = new ExportStream( + report, + (e) => errors.push(e), + await notes.count() + ); await fromAsyncIterator( _exportNotes(notes, { format, unlockVault: Vault.unlockVault }) ) diff --git a/apps/web/src/components/editor/tiptap.tsx b/apps/web/src/components/editor/tiptap.tsx index 3463e4526..5e3e2712f 100644 --- a/apps/web/src/components/editor/tiptap.tsx +++ b/apps/web/src/components/editor/tiptap.tsx @@ -118,7 +118,7 @@ function countCharacters(text: string) { function countParagraphs(fragment: Fragment) { let count = 0; fragment.nodesBetween(0, fragment.size, (node) => { - if (node.type.name === "paragraph") { + if (node.type.name === "paragraph" && node.content.size > 0) { count++; } return true; @@ -737,7 +737,7 @@ function toIEditor(editor: Editor): IEditor { function getSelectedParagraphs(editor: Editor, selection: Selection): number { let count = 0; editor.state.doc.nodesBetween(selection.from, selection.to, (node) => { - if (node.type.name === "paragraph") { + if (node.type.name === "paragraph" && node.content.size > 0) { count++; } return true; diff --git a/apps/web/src/components/navigation-menu/index.tsx b/apps/web/src/components/navigation-menu/index.tsx index 6f8c67637..5a7476e6e 100644 --- a/apps/web/src/components/navigation-menu/index.tsx +++ b/apps/web/src/components/navigation-menu/index.tsx @@ -506,6 +506,8 @@ function RouteItem({ ? "trash" : item.path === "/favorites" ? "favorites" + : item.path == "/archive" + ? "archive" : undefined }); }} diff --git a/apps/web/src/utils/streams/export-stream.ts b/apps/web/src/utils/streams/export-stream.ts index 9263929e4..b991f64bd 100644 --- a/apps/web/src/utils/streams/export-stream.ts +++ b/apps/web/src/utils/streams/export-stream.ts @@ -28,8 +28,13 @@ export class ExportStream extends TransformStream< > { progress = 0; constructor( - report: (progress: { text: string; current?: number }) => void, - handleError: (error: Error) => void + report: (progress: { + text: string; + current?: number; + total?: number; + }) => void, + handleError: (error: Error) => void, + totalItems?: number ) { super({ transform: async (item, controller) => { @@ -69,7 +74,8 @@ export class ExportStream extends TransformStream< controller.enqueue(item); report({ current: this.progress++, - text: `Exporting note: ${item.path}` + text: `Exporting note: ${item.path}`, + total: totalItems }); } } diff --git a/fastlane/metadata/android/en-US/changelogs/15394.txt b/fastlane/metadata/android/en-US/changelogs/15394.txt new file mode 100644 index 000000000..16b14ac7f --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/15394.txt @@ -0,0 +1,3 @@ +- Minor bug fixes and improvements + +Thank you for using Notesnook! \ No newline at end of file diff --git a/packages/core/src/api/user-manager.ts b/packages/core/src/api/user-manager.ts index 8ab3cc5c4..bd6875833 100644 --- a/packages/core/src/api/user-manager.ts +++ b/packages/core/src/api/user-manager.ts @@ -666,6 +666,15 @@ class UserManager { usesFallback: await this.usesFallbackPWHash(old_password) }); + // retrieve user keys before deriving a new encryption key + const oldUserKeys = { + attachmentsKey: await this.getAttachmentsKey(), + monographPasswordsKey: await this.getMonographPasswordsKey(), + inboxKeys: (await this.hasInboxKeys()) + ? await this.getInboxKeys() + : undefined + } as const; + await this.db.storage().deriveCryptoKey({ password: new_password, salt @@ -678,27 +687,33 @@ class UserManager { const userEncryptionKey = await this.getEncryptionKey(); if (userEncryptionKey) { const updateUserPayload: Partial = {}; - const attachmentsKey = await this.getAttachmentsKey(); - if (attachmentsKey) { + if (oldUserKeys.attachmentsKey) { user.attachmentsKey = await this.db .storage() - .encrypt(userEncryptionKey, JSON.stringify(attachmentsKey)); + .encrypt( + userEncryptionKey, + JSON.stringify(oldUserKeys.attachmentsKey) + ); updateUserPayload.attachmentsKey = user.attachmentsKey; } - const monographPasswordsKey = await this.getMonographPasswordsKey(); - if (monographPasswordsKey) { + if (oldUserKeys.monographPasswordsKey) { user.monographPasswordsKey = await this.db .storage() - .encrypt(userEncryptionKey, JSON.stringify(monographPasswordsKey)); + .encrypt( + userEncryptionKey, + JSON.stringify(oldUserKeys.monographPasswordsKey) + ); updateUserPayload.monographPasswordsKey = user.monographPasswordsKey; } - const inboxKeys = await this.getInboxKeys(); - if (inboxKeys) { + if (oldUserKeys.inboxKeys) { user.inboxKeys = { - public: inboxKeys.publicKey, + public: oldUserKeys.inboxKeys.publicKey, private: await this.db .storage() - .encrypt(userEncryptionKey, JSON.stringify(inboxKeys.privateKey)) + .encrypt( + userEncryptionKey, + JSON.stringify(oldUserKeys.inboxKeys.privateKey) + ) }; updateUserPayload.inboxKeys = user.inboxKeys; } diff --git a/packages/editor/src/extensions/heading/heading.ts b/packages/editor/src/extensions/heading/heading.ts index 465e8d471..1873f4be3 100644 --- a/packages/editor/src/extensions/heading/heading.ts +++ b/packages/editor/src/extensions/heading/heading.ts @@ -184,11 +184,14 @@ export const Heading = TiptapHeading.extend({ if (typeof pos !== "number") return; const resolvedPos = editor.state.doc.resolve(pos); - const calloutAncestor = findParentNodeClosestToPos( - resolvedPos, - (node) => node.type.name === "callout" - ); - if (calloutAncestor) return; + const forbiddenParents = ["callout", "table"]; + if ( + findParentNodeClosestToPos(resolvedPos, (node) => + forbiddenParents.includes(node.type.name) + ) + ) { + return; + } if ( isClickWithinBounds( diff --git a/packages/editor/styles/styles.css b/packages/editor/styles/styles.css index af77b9834..cec91095b 100644 --- a/packages/editor/styles/styles.css +++ b/packages/editor/styles/styles.css @@ -707,6 +707,10 @@ p > *::selection { mask-size: cover; } +.simple-checklist > li.checked p { + opacity: 0.8; +} + /* Callout */ .ProseMirror div.callout { padding: 15px; @@ -978,6 +982,21 @@ del.diffdel { display: none; } +.ProseMirror table h1::before, +.ProseMirror table h2::before, +.ProseMirror table h3::before, +.ProseMirror table h4::before, +.ProseMirror table h5::before, +.ProseMirror table h6::before, +.ProseMirror table h1::after, +.ProseMirror table h2::after, +.ProseMirror table h3::after, +.ProseMirror table h4::after, +.ProseMirror table h5::after, +.ProseMirror table h6::after { + display: none; +} + [data-hidden="true"] { display: none !important; }