diff --git a/apps/web/src/common/multi-select.ts b/apps/web/src/common/multi-select.ts index f853c0409..c191644aa 100644 --- a/apps/web/src/common/multi-select.ts +++ b/apps/web/src/common/multi-select.ts @@ -31,9 +31,15 @@ import { pluralize } from "@notesnook/common"; async function moveNotesToTrash(ids: string[], confirm = true) { if (confirm && !(await showMultiDeleteConfirmation(ids.length))) return; - const lockedIds = await db.notes.locked.ids(); - if (ids.some((id) => lockedIds.includes(id)) && !(await Vault.unlockVault())) - return; + const vault = await db.vaults.default(); + if (vault) { + const lockedIds = await db.relations.from(vault, "note").get(); + if ( + ids.some((id) => lockedIds.findIndex((s) => s.toId === id) > -1) && + !(await Vault.unlockVault()) + ) + return; + } const items = ids.filter((id) => !db.monographs.isPublished(id)); diff --git a/apps/web/src/components/attachment/index.tsx b/apps/web/src/components/attachment/index.tsx index 3e99a29a9..6cb68c3cd 100644 --- a/apps/web/src/components/attachment/index.tsx +++ b/apps/web/src/components/attachment/index.tsx @@ -288,7 +288,7 @@ const AttachmentMenuItems: ( onClick: async () => { const isDownloading = status?.type === "download"; if (isDownloading) { - await db.fs().cancel(attachment.hash, "download"); + await db.fs().cancel(attachment.hash); } else await saveAttachment(attachment.hash); } }, @@ -300,7 +300,7 @@ const AttachmentMenuItems: ( onClick: async () => { const isDownloading = status?.type === "upload"; if (isDownloading) { - await db.fs().cancel(attachment.hash, "upload"); + await db.fs().cancel(attachment.hash); } else await reuploadAttachment(attachment.type, attachment.hash); } }, diff --git a/apps/web/src/components/editor/index.tsx b/apps/web/src/components/editor/index.tsx index e8909b60d..903322ac4 100644 --- a/apps/web/src/components/editor/index.tsx +++ b/apps/web/src/components/editor/index.tsx @@ -61,7 +61,6 @@ import { MaybeDeletedItem, isDeleted } from "@notesnook/core/dist/types"; -import { isEncryptedContent } from "@notesnook/core/dist/collections/content"; import { PreviewSession } from "./types"; const PDFPreview = React.lazy(() => import("../pdf-preview")); @@ -89,7 +88,7 @@ export default function EditorManager({ noteId, nonce }: { - noteId?: string; + noteId?: string | number; nonce?: string; }) { const isNewSession = !!nonce && !noteId; @@ -136,7 +135,7 @@ export default function EditorManager({ if (id && isContent && editorInstance.current) { let content: string | null = null; - if (locked && isEncryptedContent(item)) { + if (locked && item.locked) { const result = await db.vault .decryptContent(item, item.noteId) .catch(() => undefined); @@ -181,7 +180,7 @@ export default function EditorManager({ }, [isNewSession, nonce]); useEffect(() => { - if (!isOldSession) return; + if (!isOldSession || typeof noteId === "number") return; openSession(noteId); }, [noteId]); @@ -210,7 +209,9 @@ export default function EditorManager({ {previewSession.current && noteId && ( openSession(noteId)} + onDiscard={() => + typeof noteId === "string" && openSession(noteId) + } /> )} void; }; type EditorProps = { - id: string | number; + id?: string | number; content: () => string | undefined; nonce?: number; options?: EditorOptions; @@ -393,7 +394,7 @@ export function Editor(props: EditorProps) { const dataurl = await downloadAttachment( hash, "base64", - id.toString() + id?.toString() ); if (!dataurl) return showToast("error", "This image cannot be previewed."); @@ -411,7 +412,7 @@ export function Editor(props: EditorProps) { ); } else if (attachment && onPreviewDocument) { onPreviewDocument({ hash }); - const blob = await downloadAttachment(hash, "blob", id.toString()); + const blob = await downloadAttachment(hash, "blob", id?.toString()); if (!blob) return; onPreviewDocument({ url: URL.createObjectURL(blob), hash }); } @@ -427,7 +428,7 @@ export function Editor(props: EditorProps) { return downloadAttachment( attachment.hash, attachment.type === "web-clip" ? "text" : "base64", - id.toString() + id?.toString() ); }} onAttachFile={async (file) => { diff --git a/apps/web/src/components/editor/picker.ts b/apps/web/src/components/editor/picker.ts index 72ea9cea1..9b9cf923b 100644 --- a/apps/web/src/components/editor/picker.ts +++ b/apps/web/src/components/editor/picker.ts @@ -190,8 +190,8 @@ async function addAttachment( metadata: { hash, hashType, - filename: exists?.metadata.filename || file.name, - type: exists?.metadata.type || file.type + filename: exists?.filename || file.name, + type: exists?.type || file.type }, key }); diff --git a/apps/web/src/components/filtered-list/index.tsx b/apps/web/src/components/filtered-list/index.tsx index 5839dc59e..abc20e0bd 100644 --- a/apps/web/src/components/filtered-list/index.tsx +++ b/apps/web/src/components/filtered-list/index.tsx @@ -25,21 +25,20 @@ import { VirtualizedList, VirtualizedListProps } from "../virtualized-list"; type FilteredListProps = { placeholders: { filter: string; empty: string }; - filter: (query: string) => Promise; + filter: (query: string) => Promise; onCreateNewItem: (title: string) => Promise; } & VirtualizedListProps; export function FilteredList(props: FilteredListProps) { const { items, filter, onCreateNewItem, placeholders, ...listProps } = props; - const [filteredItems, setFilteredItems] = useState([]); const [query, setQuery] = useState(); - const noItemsFound = filteredItems.length <= 0 && query && query.length > 0; + const noItemsFound = items.length <= 0 && query && query.length > 0; const inputRef = useRef(null); const _filter = useCallback( async (query = "") => { - setFilteredItems(query ? await filter(query) : []); + await filter(query); setQuery(query); }, [filter] @@ -61,7 +60,7 @@ export function FilteredList(props: FilteredListProps) { data-test-id={"filter-input"} autoFocus placeholder={ - filteredItems.length <= 0 ? placeholders.empty : placeholders.filter + items.length <= 0 ? placeholders.empty : placeholders.filter } onChange={(e) => _filter((e.target as HTMLInputElement).value)} onKeyUp={async (e) => { @@ -70,7 +69,7 @@ export function FilteredList(props: FilteredListProps) { } }} action={ - filteredItems.length <= 0 && !!query + items.length <= 0 && !!query ? { icon: Plus, onClick: async () => await _createNewItem(query) @@ -95,7 +94,7 @@ export function FilteredList(props: FilteredListProps) { ) : ( - + )} ); diff --git a/apps/web/src/components/session-item/index.tsx b/apps/web/src/components/session-item/index.tsx index 6d9868160..85c7b6b2d 100644 --- a/apps/web/src/components/session-item/index.tsx +++ b/apps/web/src/components/session-item/index.tsx @@ -69,7 +69,6 @@ export function SessionItem(props: SessionItemProps) { try { const decryptedContent = await db.vault.decryptContent( content, - noteId, password ); onOpenPreviewSession({ diff --git a/apps/web/src/dialogs/add-tags-dialog.tsx b/apps/web/src/dialogs/add-tags-dialog.tsx index 90f0a89c3..374115acf 100644 --- a/apps/web/src/dialogs/add-tags-dialog.tsx +++ b/apps/web/src/dialogs/add-tags-dialog.tsx @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { Flex, Text } from "@theme-ui/components"; import { CheckCircleOutline, @@ -34,6 +34,7 @@ import { FilteredList } from "../components/filtered-list"; import { ItemReference, Tag } from "@notesnook/core/dist/types"; import { ResolvedItem } from "../components/list-container/resolved-item"; import { create } from "zustand"; +import { VirtualizedGrouping } from "@notesnook/core"; type SelectedReference = { id: string; @@ -58,14 +59,18 @@ export type AddTagsDialogProps = { function AddTagsDialog(props: AddTagsDialogProps) { const { onClose, noteIds } = props; - const tags = useStore((store) => store.tags); + const [tags, setTags] = useState | undefined>( + () => useStore.getState().tags + ); useEffect(() => { (async function () { if (!tags) { await useStore.getState().refresh(); + setTags(useStore.getState().tags); return; } + const selected: SelectedReference[] = []; const selectedTags = await db.relations .to({ type: "note", ids: noteIds }, "tag") @@ -76,7 +81,7 @@ function AddTagsDialog(props: AddTagsDialogProps) { }); useSelectionStore.getState().setSelected(selected); })(); - }, [tags, noteIds]); + }, []); return ( db.lookup.tags(query).ids()} + filter={async (query) => { + setTags( + query + ? await db.lookup.tags(query).sorted() + : useStore.getState().tags + ); + }} onCreateNewItem={async (title) => { const tagId = await db.tags.add({ title }); if (!tagId) return; @@ -202,7 +213,3 @@ function SelectedCheck({ id, size = 20 }: { id: string; size?: number }) { ); } - -function tagHasNotes(tagId: string, noteIds: string[]) { - return db.relations.from({ type: "tag", id: tagId }, "note").has(...noteIds); -} diff --git a/apps/web/src/stores/editor-store.ts b/apps/web/src/stores/editor-store.ts index c43c55109..7a0133c9a 100644 --- a/apps/web/src/stores/editor-store.ts +++ b/apps/web/src/stores/editor-store.ts @@ -135,7 +135,7 @@ class EditorStore extends BaseStore { openSession = async (noteId: string, force = false) => { const session = this.get().session; - if (session.id) await db.fs().cancel(session.id, "download"); + if (session.id) await db.fs().cancel(session.id); if (session.id === noteId && !force) return; if (session.state === SESSION_STATES.unlocked) { @@ -285,7 +285,7 @@ class EditorStore extends BaseStore { newSession = async (nonce?: string) => { const context = noteStore.get().context; const session = this.get().session; - if (session.id) await db.fs().cancel(session.id, "download"); + if (session.id) await db.fs().cancel(session.id); this.set((state) => { state.session = { @@ -302,7 +302,7 @@ class EditorStore extends BaseStore { clearSession = async (shouldNavigate = true) => { const session = this.get().session; - if (session.id) await db.fs().cancel(session.id, "download"); + if (session.id) await db.fs().cancel(session.id); this.set((state) => { state.session = { diff --git a/apps/web/src/utils/streams/attachment-stream.ts b/apps/web/src/utils/streams/attachment-stream.ts index 23cd55252..b4d9013ca 100644 --- a/apps/web/src/utils/streams/attachment-stream.ts +++ b/apps/web/src/utils/streams/attachment-stream.ts @@ -36,7 +36,7 @@ export class AttachmentStream extends ReadableStream { const counters: Record = {}; if (signal) signal.onabort = async () => { - await db.fs().cancel(GROUP_ID, "download"); + await db.fs().cancel(GROUP_ID); }; super({ diff --git a/apps/web/src/utils/streams/export-stream.ts b/apps/web/src/utils/streams/export-stream.ts index 9e4b02d48..f8fee1279 100644 --- a/apps/web/src/utils/streams/export-stream.ts +++ b/apps/web/src/utils/streams/export-stream.ts @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +import { db } from "../../common/db"; import { exportNote } from "../../common/export"; import { makeUniqueFilename } from "./utils"; import { ZipFile } from "./zip-stream"; @@ -44,41 +45,18 @@ export class ExportStream extends TransformStream { const { filename, content } = result; - const notebooks = [ - ...( - db.relations - ?.to({ id: note.id, type: "note" }, "notebook") - .resolved() || [] - ).map((n) => ({ title: n.title, topics: [] })), - ...(note.notebooks || []).map( - (ref: { id: string; topics: string[] }) => { - const notebook = db.notebooks?.notebook(ref.id); - const topics: any[] = notebook?.topics.all || []; + const notebooks = await db.relations + .to({ id: note.id, type: "note" }, "notebook") + .get(); - return { - title: notebook?.title, - topics: ref.topics - .map((topicId: string) => - topics.find((topic) => topic.id === topicId) - ) - .filter(Boolean) - }; - } - ) - ]; - - const filePaths: Array = - notebooks.length > 0 - ? notebooks - .map((notebook) => { - if (notebook.topics.length > 0) - return notebook.topics.map((topic: { title: string }) => - [notebook.title, topic.title, filename].join("/") - ); - return [notebook.title, filename].join("/"); - }) - .flat() - : [filename]; + const filePaths: string[] = []; + for (const { fromId: notebookId } of notebooks) { + const crumbs = (await db.notebooks.breadcrumbs(notebookId)).map( + (n) => n.title + ); + filePaths.push([...crumbs, filename].join("/")); + } + if (filePaths.length <= 0) filePaths.push(filename); filePaths.forEach((filePath) => { controller.enqueue({