Skip to content

Commit 70f7aba

Browse files
committed
feat: add Notion Link
1 parent 859dff0 commit 70f7aba

File tree

6 files changed

+93
-4
lines changed

6 files changed

+93
-4
lines changed

src/lib/parser/DeckParser.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,7 @@ export class DeckParser {
551551

552552
private extractCards(dom: cheerio.Root, toggleList: cheerio.Element[]) {
553553
let cards: Note[] = [];
554+
const pageId = dom('article').attr('id');
554555

555556
toggleList.forEach((t) => {
556557
// We want to perserve the parent's style, so getting the class
@@ -605,6 +606,12 @@ export class DeckParser {
605606
})();
606607
const note = new Note(front || '', backSide);
607608
note.notionId = parentUL.attr('id');
609+
if (note.notionId && this.settings.addNotionLink) {
610+
const link = this.getLink(pageId, note);
611+
if (link !== null) {
612+
note.back += link;
613+
}
614+
}
608615
if (
609616
(this.settings.isAvocado && this.noteHasAvocado(note)) ||
610617
(this.settings.isCherry && !this.noteHasCherry(note))

src/lib/parser/Note.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export default class Note {
1717

1818
notionId?: string;
1919

20+
notionLink?: string;
21+
2022
constructor(name: string, back: string) {
2123
this.name = name;
2224
this.back = back;
@@ -50,6 +52,7 @@ export default class Note {
5052
this.answer = clozeCard.answer;
5153
this.media = clozeCard.media;
5254
this.notionId = clozeCard.notionId;
55+
this.notionLink = clozeCard.notionLink;
5356
}
5457

5558
hasRefreshIcon() {

src/lib/parser/Settings/Settings.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ export class Settings {
6262

6363
parentBlockId: string;
6464

65+
readonly addNotionLink: boolean;
66+
6567
constructor(input: { [key: string]: string }) {
6668
this.deckName = input.deckName;
6769
if (this.deckName && !this.deckName.trim()) {
@@ -92,6 +94,11 @@ export class Settings {
9294
this.useNotionId = input['use-notion-id'] === 'true';
9395
this.parentBlockId = input.parentBlockId;
9496
this.pageEmoji = input['page-emoji'] || 'first_emoji';
97+
this.addNotionLink = input['add-notion-link'] === 'true';
98+
/* Is this really needed? */
99+
if (this.parentBlockId) {
100+
this.addNotionLink = true;
101+
}
95102

96103
this.retrieveTemplates(input);
97104
}
@@ -108,6 +115,7 @@ export class Settings {
108115

109116
static LoadDefaultOptions(): { [key: string]: string } {
110117
return {
118+
'add-notion-link': 'false',
111119
'use-notion-id': 'true',
112120
all: 'true',
113121
paragraph: 'false',

src/services/NotionService/BlockHandler/BlockHandler.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,25 @@ describe('BlockHandler', () => {
247247
expect(flashcards.length).toBe(1);
248248
});
249249

250+
test('Add Notion Link', async () => {
251+
const expected =
252+
'https://www.notion.so/Notion-API-Test-Page-3ce6b147ac8a425f836b51cc21825b85#e5201f35c72240d38e3a5d218e5d80a5';
253+
const flashcards = await loadCards(
254+
{
255+
'add-notion-link': 'true',
256+
parentBlockId: examplId,
257+
},
258+
examplId,
259+
new Workspace(true, 'fs'),
260+
new ParserRules()
261+
);
262+
const card = flashcards.find((f) =>
263+
f.name.includes('1 - This is a basic card')
264+
);
265+
expect(card).toBeTruthy();
266+
expect(card?.notionLink).toBe(expected);
267+
});
268+
250269
test.todo('Maximum One Toggle Per Card');
251270
test.todo('Use All Toggle Lists');
252271
test.todo('Template Options');

src/services/NotionService/BlockHandler/BlockHandler.ts

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isFullBlock } from '@notionhq/client';
1+
import { isFullBlock, isFullPage } from '@notionhq/client';
22
import {
33
AudioBlockObjectResponse,
44
BlockObjectResponse,
@@ -9,6 +9,7 @@ import {
99
PageObjectResponse,
1010
} from '@notionhq/client/build/src/api-endpoints';
1111
import axios from 'axios';
12+
1213
import getDeckName from '../../../lib/anki/getDeckname';
1314
import sanitizeTags from '../../../lib/anki/sanitizeTags';
1415
import { sendError } from '../../../lib/error/sendError';
@@ -37,6 +38,7 @@ import perserveNewlinesIfApplicable from '../helpers/preserveNewlinesIfApplicabl
3738
import { renderBack } from '../helpers/renderBack';
3839
import { toText } from './helpers/deckNameToText';
3940
import getSubDeckName from './helpers/getSubDeckName';
41+
import RenderNotionLink from './RenderNotionLink';
4042

4143
interface Finder {
4244
parentType: string;
@@ -138,10 +140,20 @@ class BlockHandler {
138140
}
139141
}
140142

143+
__notionLink(
144+
id: string,
145+
notionBaseLink: string | undefined
146+
): string | undefined {
147+
return notionBaseLink
148+
? `${notionBaseLink}#${id.replace(/-/g, '')}`
149+
: undefined;
150+
}
151+
141152
private async getFlashcards(
142153
rules: ParserRules,
143154
flashcardBlocks: GetBlockResponse[],
144-
tags: string[]
155+
tags: string[],
156+
notionBaseLink: string | undefined
145157
): Promise<Note[]> {
146158
let cards = [];
147159
let counter = 0;
@@ -186,6 +198,10 @@ class BlockHandler {
186198
}
187199

188200
ankiNote.back = back!;
201+
ankiNote.notionLink = this.__notionLink(block.id, notionBaseLink);
202+
if (this.settings.addNotionLink) {
203+
ankiNote.back += RenderNotionLink(ankiNote.notionLink!, this);
204+
}
189205
ankiNote.notionId = this.settings.useNotionId ? block.id : undefined;
190206
ankiNote.media = this.exporter.media;
191207
this.exporter.media = [];
@@ -291,7 +307,18 @@ class BlockHandler {
291307
});
292308
this.settings.parentBlockId = page.id;
293309

294-
const cards = await this.getFlashcards(rules, cBlocks, tags);
310+
let notionBaseLink =
311+
this.settings.addNotionLink && this.settings.parentBlockId
312+
? isFullPage(page)
313+
? page?.url
314+
: undefined
315+
: undefined;
316+
const cards = await this.getFlashcards(
317+
rules,
318+
cBlocks,
319+
tags,
320+
notionBaseLink
321+
);
295322
const deck = new Deck(
296323
toText(getDeckName(parentName, title)),
297324
Deck.CleanCards(cards),
@@ -326,7 +353,12 @@ class BlockHandler {
326353
);
327354

328355
this.settings.parentBlockId = sd.id;
329-
const cards = await this.getFlashcards(rules, cBlocks, tags);
356+
const cards = await this.getFlashcards(
357+
rules,
358+
cBlocks,
359+
tags,
360+
undefined
361+
);
330362
let subDeckName = getSubDeckName(sd);
331363

332364
decks.push(
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import ReactDOMServer from 'react-dom/server';
2+
import BlockHandler from './BlockHandler';
3+
4+
const RenderNotionLink = (
5+
link: string,
6+
handler: BlockHandler
7+
): string | null => {
8+
if (handler.settings?.isTextOnlyBack) {
9+
return link;
10+
}
11+
return ReactDOMServer.renderToStaticMarkup(
12+
<div style={{ textAlign: 'center' }}>
13+
<a style={{ textDecoration: 'none', color: 'grey' }} href={link}>
14+
Open in Notion
15+
</a>
16+
</div>
17+
);
18+
};
19+
20+
export default RenderNotionLink;

0 commit comments

Comments
 (0)