From 70f7aba27b1cd45a21c1e65e7bd870ae98b86830 Mon Sep 17 00:00:00 2001 From: Alexander Alemayhu Date: Mon, 19 Feb 2024 21:42:45 +0100 Subject: [PATCH] feat: add Notion Link --- src/lib/parser/DeckParser.ts | 7 ++++ src/lib/parser/Note.ts | 3 ++ src/lib/parser/Settings/Settings.ts | 8 ++++ .../BlockHandler/BlockHandler.test.ts | 19 +++++++++ .../BlockHandler/BlockHandler.ts | 40 +++++++++++++++++-- .../BlockHandler/RenderNotionLink.tsx | 20 ++++++++++ 6 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 src/services/NotionService/BlockHandler/RenderNotionLink.tsx diff --git a/src/lib/parser/DeckParser.ts b/src/lib/parser/DeckParser.ts index f6ffc434b..b116e372c 100644 --- a/src/lib/parser/DeckParser.ts +++ b/src/lib/parser/DeckParser.ts @@ -551,6 +551,7 @@ export class DeckParser { private extractCards(dom: cheerio.Root, toggleList: cheerio.Element[]) { let cards: Note[] = []; + const pageId = dom('article').attr('id'); toggleList.forEach((t) => { // We want to perserve the parent's style, so getting the class @@ -605,6 +606,12 @@ export class DeckParser { })(); const note = new Note(front || '', backSide); note.notionId = parentUL.attr('id'); + if (note.notionId && this.settings.addNotionLink) { + const link = this.getLink(pageId, note); + if (link !== null) { + note.back += link; + } + } if ( (this.settings.isAvocado && this.noteHasAvocado(note)) || (this.settings.isCherry && !this.noteHasCherry(note)) diff --git a/src/lib/parser/Note.ts b/src/lib/parser/Note.ts index 82e9d72c3..117f30cc8 100644 --- a/src/lib/parser/Note.ts +++ b/src/lib/parser/Note.ts @@ -17,6 +17,8 @@ export default class Note { notionId?: string; + notionLink?: string; + constructor(name: string, back: string) { this.name = name; this.back = back; @@ -50,6 +52,7 @@ export default class Note { this.answer = clozeCard.answer; this.media = clozeCard.media; this.notionId = clozeCard.notionId; + this.notionLink = clozeCard.notionLink; } hasRefreshIcon() { diff --git a/src/lib/parser/Settings/Settings.ts b/src/lib/parser/Settings/Settings.ts index cecfb7a65..b0b8efcda 100644 --- a/src/lib/parser/Settings/Settings.ts +++ b/src/lib/parser/Settings/Settings.ts @@ -62,6 +62,8 @@ export class Settings { parentBlockId: string; + readonly addNotionLink: boolean; + constructor(input: { [key: string]: string }) { this.deckName = input.deckName; if (this.deckName && !this.deckName.trim()) { @@ -92,6 +94,11 @@ export class Settings { this.useNotionId = input['use-notion-id'] === 'true'; this.parentBlockId = input.parentBlockId; this.pageEmoji = input['page-emoji'] || 'first_emoji'; + this.addNotionLink = input['add-notion-link'] === 'true'; + /* Is this really needed? */ + if (this.parentBlockId) { + this.addNotionLink = true; + } this.retrieveTemplates(input); } @@ -108,6 +115,7 @@ export class Settings { static LoadDefaultOptions(): { [key: string]: string } { return { + 'add-notion-link': 'false', 'use-notion-id': 'true', all: 'true', paragraph: 'false', diff --git a/src/services/NotionService/BlockHandler/BlockHandler.test.ts b/src/services/NotionService/BlockHandler/BlockHandler.test.ts index 8d486b36a..52a00c94e 100644 --- a/src/services/NotionService/BlockHandler/BlockHandler.test.ts +++ b/src/services/NotionService/BlockHandler/BlockHandler.test.ts @@ -247,6 +247,25 @@ describe('BlockHandler', () => { expect(flashcards.length).toBe(1); }); + test('Add Notion Link', async () => { + const expected = + 'https://www.notion.so/Notion-API-Test-Page-3ce6b147ac8a425f836b51cc21825b85#e5201f35c72240d38e3a5d218e5d80a5'; + const flashcards = await loadCards( + { + 'add-notion-link': 'true', + parentBlockId: examplId, + }, + examplId, + new Workspace(true, 'fs'), + new ParserRules() + ); + const card = flashcards.find((f) => + f.name.includes('1 - This is a basic card') + ); + expect(card).toBeTruthy(); + expect(card?.notionLink).toBe(expected); + }); + test.todo('Maximum One Toggle Per Card'); test.todo('Use All Toggle Lists'); test.todo('Template Options'); diff --git a/src/services/NotionService/BlockHandler/BlockHandler.ts b/src/services/NotionService/BlockHandler/BlockHandler.ts index 9f5f9c2e4..f27d7851c 100644 --- a/src/services/NotionService/BlockHandler/BlockHandler.ts +++ b/src/services/NotionService/BlockHandler/BlockHandler.ts @@ -1,4 +1,4 @@ -import { isFullBlock } from '@notionhq/client'; +import { isFullBlock, isFullPage } from '@notionhq/client'; import { AudioBlockObjectResponse, BlockObjectResponse, @@ -9,6 +9,7 @@ import { PageObjectResponse, } from '@notionhq/client/build/src/api-endpoints'; import axios from 'axios'; + import getDeckName from '../../../lib/anki/getDeckname'; import sanitizeTags from '../../../lib/anki/sanitizeTags'; import { sendError } from '../../../lib/error/sendError'; @@ -37,6 +38,7 @@ import perserveNewlinesIfApplicable from '../helpers/preserveNewlinesIfApplicabl import { renderBack } from '../helpers/renderBack'; import { toText } from './helpers/deckNameToText'; import getSubDeckName from './helpers/getSubDeckName'; +import RenderNotionLink from './RenderNotionLink'; interface Finder { parentType: string; @@ -138,10 +140,20 @@ class BlockHandler { } } + __notionLink( + id: string, + notionBaseLink: string | undefined + ): string | undefined { + return notionBaseLink + ? `${notionBaseLink}#${id.replace(/-/g, '')}` + : undefined; + } + private async getFlashcards( rules: ParserRules, flashcardBlocks: GetBlockResponse[], - tags: string[] + tags: string[], + notionBaseLink: string | undefined ): Promise { let cards = []; let counter = 0; @@ -186,6 +198,10 @@ class BlockHandler { } ankiNote.back = back!; + ankiNote.notionLink = this.__notionLink(block.id, notionBaseLink); + if (this.settings.addNotionLink) { + ankiNote.back += RenderNotionLink(ankiNote.notionLink!, this); + } ankiNote.notionId = this.settings.useNotionId ? block.id : undefined; ankiNote.media = this.exporter.media; this.exporter.media = []; @@ -291,7 +307,18 @@ class BlockHandler { }); this.settings.parentBlockId = page.id; - const cards = await this.getFlashcards(rules, cBlocks, tags); + let notionBaseLink = + this.settings.addNotionLink && this.settings.parentBlockId + ? isFullPage(page) + ? page?.url + : undefined + : undefined; + const cards = await this.getFlashcards( + rules, + cBlocks, + tags, + notionBaseLink + ); const deck = new Deck( toText(getDeckName(parentName, title)), Deck.CleanCards(cards), @@ -326,7 +353,12 @@ class BlockHandler { ); this.settings.parentBlockId = sd.id; - const cards = await this.getFlashcards(rules, cBlocks, tags); + const cards = await this.getFlashcards( + rules, + cBlocks, + tags, + undefined + ); let subDeckName = getSubDeckName(sd); decks.push( diff --git a/src/services/NotionService/BlockHandler/RenderNotionLink.tsx b/src/services/NotionService/BlockHandler/RenderNotionLink.tsx new file mode 100644 index 000000000..0c9502742 --- /dev/null +++ b/src/services/NotionService/BlockHandler/RenderNotionLink.tsx @@ -0,0 +1,20 @@ +import ReactDOMServer from 'react-dom/server'; +import BlockHandler from './BlockHandler'; + +const RenderNotionLink = ( + link: string, + handler: BlockHandler +): string | null => { + if (handler.settings?.isTextOnlyBack) { + return link; + } + return ReactDOMServer.renderToStaticMarkup( +
+ + Open in Notion + +
+ ); +}; + +export default RenderNotionLink;