From ccb9c492a85e6c7b626a65cd9803717c009158db Mon Sep 17 00:00:00 2001 From: Abdullah Atta Date: Sat, 9 Mar 2024 23:16:55 +0500 Subject: [PATCH] core: add block ids before returning content --- packages/core/src/api/vault.ts | 27 ++++++++----- packages/core/src/collections/content.ts | 35 +++++++++++++++++ packages/core/src/content-types/tiptap.ts | 48 +++++++++++++++++------ packages/core/src/utils/content-block.ts | 6 ++- 4 files changed, 94 insertions(+), 22 deletions(-) diff --git a/packages/core/src/api/vault.ts b/packages/core/src/api/vault.ts index e21696e15..45f5be803 100644 --- a/packages/core/src/api/vault.ts +++ b/packages/core/src/api/vault.ts @@ -191,7 +191,12 @@ export default class Vault { const note = await this.db.notes.note(noteId); if (!note) return; - const content = await this.unlockNote(noteId, password, false); + const content = await this.unlockNote( + noteId, + password || this.password, + false + ); + if (password) { this.password = password; if (!(await this.exists())) await this.create(password); @@ -260,18 +265,10 @@ export default class Vault { .storage() .decrypt({ password }, encryptedContent.data); - const content: NoteContent = { + return >{ type: encryptedContent.type, data: JSON.parse(decryptedContent) }; - - // #MIGRATION: convert tiny to tiptap - if (content.type === "tiny") { - content.type = "tiptap"; - content.data = tinyToTiptap(content.data); - } - - return content; } private async lockNote( @@ -335,6 +332,16 @@ export default class Vault { if (!content || !content.locked) return; const decryptedContent = await this.decryptContent(content, password); + if (this.db.content.preProcess(decryptedContent)) { + if (!password) password = await this.getVaultPassword(); + await this.encryptContent( + decryptedContent, + noteId, + password, + `${Date.now}` + ); + } + if (perm) { await this.db.notes.add({ id: noteId, diff --git a/packages/core/src/collections/content.ts b/packages/core/src/collections/content.ts index 0d1199cab..dde305c71 100644 --- a/packages/core/src/collections/content.ts +++ b/packages/core/src/collections/content.ts @@ -34,6 +34,7 @@ import { getOutputType } from "./attachments"; import { SQLCollection } from "../database/sql-collection"; import { NoteContent } from "./session-content"; import { InternalLink } from "../utils/internal-link"; +import { tinyToTiptap } from "../migrations"; export const EMPTY_CONTENT = (noteId: string): UnencryptedContentItem => ({ noteId, @@ -157,6 +158,12 @@ export class Content implements ICollection { async get(id: string) { const content = await this.collection.get(id); if (!content || isDeleted(content)) return; + if (!content.locked && this.preProcess(content)) { + await this.db.content.add({ + ...content, + sessionId: `${Date.now()}` + }); + } return content; } @@ -208,6 +215,12 @@ export class Content implements ICollection { .selectAll() .executeTakeFirst()) as ContentItem; if (!content || isDeleted(content)) return; + if (!content.locked && this.preProcess(content)) { + await this.db.content.add({ + ...content, + sessionId: `${Date.now()}` + }); + } return content; } @@ -300,6 +313,28 @@ export class Content implements ICollection { await this.add(contentItem); } + preProcess(content: NoteContent) { + let changed = false; + + // #MIGRATION: convert tiny to tiptap + if (content.type === "tiny") { + content.type = "tiptap"; + content.data = tinyToTiptap(content.data); + changed = true; + } + + // add block id on all appropriate nodes + if (!content.data.includes("data-block-id")) { + content.data = getContentFromData( + content.type, + content.data + ).insertBlockIds(); + changed = true; + } + + return changed; + } + async postProcess(contentItem: NoteContent & { noteId: string }) { const content = getContentFromData(contentItem.type, contentItem.data); if (!content) return contentItem.data; diff --git a/packages/core/src/content-types/tiptap.ts b/packages/core/src/content-types/tiptap.ts index 0ce393d8f..049dc91c4 100644 --- a/packages/core/src/content-types/tiptap.ts +++ b/packages/core/src/content-types/tiptap.ts @@ -92,6 +92,34 @@ export class Tiptap { return tokens.some((token) => lowercase.indexOf(token) > -1); } + insertBlockIds() { + let index = 0; + return new HTMLRewriter({ + ontag(name, attr) { + switch (name) { + case "p": + case "h1": + case "h2": + case "h3": + case "h4": + case "h5": + case "h6": + case "blockquote": + case "ul": + case "ol": + case "pre": + case "img": + case "iframe": + case "div": + return { + name, + attr: { ...attr, [ATTRIBUTES.blockId]: `${name}${++index}` } + }; + } + } + }).transform(this.data); + } + async insertMedia(resolve: ResolveHashes) { const hashes: string[] = []; new HTMLParser({ @@ -124,17 +152,15 @@ export class Tiptap { if (types.includes("blocks")) { result.blocks.push( - ...document.childNodes - .filter((element): element is Element => { - return isTag(element) && !!element.attribs[ATTRIBUTES.blockId]; - }) - .map((node) => ({ - id: node.attribs[ATTRIBUTES.blockId], - type: node.tagName.toLowerCase(), - content: convertHtmlToTxt( - this.data.slice(node.startIndex || 0, node.endIndex || 0) - ) - })) + ...findAll((element): element is Element => { + return isTag(element) && !!element.attribs[ATTRIBUTES.blockId]; + }, document.childNodes).map((node) => ({ + id: node.attribs[ATTRIBUTES.blockId], + type: node.tagName.toLowerCase(), + content: convertHtmlToTxt( + this.data.slice(node.startIndex || 0, node.endIndex || 0) + ) + })) ); } diff --git a/packages/core/src/utils/content-block.ts b/packages/core/src/utils/content-block.ts index 58443c460..d38d4242b 100644 --- a/packages/core/src/utils/content-block.ts +++ b/packages/core/src/utils/content-block.ts @@ -90,7 +90,11 @@ export function highlightInternalLinks( return highlighted; } -function ellipsize(text: string, maxLength: number, from: "start" | "end") { +export function ellipsize( + text: string, + maxLength: number, + from: "start" | "end" +) { const needsTruncation = text.length > maxLength; const offsets = needsTruncation ? from === "start"