diff --git a/src/lib/anki/getDeckname.test.ts b/src/lib/anki/getDeckname.test.ts index 4902b820e..9c932320e 100644 --- a/src/lib/anki/getDeckname.test.ts +++ b/src/lib/anki/getDeckname.test.ts @@ -2,7 +2,7 @@ import getDeckName from './getDeckname'; describe('getDeckname', () => { it('has no parent', () => { - expect(getDeckName(undefined, 'test')).toBe('test'); + expect(getDeckName('', 'test')).toBe('test'); }); it('has parent', () => { expect(getDeckName('parent', 'test')).toBe('parent::test'); diff --git a/src/lib/anki/getDeckname.ts b/src/lib/anki/getDeckname.ts index 6fcdc78a2..07c4c791d 100644 --- a/src/lib/anki/getDeckname.ts +++ b/src/lib/anki/getDeckname.ts @@ -5,10 +5,7 @@ * In the real world this does not really matter but adding this note in case that assumption * changes. */ -export default function getDeckName( - parent: string | undefined, - name: string -): string { +export default function getDeckName(parent: string, name: string): string { if (parent && parent !== name) { return `${parent}::${name}`; } diff --git a/src/lib/anki/zip.tsx b/src/lib/anki/zip.tsx index eef04693d..5b3b131e7 100644 --- a/src/lib/anki/zip.tsx +++ b/src/lib/anki/zip.tsx @@ -1,5 +1,5 @@ import JSZip from 'jszip'; -import { unzipSync, strFromU8 } from 'fflate'; +import { strFromU8, unzipSync } from 'fflate'; import { renderToStaticMarkup } from 'react-dom/server'; diff --git a/src/lib/jobs/helpers/deleteOldUploads.ts b/src/lib/jobs/helpers/deleteOldUploads.ts index e13681984..2a394993d 100644 --- a/src/lib/jobs/helpers/deleteOldUploads.ts +++ b/src/lib/jobs/helpers/deleteOldUploads.ts @@ -2,34 +2,53 @@ import { Knex } from 'knex'; import { TIME_21_MINUTES_AS_SECONDS } from '../../constants'; import StorageHandler from '../../storage/StorageHandler'; +import { Upload } from '../../storage/types'; +import { S3 } from 'aws-sdk'; export const MS_21 = TIME_21_MINUTES_AS_SECONDS * 1000; +const isFileOld = (file: S3.Object) => { + const now = new Date(); + if (file.LastModified) { + return now.getMilliseconds() - file.LastModified.getMilliseconds() > MS_21; + } + return false; +}; + +const hasOwner = async ( + key: S3.ObjectKey | undefined, + db: Knex +): Promise => { + if (!key) { + return false; + } + const upload = (await db('uploads') + .where('key', key) + .returning('owner')) as Upload; + return Boolean(upload.owner); +}; + export default async function deleteOldUploads(db: Knex) { const s = new StorageHandler(); const files = await s.getContents(); - const now = new Date(); if (!files) { return; } for (const file of files) { - /* @ts-ignore */ - if (now - new Date(file.LastModified) > MS_21) { - const upload = await db('uploads') - .where('key', file.Key) - .returning('owner'); - /* @ts-ignore */ - if (upload.owner) { - console.info('file has an owner, skipping'); - continue; - } else { - await s.delete(file); - console.debug( - `Delete **** which was last modified on ${file.LastModified}` - ); - } + if (!file || !isFileOld(file)) { + continue; + } + + if (await hasOwner(file.Key, db)) { + console.info('file has an owner, skipping'); + continue; } + + await s.delete(file); + console.debug( + `Delete **** which was last modified on ${file.LastModified}` + ); } } diff --git a/src/lib/misc/TokenHandler.ts b/src/lib/misc/TokenHandler.ts index eafc3e445..1c830bcd6 100644 --- a/src/lib/misc/TokenHandler.ts +++ b/src/lib/misc/TokenHandler.ts @@ -89,7 +89,6 @@ class TokenHandler { return false; } const user = await DB('users').where({ reset_token: token }).first(); - /* @ts-ignore */ return user && user.reset_token; } @@ -135,13 +134,15 @@ class TokenHandler { jwt.sign( { userId }, process.env.SECRET!, + // TODO: let user decide expiry // { expiresIn: "1d" }, - /* @ts-ignore */ - (error, token) => { + (error: Error | null, token: string | undefined) => { if (error) { reject(error); - } else { + } else if (token) { resolve(token); + } else { + reject(new Error('Token is undefined')); } } ); diff --git a/src/lib/notion/BlockHandler.test.ts b/src/lib/notion/BlockHandler.test.ts index abf3a0c50..ec48587ad 100644 --- a/src/lib/notion/BlockHandler.test.ts +++ b/src/lib/notion/BlockHandler.test.ts @@ -11,6 +11,7 @@ import Workspace from '../parser/WorkSpace'; import BlockHandler from './BlockHandler'; import { pageId as examplId } from '../../test/test-utils'; import MockNotionAPI from './_mock/MockNotionAPI'; +import { getToggleBlocks } from './helpers/getToggleBlocks'; dotenv.config({ path: 'test/.env' }); const api = new MockNotionAPI(process.env.NOTION_KEY!); @@ -67,8 +68,7 @@ describe('BlockHandler', () => { '07a7b319183642b9afecdcc4c456f73d', true ); - /* @ts-ignore */ - const topLevelToggles = blocks.results.filter((t) => t.type === 'toggle'); + const topLevelToggles = getToggleBlocks(blocks.results); expect(topLevelToggles.length).toEqual(14); }); diff --git a/src/lib/notion/BlockHandler.ts b/src/lib/notion/BlockHandler.ts index 1d98ce391..7d3c7e304 100644 --- a/src/lib/notion/BlockHandler.ts +++ b/src/lib/notion/BlockHandler.ts @@ -4,6 +4,7 @@ import fs from 'fs'; import { AudioBlockObjectResponse, BlockObjectResponse, + ColumnBlockObjectResponse, FileBlockObjectResponse, GetBlockResponse, ImageBlockObjectResponse, @@ -26,7 +27,6 @@ import getInputCard from './helpers/getInputCard'; import getColumn from './helpers/getColumn'; import isColumnList from './helpers/isColumnList'; import isTesting from './helpers/isTesting'; -import renderFront from './helpers/renderFront'; import perserveNewlinesIfApplicable from './helpers/preserveNewlinesIfApplicable'; import getDeckName from '../anki/getDeckname'; import getUniqueFileName from '../misc/getUniqueFileName'; @@ -37,6 +37,7 @@ import { getImageUrl } from './helpers/getImageUrl'; import { getAudioUrl } from './helpers/getAudioUrl'; import { getFileUrl } from './helpers/getFileUrl'; import { isFullBlock, isFullPage } from '@notionhq/client'; +import { blockToStaticMarkup } from './helpers/blockToStaticMarkup'; interface Finder { parentType: string; @@ -121,12 +122,11 @@ class BlockHandler { block: GetBlockResponse, handleChildren?: boolean ): Promise { - let response: ListBlockChildrenResponse | null; - + let response2: ListBlockChildrenResponse | null; try { - response = await this.api.getBlocks(block.id, this.useAll); - const requestChildren = response.results; - return await renderBack(this, requestChildren, response, handleChildren); + response2 = await this.api.getBlocks(block.id, this.useAll); + const requestChildren = response2.results; + return await renderBack(this, requestChildren, response2, handleChildren); } catch (e: unknown) { captureException(e); return null; @@ -153,16 +153,26 @@ class BlockHandler { for (const block of flashcardBlocks) { // Assume it's a basic card then check for children - const name = await renderFront(block, this); + const name = await blockToStaticMarkup( + this, + block as BlockObjectResponse + ); let back: null | string = ''; if (isColumnList(block) && rules.useColums()) { const secondColumn = await getColumn(block.id, this, 1); if (secondColumn) { - back = await BlockColumn(secondColumn, this); + back = await BlockColumn( + secondColumn as ColumnBlockObjectResponse, + this + ); } } else { back = await this.getBackSide(block); } + if (!name) { + console.debug('name is not valid for front, skipping', name, back); + continue; + } const ankiNote = new Note(name, back || ''); ankiNote.media = this.exporter.media; let isBasicType = true; @@ -344,9 +354,7 @@ class BlockHandler { path.join(__dirname, '../../templates/notion.css'), 'utf8' ); - // @ts-ignore - const block = sd[sd.type]; - let subDeckName = getSubDeckName(block); + let subDeckName = getSubDeckName(sd); decks.push( new Deck( diff --git a/src/lib/notion/NotionAPIWrapper.ts b/src/lib/notion/NotionAPIWrapper.ts index 8fba0a37b..0317d41f1 100644 --- a/src/lib/notion/NotionAPIWrapper.ts +++ b/src/lib/notion/NotionAPIWrapper.ts @@ -1,17 +1,21 @@ -import { Client } from '@notionhq/client'; +import { Client, isFullBlock, isFullDatabase } from '@notionhq/client'; import { + BlockObjectRequest, GetBlockResponse, GetDatabaseResponse, GetPageResponse, ListBlockChildrenResponse, QueryDatabaseResponse, + SearchResponse, } from '@notionhq/client/build/src/api-endpoints'; -import axios from 'axios'; import sanitizeTags from '../anki/sanitizeTags'; import ParserRules from '../parser/ParserRules'; import Settings from '../parser/Settings'; -import isHeading from './helpers/isHeading'; -import { ParagraphBlockObject } from './types'; +import { getParagraphBlocks } from './helpers/getParagraphBlocks'; +import renderIcon from './helpers/renderIcon'; +import getBlockIcon, { WithIcon } from './blocks/getBlockIcon'; +import { isHeading } from './helpers/isHeading'; +import { getHeadingText } from './helpers/getHeadingText'; const ANON_LIMIT = 21 * 2; const PATREON_LIMIT = 100 * 2; @@ -74,11 +78,10 @@ class NotionAPIWrapper { async createBlock( parent: string, - newBlock: ParagraphBlockObject + newBlock: BlockObjectRequest ): Promise { return this.notion.blocks.children.append({ block_id: parent, - /* @ts-ignore */ children: [newBlock], }); } @@ -99,16 +102,14 @@ class NotionAPIWrapper { if (all && response.has_more && response.next_cursor) { while (true) { - /* @ts-ignore */ - const { results, next_cursor: nextCursor } = - await this.notion.databases.query({ - database_id: id, - page_size: all ? PATREON_LIMIT : ANON_LIMIT, - start_cursor: response.next_cursor!, - }); - response.results.push(...results); - if (nextCursor) { - response.next_cursor = nextCursor; + const res2: QueryDatabaseResponse = await this.notion.databases.query({ + database_id: id, + page_size: all ? PATREON_LIMIT : ANON_LIMIT, + start_cursor: response.next_cursor!, + }); + response.results.push(...res2.results); + if (res2.next_cursor) { + response.next_cursor = res2.next_cursor; } else { break; } @@ -130,8 +131,7 @@ class NotionAPIWrapper { if (all && response.has_more && response.next_cursor) { while (true) { - /* @ts-ignore */ - const { results, next_cursor: nextCursor } = await this.notion.search({ + const res2: SearchResponse = await this.notion.search({ page_size: all ? PATREON_LIMIT : ANON_LIMIT, query, start_cursor: response.next_cursor!, @@ -140,9 +140,9 @@ class NotionAPIWrapper { timestamp: 'last_edited_time', }, }); - response.results.push(...results); - if (nextCursor) { - response.next_cursor = nextCursor; + response.results.push(...res2.results); + if (res2.next_cursor) { + response.next_cursor = res2.next_cursor; } else { break; } @@ -163,35 +163,33 @@ class NotionAPIWrapper { if (useHeadings) { const headings = response.results.filter((block) => isHeading(block)); for (const heading of headings) { - /* @ts-ignore */ - const t = heading.type; - /* @ts-ignore */ - const text = heading[t].text[0]; - globalTags.push(text.plain_text); + if (isFullBlock(heading)) { + const newTag = getHeadingText(heading) + ?.map((t) => t.plain_text) + .join(''); + if (newTag) { + globalTags.push(newTag); + } + } } } else { - const paragraphs = response.results.filter( - /* @ts-ignore */ - (block) => block.type === 'paragraph' - ); + const paragraphs = getParagraphBlocks(response.results); for (const p of paragraphs) { - /* @ts-ignore */ const pp = p.paragraph; if (!pp) { continue; } - const tt = pp.text; - /* @ts-ignore */ + const tt = pp.rich_text; if (!tt || tt.length < 1) { continue; } - const { annotations } = tt[0]; - if (annotations.strikethrough) { - globalTags.push(tt[0].text.content); - } + // const { annotations } = tt[0]; + // if (annotations.strikethrough) { + // globalTags.push(tt[0].text.content); + // } } } return sanitizeTags(globalTags); @@ -205,39 +203,9 @@ class NotionAPIWrapper { return ''; } let title = `Untitled: ${new Date()}`; - let icon = ''; + let icon = renderIcon(getBlockIcon(page as WithIcon, settings.pageEmoji)); - /* @ts-ignore */ - if (page.icon && settings.pageEmoji !== 'disable_emoji') { - /* @ts-ignore */ - const pageIcon = page.icon; - if (pageIcon.type === 'external') { - icon = ` `; - } else if (pageIcon.type === 'emoji') { - icon = `${pageIcon.emoji} `; - } else if (pageIcon.type === 'file') { - const fileRequest = await axios.get(pageIcon.file.url, { - responseType: 'arraybuffer', - }); - const file = fileRequest.data; - const uri = `data:${ - fileRequest.headers['content-type'] - };base64,${file.toString('base64')}`; - icon = ` `; - } - } - - /* @ts-ignore */ - const { properties } = page; - if (properties.title && properties.title.title.length > 0) { - title = properties.title.title[0].plain_text; - } else if ( - properties.Name && - properties.Name.title && - properties.Name.title.length > 0 - ) { - title = properties.Name.title[0].plain_text; - } + // XXX: get the page title // the order here matters due to icon not being set and last not being default return settings.pageEmoji !== 'last_emoji' @@ -246,26 +214,12 @@ class NotionAPIWrapper { } getDatabaseTitle(database: GetDatabaseResponse, settings: Settings): string { - let icon = ''; - let title = ''; - try { - /* @ts-ignore */ - title = database.title - /* @ts-ignore */ - .map((t) => t.plain_text) - .join(''); - /* @ts-ignore */ - const dbIcon = database.icon; - if (dbIcon.type === 'emoji' && settings.pageEmoji !== 'disable_emoji') { - /* @ts-ignore */ - icon = `${dbIcon.emoji} `; - /* @ts-ignore */ - } else if (dbIcon.type === 'external') { - /* @ts-ignore */ - icon = ` `; - } - /* @ts-ignore */ - } catch (error) {} + let icon = renderIcon( + getBlockIcon(database as WithIcon, settings.pageEmoji) + ); + let title = isFullDatabase(database) + ? database.title.map((t) => t.plain_text).join('') + : ''; return settings.pageEmoji !== 'last_emoji' ? `${icon}${title}` diff --git a/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/047d1106-be33-4b50-a754-b894acbb0a44.json b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/047d1106-be33-4b50-a754-b894acbb0a44.json new file mode 100644 index 000000000..a907484b5 --- /dev/null +++ b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/047d1106-be33-4b50-a754-b894acbb0a44.json @@ -0,0 +1,8 @@ +{ + "object": "list", + "results": [], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} \ No newline at end of file diff --git a/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/268562a1-a4e6-49f1-b3c6-37b2c15b4f91.json b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/268562a1-a4e6-49f1-b3c6-37b2c15b4f91.json new file mode 100644 index 000000000..a907484b5 --- /dev/null +++ b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/268562a1-a4e6-49f1-b3c6-37b2c15b4f91.json @@ -0,0 +1,8 @@ +{ + "object": "list", + "results": [], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} \ No newline at end of file diff --git a/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/306b52f3-6fc3-4bb0-ad1b-459bfe843109.json b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/306b52f3-6fc3-4bb0-ad1b-459bfe843109.json new file mode 100644 index 000000000..a907484b5 --- /dev/null +++ b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/306b52f3-6fc3-4bb0-ad1b-459bfe843109.json @@ -0,0 +1,8 @@ +{ + "object": "list", + "results": [], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} \ No newline at end of file diff --git a/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/34be35bd-db68-4588-85d9-e1adc84c45a5.json b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/34be35bd-db68-4588-85d9-e1adc84c45a5.json new file mode 100644 index 000000000..a907484b5 --- /dev/null +++ b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/34be35bd-db68-4588-85d9-e1adc84c45a5.json @@ -0,0 +1,8 @@ +{ + "object": "list", + "results": [], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} \ No newline at end of file diff --git a/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/37ba774b-3892-4f7c-8149-29e9aacee36d.json b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/37ba774b-3892-4f7c-8149-29e9aacee36d.json new file mode 100644 index 000000000..a907484b5 --- /dev/null +++ b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/37ba774b-3892-4f7c-8149-29e9aacee36d.json @@ -0,0 +1,8 @@ +{ + "object": "list", + "results": [], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} \ No newline at end of file diff --git a/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/4b4e4811-f0b8-401d-a708-03ff1305f1e3.json b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/4b4e4811-f0b8-401d-a708-03ff1305f1e3.json new file mode 100644 index 000000000..a907484b5 --- /dev/null +++ b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/4b4e4811-f0b8-401d-a708-03ff1305f1e3.json @@ -0,0 +1,8 @@ +{ + "object": "list", + "results": [], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} \ No newline at end of file diff --git a/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/56f93f57-6ad0-4462-82a4-fcdc96aa1182.json b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/56f93f57-6ad0-4462-82a4-fcdc96aa1182.json new file mode 100644 index 000000000..a907484b5 --- /dev/null +++ b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/56f93f57-6ad0-4462-82a4-fcdc96aa1182.json @@ -0,0 +1,8 @@ +{ + "object": "list", + "results": [], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} \ No newline at end of file diff --git a/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/5bc8d7e8-1b23-4776-bca4-5c069a12bedf.json b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/5bc8d7e8-1b23-4776-bca4-5c069a12bedf.json new file mode 100644 index 000000000..a907484b5 --- /dev/null +++ b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/5bc8d7e8-1b23-4776-bca4-5c069a12bedf.json @@ -0,0 +1,8 @@ +{ + "object": "list", + "results": [], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} \ No newline at end of file diff --git a/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/7a3e51c3-896f-46f6-9e44-3f780f7b83c7.json b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/7a3e51c3-896f-46f6-9e44-3f780f7b83c7.json new file mode 100644 index 000000000..a907484b5 --- /dev/null +++ b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/7a3e51c3-896f-46f6-9e44-3f780f7b83c7.json @@ -0,0 +1,8 @@ +{ + "object": "list", + "results": [], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} \ No newline at end of file diff --git a/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/96b9ca2d-62ea-444f-89f7-e88bbd6e2e83.json b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/96b9ca2d-62ea-444f-89f7-e88bbd6e2e83.json new file mode 100644 index 000000000..a907484b5 --- /dev/null +++ b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/96b9ca2d-62ea-444f-89f7-e88bbd6e2e83.json @@ -0,0 +1,8 @@ +{ + "object": "list", + "results": [], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} \ No newline at end of file diff --git a/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/a2f0234b-19b8-4808-b602-e8bd54e578d1.json b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/a2f0234b-19b8-4808-b602-e8bd54e578d1.json new file mode 100644 index 000000000..a907484b5 --- /dev/null +++ b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/a2f0234b-19b8-4808-b602-e8bd54e578d1.json @@ -0,0 +1,8 @@ +{ + "object": "list", + "results": [], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} \ No newline at end of file diff --git a/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/b32888b6-cda7-441a-96fe-ee28f02172a4.json b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/b32888b6-cda7-441a-96fe-ee28f02172a4.json new file mode 100644 index 000000000..a907484b5 --- /dev/null +++ b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/b32888b6-cda7-441a-96fe-ee28f02172a4.json @@ -0,0 +1,8 @@ +{ + "object": "list", + "results": [], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} \ No newline at end of file diff --git a/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/d46ff0a8-4ed0-4095-8116-1fa42125ed1b.json b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/d46ff0a8-4ed0-4095-8116-1fa42125ed1b.json new file mode 100644 index 000000000..a907484b5 --- /dev/null +++ b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/d46ff0a8-4ed0-4095-8116-1fa42125ed1b.json @@ -0,0 +1,8 @@ +{ + "object": "list", + "results": [], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} \ No newline at end of file diff --git a/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/db2b7a69-125d-499e-b9d7-d561fdd544ec.json b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/db2b7a69-125d-499e-b9d7-d561fdd544ec.json new file mode 100644 index 000000000..a907484b5 --- /dev/null +++ b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/db2b7a69-125d-499e-b9d7-d561fdd544ec.json @@ -0,0 +1,8 @@ +{ + "object": "list", + "results": [], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} \ No newline at end of file diff --git a/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/dcd150a8-3838-44ab-a557-f5f36e57fe9f.json b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/dcd150a8-3838-44ab-a557-f5f36e57fe9f.json new file mode 100644 index 000000000..a907484b5 --- /dev/null +++ b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/dcd150a8-3838-44ab-a557-f5f36e57fe9f.json @@ -0,0 +1,8 @@ +{ + "object": "list", + "results": [], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} \ No newline at end of file diff --git a/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/f83ce56a-9039-4888-81be-375b19a84790.json b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/f83ce56a-9039-4888-81be-375b19a84790.json new file mode 100644 index 000000000..a907484b5 --- /dev/null +++ b/src/lib/notion/_mock/payloads/ListBlockChildrenResponse/f83ce56a-9039-4888-81be-375b19a84790.json @@ -0,0 +1,8 @@ +{ + "object": "list", + "results": [], + "next_cursor": null, + "has_more": false, + "type": "block", + "block": {} +} \ No newline at end of file diff --git a/src/lib/notion/blocks/BlockCallout.tsx b/src/lib/notion/blocks/BlockCallout.tsx index 73f07f65e..1dfc540b6 100644 --- a/src/lib/notion/blocks/BlockCallout.tsx +++ b/src/lib/notion/blocks/BlockCallout.tsx @@ -1,4 +1,7 @@ -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; +import { + CalloutBlockObjectResponse, + RichTextItemResponse, +} from '@notionhq/client/build/src/api-endpoints'; import ReactDOMServer from 'react-dom/server'; import BlockHandler from '../BlockHandler'; import getPlainText from '../helpers/getPlainText'; @@ -6,16 +9,15 @@ import { styleWithColors } from '../NotionColors'; import HandleBlockAnnotations from './HandleBlockAnnotations'; export const BlockCallout = ( - block: GetBlockResponse, + block: CalloutBlockObjectResponse, handler: BlockHandler ) => { - /* @ts-ignore */ const { callout } = block; const { icon } = callout; - const { text } = callout; + const { rich_text: richText } = callout; if (handler.settings?.isTextOnlyBack) { - return getPlainText(text); + return getPlainText(richText); } return ReactDOMServer.renderToStaticMarkup( @@ -30,11 +32,9 @@ export const BlockCallout = ( )}
- {text.map((t: GetBlockResponse) => { - /* @ts-ignore */ + {richText.map((t: RichTextItemResponse) => { const { annotations } = t; - /* @ts-ignore */ - return HandleBlockAnnotations(annotations, t.text); + return HandleBlockAnnotations(annotations, t); })}
diff --git a/src/lib/notion/blocks/BlockChildPage.tsx b/src/lib/notion/blocks/BlockChildPage.tsx index e178bc35d..f5e2defe8 100644 --- a/src/lib/notion/blocks/BlockChildPage.tsx +++ b/src/lib/notion/blocks/BlockChildPage.tsx @@ -1,17 +1,16 @@ -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; +import { ChildPageBlockObjectResponse } from '@notionhq/client/build/src/api-endpoints'; import BlockHandler from '../BlockHandler'; +import getBlockIcon, { WithIcon } from './getBlockIcon'; import renderLink from '../helpers/renderLink'; export const BlockChildPage = async ( - block: GetBlockResponse, + block: ChildPageBlockObjectResponse, handler: BlockHandler ) => { - /* @ts-ignore */ const childPage = block.child_page; const { api } = handler; const page = await api.getPage(block.id); - /* @ts-ignore */ - const { icon } = page; + const icon = getBlockIcon(page as WithIcon); if (handler.settings?.isTextOnlyBack && childPage) { return childPage.title; diff --git a/src/lib/notion/blocks/BlockCode.tsx b/src/lib/notion/blocks/BlockCode.tsx index 1abcfd6ad..16943db50 100644 --- a/src/lib/notion/blocks/BlockCode.tsx +++ b/src/lib/notion/blocks/BlockCode.tsx @@ -1,30 +1,26 @@ -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; +import { + CodeBlockObjectResponse, + RichTextItemResponse, +} from '@notionhq/client/build/src/api-endpoints'; import ReactDOMServer from 'react-dom/server'; import BlockHandler from '../BlockHandler'; import getPlainText from '../helpers/getPlainText'; -import { styleWithColors } from '../NotionColors'; import HandleBlockAnnotations from './HandleBlockAnnotations'; -const BlockCode = (block: GetBlockResponse, handler: BlockHandler) => { - /* @ts-ignore */ +const BlockCode = (block: CodeBlockObjectResponse, handler: BlockHandler) => { const { code } = block; - const { text } = code; + const { rich_text: richText } = code; if (handler.settings?.isTextOnlyBack) { - return getPlainText(text); + return getPlainText(richText); } return ReactDOMServer.renderToStaticMarkup( -
+    
       
-        {text.map((t: GetBlockResponse) => {
-          /* @ts-ignore */
+        {richText.map((t: RichTextItemResponse) => {
           const { annotations } = t;
-          /* @ts-ignore */
-          return HandleBlockAnnotations(annotations, t.text);
+          return HandleBlockAnnotations(annotations, t);
         })}
       
     
diff --git a/src/lib/notion/blocks/BlockEquation.test.tsx b/src/lib/notion/blocks/BlockEquation.test.tsx index 87db430b9..616eb5bd6 100644 --- a/src/lib/notion/blocks/BlockEquation.test.tsx +++ b/src/lib/notion/blocks/BlockEquation.test.tsx @@ -1,25 +1,31 @@ import BlockEquation from './BlockEquation'; +import { EquationBlockObjectResponse } from '@notionhq/client/build/src/api-endpoints'; describe('BlockEquation', () => { test('MathJax transform', () => { const expected = '\\(sqrt{x}\\)'; const input = { - type: 'equation', - equation: { - expression: 'sqrt{x}', + object: 'block', + id: 'be5503f9-7544-460d-a500-fdc3c04431e8', + parent: { + type: 'block_id', + block_id: '0d75beab-5fbe-46b3-aeaa-bc64e765bb41', + }, + created_time: '2022-12-25T19:32:00.000Z', + last_edited_time: '2022-12-25T19:32:00.000Z', + created_by: { + object: 'user', + id: 'aa', }, - annotations: { - bold: false, - italic: false, - strikethrough: false, - underline: false, - code: false, - color: 'default', + last_edited_by: { + object: 'user', + id: 'aa', }, - plain_text: 'sqrt{x}', - href: null, - }; - /* @ts-ignore */ + has_children: false, + archived: false, + type: 'equation', + equation: { expression: '\\(sqrt{x}\\)' }, + } as EquationBlockObjectResponse; expect(BlockEquation(input)).toBe(expected); }); }); diff --git a/src/lib/notion/blocks/BlockHeadings.tsx b/src/lib/notion/blocks/BlockHeadings.tsx index 0b632240a..9be7bd5d9 100644 --- a/src/lib/notion/blocks/BlockHeadings.tsx +++ b/src/lib/notion/blocks/BlockHeadings.tsx @@ -1,4 +1,8 @@ -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; +import { + BlockObjectResponse, + GetBlockResponse, + RichTextItemResponse, +} from '@notionhq/client/build/src/api-endpoints'; import React from 'react'; import ReactDOMServer from 'react-dom/server'; import TagRegistry from '../../parser/TagRegistry'; @@ -6,6 +10,8 @@ import BlockHandler from '../BlockHandler'; import getPlainText from '../helpers/getPlainText'; import { styleWithColors } from '../NotionColors'; import HandleBlockAnnotations from './HandleBlockAnnotations'; +import { getHeadingText } from '../helpers/getHeadingText'; +import { getHeadingColor } from '../helpers/getHeadingColor'; interface HeadingProps { id: string; @@ -39,31 +45,29 @@ const Heading = (props: HeadingProps) => { }; export const BlockHeading = async ( - level: string, + level: 'heading_1' | 'heading_2' | 'heading_3', block: GetBlockResponse, handler: BlockHandler ) => { - /* @ts-ignore */ - const heading = block[level]; - const { text } = heading; + const headingText = getHeadingText(block as BlockObjectResponse); + if (!headingText) { + return null; + } if (handler.settings?.isTextOnlyBack) { - return getPlainText(text); + return getPlainText(headingText); } return ReactDOMServer.renderToStaticMarkup( - {text.map((t: GetBlockResponse) => { - /* @ts-ignore */ + {headingText.map((t: RichTextItemResponse) => { TagRegistry.getInstance().addHeading(t.plain_text); - /* @ts-ignore */ const { annotations } = t; - /* @ts-ignore */ - return HandleBlockAnnotations(annotations, t.text); + return HandleBlockAnnotations(annotations, t); })} ); diff --git a/src/lib/notion/blocks/BlockParagraph.tsx b/src/lib/notion/blocks/BlockParagraph.tsx index 7984bc215..da91bb43a 100644 --- a/src/lib/notion/blocks/BlockParagraph.tsx +++ b/src/lib/notion/blocks/BlockParagraph.tsx @@ -1,4 +1,4 @@ -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; +import { ParagraphBlockObjectResponse } from '@notionhq/client/build/src/api-endpoints'; import ReactDOMServer from 'react-dom/server'; import { convert } from 'html-to-text'; import BlockHandler from '../BlockHandler'; @@ -7,19 +7,18 @@ import { styleWithColors } from '../NotionColors'; import renderTextChildren from '../helpers/renderTextChildren'; const BlockParagraph = async ( - block: GetBlockResponse, + block: ParagraphBlockObjectResponse, handler: BlockHandler ): Promise => { - /* @ts-ignore */ const { paragraph } = block; - const { text } = paragraph; + const { rich_text: richText } = paragraph; const markup = ReactDOMServer.renderToStaticMarkup(

); diff --git a/src/lib/notion/blocks/BlockQuote.tsx b/src/lib/notion/blocks/BlockQuote.tsx index 651d2e761..78a22d4fe 100644 --- a/src/lib/notion/blocks/BlockQuote.tsx +++ b/src/lib/notion/blocks/BlockQuote.tsx @@ -1,26 +1,26 @@ -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; +import { QuoteBlockObjectResponse } from '@notionhq/client/build/src/api-endpoints'; import ReactDOMServer from 'react-dom/server'; import BlockHandler from '../BlockHandler'; import getPlainText from '../helpers/getPlainText'; import { styleWithColors } from '../NotionColors'; import HandleBlockAnnotations from './HandleBlockAnnotations'; -export const BlockQuote = (block: GetBlockResponse, handler: BlockHandler) => { - /* @ts-ignore */ +export const BlockQuote = ( + block: QuoteBlockObjectResponse, + handler: BlockHandler +) => { const { quote } = block; - const { text } = quote; + const { rich_text: richText } = quote; if (handler.settings?.isTextOnlyBack) { - return getPlainText(text); + return getPlainText(richText); } return ReactDOMServer.renderToStaticMarkup(
- {text.map((t: GetBlockResponse) => { - /* @ts-ignore */ + {richText.map((t) => { const { annotations } = t; - /* @ts-ignore */ - return HandleBlockAnnotations(annotations, t.text); + return HandleBlockAnnotations(annotations, t); })}
); diff --git a/src/lib/notion/blocks/FrontFlashcard.tsx b/src/lib/notion/blocks/FrontFlashcard.tsx deleted file mode 100644 index 2464360a9..000000000 --- a/src/lib/notion/blocks/FrontFlashcard.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; -import ReactDOMServer from 'react-dom/server'; -import BlockHandler from '../BlockHandler'; -import getPlainText from '../helpers/getPlainText'; -import { styleWithColors } from '../NotionColors'; -import HandleBlockAnnotations from './HandleBlockAnnotations'; - -const FrontFlashcard = (block: GetBlockResponse, handler: BlockHandler) => { - /* @ts-ignore */ - const { text } = block; - - if (handler?.settings?.isTextOnlyBack) { - return getPlainText(text); - } - /* @ts-ignore */ - const style = styleWithColors(block.color); - return ReactDOMServer.renderToStaticMarkup( -
- {text.map((t: GetBlockResponse) => { - /* @ts-ignore */ - const { annotations } = t; - /* @ts-ignore */ - return HandleBlockAnnotations(annotations, t.text); - })} -
- ); -}; - -export default FrontFlashcard; diff --git a/src/lib/notion/blocks/HandleBlockAnnotations.tsx b/src/lib/notion/blocks/HandleBlockAnnotations.tsx index 550b153ae..f0580bb59 100644 --- a/src/lib/notion/blocks/HandleBlockAnnotations.tsx +++ b/src/lib/notion/blocks/HandleBlockAnnotations.tsx @@ -1,4 +1,5 @@ import TagRegistry from '../../parser/TagRegistry'; +import { RichTextItemResponse } from '@notionhq/client/build/src/api-endpoints'; interface Annotations { underline: boolean; @@ -10,22 +11,19 @@ interface Annotations { const HandleBlockAnnotations = ( annotations: Annotations, - text: { content: string; link: null | { url: string } } + text: RichTextItemResponse ) => { - if (!text || !text.content) { + if (!text) { return null; } - const { content } = text; - - if (text.link) { - const mangle = HandleBlockAnnotations(annotations, { - content: text.content, - link: null, - }); - /* @ts-ignore */ - return {mangle}; - } - + // if (text.link) { + // const mangle = HandleBlockAnnotations(annotations, { + // content: text.content, + // link: null, + // }); + // return {mangle}; + // } + const content = text.plain_text; if (annotations.underline) { return ( { + const page = block as ChildPageBlockObjectResponse; + return page.child_page; +}; diff --git a/src/lib/notion/blocks/helpers/getHeading.ts b/src/lib/notion/blocks/helpers/getHeading.ts new file mode 100644 index 000000000..ee87c45c4 --- /dev/null +++ b/src/lib/notion/blocks/helpers/getHeading.ts @@ -0,0 +1,19 @@ +import { + BlockObjectResponse, + Heading1BlockObjectResponse, + Heading2BlockObjectResponse, + Heading3BlockObjectResponse, +} from '@notionhq/client/build/src/api-endpoints'; + +export const getHeading = (block?: BlockObjectResponse) => { + switch (block?.type) { + case 'heading_1': + return (block as Heading1BlockObjectResponse).heading_1; + case 'heading_2': + return (block as Heading2BlockObjectResponse).heading_2; + case 'heading_3': + return (block as Heading3BlockObjectResponse).heading_3; + default: + return undefined; + } +}; diff --git a/src/lib/notion/blocks/helpers/getToggleBlock.ts b/src/lib/notion/blocks/helpers/getToggleBlock.ts new file mode 100644 index 000000000..10c1ec93c --- /dev/null +++ b/src/lib/notion/blocks/helpers/getToggleBlock.ts @@ -0,0 +1,8 @@ +import { + BlockObjectResponse, + ToggleBlockObjectResponse, +} from '@notionhq/client/build/src/api-endpoints'; + +export const getToggleBlock = (block: BlockObjectResponse) => { + return (block as ToggleBlockObjectResponse).toggle; +}; diff --git a/src/lib/notion/blocks/helpers/richObjectToString.ts b/src/lib/notion/blocks/helpers/richObjectToString.ts new file mode 100644 index 000000000..cbc033dce --- /dev/null +++ b/src/lib/notion/blocks/helpers/richObjectToString.ts @@ -0,0 +1,9 @@ +import { RichTextItemResponse } from '@notionhq/client/build/src/api-endpoints'; + +export interface RichBlock { + rich_text: Array; +} + +export const richObjectToString = (block: RichBlock) => { + return block.rich_text.map((t) => t.plain_text).join(''); +}; diff --git a/src/lib/notion/blocks/lists/BlockBulletList.tsx b/src/lib/notion/blocks/lists/BlockBulletList.tsx index 9c5bf7e62..7bedd4356 100644 --- a/src/lib/notion/blocks/lists/BlockBulletList.tsx +++ b/src/lib/notion/blocks/lists/BlockBulletList.tsx @@ -1,5 +1,5 @@ import { - GetBlockResponse, + BulletedListItemBlockObjectResponse, ListBlockChildrenResponse, } from '@notionhq/client/build/src/api-endpoints'; import ReactDOMServer from 'react-dom/server'; @@ -9,11 +9,10 @@ import { styleWithColors } from '../../NotionColors'; import getListItems from '../../helpers/getListItems'; export const BlockBulletList = async ( - block: GetBlockResponse, - response: ListBlockChildrenResponse, + block: BulletedListItemBlockObjectResponse, + response: ListBlockChildrenResponse | undefined, handler: BlockHandler ) => { - /* @ts-ignore */ const list = block.bulleted_list_item; const items = await getListItems(response, handler, 'bulleted_list_item'); const listItems = items.filter(Boolean); diff --git a/src/lib/notion/blocks/lists/BlockColumn.tsx b/src/lib/notion/blocks/lists/BlockColumn.tsx index dd78e4dec..b35456790 100644 --- a/src/lib/notion/blocks/lists/BlockColumn.tsx +++ b/src/lib/notion/blocks/lists/BlockColumn.tsx @@ -1,9 +1,9 @@ -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; +import { ColumnBlockObjectResponse } from '@notionhq/client/build/src/api-endpoints'; import BlockHandler from '../../BlockHandler'; import getChildren from '../../helpers/getChildren'; export default async function BlockColumn( - block: GetBlockResponse, + block: ColumnBlockObjectResponse, handler: BlockHandler ) { return getChildren(block, handler); diff --git a/src/lib/notion/blocks/lists/BlockNumberedList.tsx b/src/lib/notion/blocks/lists/BlockNumberedList.tsx index 296385f2f..576de50a1 100644 --- a/src/lib/notion/blocks/lists/BlockNumberedList.tsx +++ b/src/lib/notion/blocks/lists/BlockNumberedList.tsx @@ -1,6 +1,6 @@ import { - GetBlockResponse, ListBlockChildrenResponse, + NumberedListItemBlockObjectResponse, } from '@notionhq/client/build/src/api-endpoints'; import ReactDOMServer from 'react-dom/server'; import { convert } from 'html-to-text'; @@ -9,11 +9,10 @@ import { styleWithColors } from '../../NotionColors'; import getListItems from '../../helpers/getListItems'; export const BlockNumberedList = async ( - block: GetBlockResponse, - response: ListBlockChildrenResponse, + block: NumberedListItemBlockObjectResponse, + response: ListBlockChildrenResponse | undefined, handler: BlockHandler ) => { - /* @ts-ignore */ const list = block.numbered_list_item; const items = await getListItems(response, handler, 'numbered_list_item'); const listItems = items.filter(Boolean); diff --git a/src/lib/notion/blocks/lists/BlockTodoList.tsx b/src/lib/notion/blocks/lists/BlockTodoList.tsx index be52aa66d..98f516634 100644 --- a/src/lib/notion/blocks/lists/BlockTodoList.tsx +++ b/src/lib/notion/blocks/lists/BlockTodoList.tsx @@ -1,6 +1,6 @@ import { - GetBlockResponse, ListBlockChildrenResponse, + ToDoBlockObjectResponse, } from '@notionhq/client/build/src/api-endpoints'; import ReactDOMServer from 'react-dom/server'; import { convert } from 'html-to-text'; @@ -9,11 +9,10 @@ import { styleWithColors } from '../../NotionColors'; import getListItems from '../../helpers/getListItems'; export const BlockTodoList = async ( - block: GetBlockResponse, - response: ListBlockChildrenResponse, + block: ToDoBlockObjectResponse, + response: ListBlockChildrenResponse | undefined, handler: BlockHandler ) => { - /* @ts-ignore */ const list = block.to_do; const items = await getListItems(response, handler, 'to_do'); const listItems = items.filter(Boolean); diff --git a/src/lib/notion/blocks/lists/BlockToggleList.tsx b/src/lib/notion/blocks/lists/BlockToggleList.tsx index badeeeaac..e530b7100 100644 --- a/src/lib/notion/blocks/lists/BlockToggleList.tsx +++ b/src/lib/notion/blocks/lists/BlockToggleList.tsx @@ -1,4 +1,4 @@ -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; +import { ToggleBlockObjectResponse } from '@notionhq/client/build/src/api-endpoints'; import ReactDOMServer from 'react-dom/server'; import { convert } from 'html-to-text'; @@ -6,21 +6,24 @@ import BlockHandler from '../../BlockHandler'; import { styleWithColors } from '../../NotionColors'; import renderTextChildren from '../../helpers/renderTextChildren'; import getChildren from '../../helpers/getChildren'; +import { ReactNode } from 'react'; + +interface DetailsProps { + children: ReactNode; +} export async function BlockToggleList( - block: GetBlockResponse, + block: ToggleBlockObjectResponse, handler: BlockHandler ) { - /* @ts-ignore */ const list = block.toggle; - const { text } = list; + const { rich_text: richText } = list; const backSide = await getChildren(block, handler); /** * We can't just set open to false that won't work since it's a boolean and will be truthy. * The open attribute has to be omitted. */ - /* @ts-ignore */ - const Details = ({ children }) => + const Details = ({ children }: DetailsProps) => handler.settings?.toggleMode === 'open_toggle' ? (
{children}
) : ( @@ -34,7 +37,7 @@ export async function BlockToggleList(
diff --git a/src/lib/notion/blocks/media/BlockBookmark/components/BookmarkContainer.tsx b/src/lib/notion/blocks/media/BlockBookmark/components/BookmarkContainer.tsx new file mode 100644 index 000000000..06c18bb69 --- /dev/null +++ b/src/lib/notion/blocks/media/BlockBookmark/components/BookmarkContainer.tsx @@ -0,0 +1,13 @@ +import { ReactNode } from 'react'; + +interface Prop { + url: string; + children: ReactNode; +} +export const BookmarkContainer = ({ url, children }: Prop) => { + return ( + + {children} + + ); +}; diff --git a/src/lib/notion/blocks/media/BlockBookmark/components/BookmarkDescription.tsx b/src/lib/notion/blocks/media/BlockBookmark/components/BookmarkDescription.tsx new file mode 100644 index 000000000..d069588b3 --- /dev/null +++ b/src/lib/notion/blocks/media/BlockBookmark/components/BookmarkDescription.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +interface Prop { + description: string; +} + +export function BookmarkDescription({ description }: Prop) { + if (!description) { + return null; + } + return
{description}
; +} diff --git a/src/lib/notion/blocks/media/BlockBookmark/components/BookmarkImage.tsx b/src/lib/notion/blocks/media/BlockBookmark/components/BookmarkImage.tsx new file mode 100644 index 000000000..f66e4e8a9 --- /dev/null +++ b/src/lib/notion/blocks/media/BlockBookmark/components/BookmarkImage.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +interface Prop { + image: string; +} + +export const BookmarkImage = ({ image }: Prop) => { + if (!image) { + return null; + } + return ; +}; diff --git a/src/lib/notion/blocks/media/BlockBookmark/components/BookmarkLogo.tsx b/src/lib/notion/blocks/media/BlockBookmark/components/BookmarkLogo.tsx new file mode 100644 index 000000000..74aa2558b --- /dev/null +++ b/src/lib/notion/blocks/media/BlockBookmark/components/BookmarkLogo.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +interface Prop { + logo: string; +} + +export const BookmarkLogo = ({ logo }: Prop) => { + if (!logo) { + return null; + } + return ; +}; diff --git a/src/lib/notion/blocks/media/BlockBookmark/components/BookmarkTitle.tsx b/src/lib/notion/blocks/media/BlockBookmark/components/BookmarkTitle.tsx new file mode 100644 index 000000000..bceeeb4db --- /dev/null +++ b/src/lib/notion/blocks/media/BlockBookmark/components/BookmarkTitle.tsx @@ -0,0 +1,10 @@ +interface Prop { + title: string; +} + +export const BookmarkTitle = ({ title }: Prop) => { + if (!title) { + return null; + } + return
{title}
; +}; diff --git a/src/lib/notion/blocks/media/BlockBookmark/index.tsx b/src/lib/notion/blocks/media/BlockBookmark/index.tsx index f5775ef2a..eae29c03b 100644 --- a/src/lib/notion/blocks/media/BlockBookmark/index.tsx +++ b/src/lib/notion/blocks/media/BlockBookmark/index.tsx @@ -1,48 +1,41 @@ import ReactDOMServer from 'react-dom/server'; import { convert } from 'html-to-text'; -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; +import { BookmarkBlockObjectResponse } from '@notionhq/client/build/src/api-endpoints'; import useMetadata from './hooks/useMetadata'; import BlockHandler from '../../../BlockHandler'; +import React from 'react'; +import { BookmarkTitle } from './components/BookmarkTitle'; +import { BookmarkDescription } from './components/BookmarkDescription'; +import { BookmarkLogo } from './components/BookmarkLogo'; +import { BookmarkImage } from './components/BookmarkImage'; +import { BookmarkContainer } from './components/BookmarkContainer'; const BlockBookmark = async ( - block: GetBlockResponse, + block: BookmarkBlockObjectResponse, handler: BlockHandler ): Promise => { - /* @ts-ignore */ const { bookmark } = block; const metadata = await useMetadata(bookmark.url); if (handler.settings?.isTextOnlyBack && bookmark) { - return `${bookmark.title} ${bookmark.url}`; + return `${metadata.title} ${bookmark.url}`; } const markup = ReactDOMServer.renderToStaticMarkup( - +
- {metadata.title && ( -
{metadata.title}
- )} - {metadata.description && ( -
{metadata.description}
- )} + +
- {metadata.logo && ( - - )} + {bookmark.url}
- {metadata.image && ( - - )} -
+ + ); if (handler.settings?.isTextOnlyBack) { diff --git a/src/lib/notion/blocks/media/BlockEmbed.tsx b/src/lib/notion/blocks/media/BlockEmbed.tsx index 46cea1383..c4d0ee014 100644 --- a/src/lib/notion/blocks/media/BlockEmbed.tsx +++ b/src/lib/notion/blocks/media/BlockEmbed.tsx @@ -1,14 +1,16 @@ -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; +import { EmbedBlockObjectResponse } from '@notionhq/client/build/src/api-endpoints'; import { renderToStaticMarkup } from 'react-dom/server'; import getYouTubeEmbedLink from '../../../parser/helpers/getYouTubeEmbedLink'; import getYouTubeID from '../../../parser/helpers/getYouTubeID'; import BlockHandler from '../../BlockHandler'; -export const BlockEmbed = (c: GetBlockResponse, handler: BlockHandler) => { +export const BlockEmbed = ( + c: EmbedBlockObjectResponse, + handler: BlockHandler +) => { if (handler.settings?.isTextOnlyBack) { return ''; } - /* @ts-ignore */ const { embed } = c; let { url } = embed; if (url) { diff --git a/src/lib/notion/blocks/media/BlockVideo.tsx b/src/lib/notion/blocks/media/BlockVideo.tsx index 92b65b5eb..2353b3c62 100644 --- a/src/lib/notion/blocks/media/BlockVideo.tsx +++ b/src/lib/notion/blocks/media/BlockVideo.tsx @@ -1,35 +1,21 @@ -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; +import { VideoBlockObjectResponse } from '@notionhq/client/build/src/api-endpoints'; import { renderToStaticMarkup } from 'react-dom/server'; -import getYouTubeEmbedLink from '../../../parser/helpers/getYouTubeEmbedLink'; -import getYouTubeID from '../../../parser/helpers/getYouTubeID'; import BlockHandler from '../../BlockHandler'; +import { getVideoUrl } from './helpers/getVideoUrl'; +import { isVimeoLink } from './helpers/isVimeoLink'; -export const BlockVideo = (c: GetBlockResponse, handler: BlockHandler) => { - if (handler.settings?.isTextOnlyBack) { - return ''; +export const BlockVideo = ( + c: VideoBlockObjectResponse, + handler: BlockHandler +) => { + let url = getVideoUrl(c); + if (handler.settings?.isTextOnlyBack || !url) { + return null; } - /* @ts-ignore */ - const { video } = c; - let { url } = video.external; - if (url) { - const yt = getYouTubeID(url); - if (yt) { - url = getYouTubeEmbedLink(yt); - } else if (url.match('vimeo.com')) { - url = url.replace('vimeo.com/', 'player.vimeo.com/video/'); - const videoId = url.split('/').pop().split('?')[0]; - return renderToStaticMarkup( - - ); - } + if (isVimeoLink(url)) { + return url; } + return renderToStaticMarkup( <> + ); + } + } + return block.video.external.url; + case 'file': + return block.video.file.url; + default: + return 'unsupported video: ' + JSON.stringify(block); + } +}; diff --git a/src/lib/notion/blocks/media/helpers/isVimeoLink.ts b/src/lib/notion/blocks/media/helpers/isVimeoLink.ts new file mode 100644 index 000000000..dcc5f24b2 --- /dev/null +++ b/src/lib/notion/blocks/media/helpers/isVimeoLink.ts @@ -0,0 +1,6 @@ +export const isVimeoLink = (url: string | null) => { + if (!url) { + return null; + } + return url.match('vimeo.com'); +}; diff --git a/src/lib/notion/helpers/blockToStaticMarkup.tsx b/src/lib/notion/helpers/blockToStaticMarkup.tsx new file mode 100644 index 000000000..924031f81 --- /dev/null +++ b/src/lib/notion/helpers/blockToStaticMarkup.tsx @@ -0,0 +1,116 @@ +import { getImageUrl } from './getImageUrl'; +import BlockParagraph from '../blocks/BlockParagraph'; +import BlockCode from '../blocks/BlockCode'; +import { BlockHeading } from '../blocks/BlockHeadings'; +import { BlockQuote } from '../blocks/BlockQuote'; +import { BlockDivider } from '../blocks/BlockDivider'; +import { BlockChildPage } from '../blocks/BlockChildPage'; +import { BlockTodoList } from '../blocks/lists/BlockTodoList'; +import { BlockCallout } from '../blocks/BlockCallout'; +import { BlockBulletList } from '../blocks/lists/BlockBulletList'; +import { BlockNumberedList } from '../blocks/lists/BlockNumberedList'; +import { BlockToggleList } from '../blocks/lists/BlockToggleList'; +import BlockBookmark from '../blocks/media/BlockBookmark'; +import { BlockVideo } from '../blocks/media/BlockVideo'; +import { BlockEmbed } from '../blocks/media/BlockEmbed'; +import BlockColumn from '../blocks/lists/BlockColumn'; +import BlockEquation from '../blocks/BlockEquation'; +import { + BlockObjectResponse, + EquationBlockObjectResponse, + ListBlockChildrenResponse, +} from '@notionhq/client/build/src/api-endpoints'; +import LinkToPage from '../blocks/LinkToPage/LinkToPage'; +import BlockHandler from '../BlockHandler'; + +export const blockToStaticMarkup = async ( + handler: BlockHandler, + c: BlockObjectResponse, + response?: ListBlockChildrenResponse +) => { + let back = ''; + switch (c.type) { + case 'image': + if (!handler.settings.learnMode) { + const image = await handler.embedImage(c); + back += image; + } else { + back += ``; + } + break; + case 'audio': + const audio = await handler.embedAudioFile(c); + back += audio; + break; + case 'file': + const file = await handler.embedFile(c); + back += file; + break; + case 'paragraph': + back += await BlockParagraph(c, handler); + break; + case 'code': + back += BlockCode(c, handler); + break; + case 'heading_1': + back += await BlockHeading('heading_1', c, handler); + break; + case 'heading_2': + back += await BlockHeading('heading_2', c, handler); + break; + case 'heading_3': + back += await BlockHeading('heading_3', c, handler); + break; + case 'quote': + back += BlockQuote(c, handler); + break; + case 'divider': + back += BlockDivider(); + break; + case 'child_page': + back += await BlockChildPage(c, handler); + break; + case 'to_do': + back += await BlockTodoList(c, response, handler); + break; + case 'callout': + back += BlockCallout(c, handler); + break; + case 'bulleted_list_item': + back += await BlockBulletList(c, response, handler); + break; + case 'numbered_list_item': + back += await BlockNumberedList(c, response, handler); + break; + case 'toggle': + back += await BlockToggleList(c, handler); + break; + case 'bookmark': + back += await BlockBookmark(c, handler); + break; + case 'video': + back += BlockVideo(c, handler); + break; + case 'embed': + back += BlockEmbed(c, handler); + break; + case 'column': + back += await BlockColumn(c, handler); + break; + case 'equation': + back += BlockEquation(c as EquationBlockObjectResponse); + break; + case 'link_to_page': + back += await LinkToPage(c, handler); + break; + default: + back += `unsupported: ${c.type}`; + back += BlockDivider(); + back += ` +
+          ${JSON.stringify(c, null, 4)}
+          
`; + console.debug(`unsupported ${c.type}`); + } + return back; +}; diff --git a/src/lib/notion/helpers/getChildren.ts b/src/lib/notion/helpers/getChildren.ts index 976189945..390e7e117 100644 --- a/src/lib/notion/helpers/getChildren.ts +++ b/src/lib/notion/helpers/getChildren.ts @@ -1,13 +1,12 @@ -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; +import { BlockObjectResponse } from '@notionhq/client/build/src/api-endpoints'; import BlockHandler from '../BlockHandler'; export default async function getChildren( - block: GetBlockResponse, + block: BlockObjectResponse, handler: BlockHandler ): Promise { let backSide = ''; - /* @ts-ignore */ if (block.has_children) { backSide += await handler.getBackSide(block, true); } diff --git a/src/lib/notion/helpers/getClozeDeletionCard.ts b/src/lib/notion/helpers/getClozeDeletionCard.ts index 6e32f97b1..9565b4cd5 100644 --- a/src/lib/notion/helpers/getClozeDeletionCard.ts +++ b/src/lib/notion/helpers/getClozeDeletionCard.ts @@ -1,8 +1,12 @@ -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; +import { + GetBlockResponse, + TextRichTextItemResponse, +} from '@notionhq/client/build/src/api-endpoints'; import ParserRules from '../../parser/ParserRules'; import Note from '../../parser/Note'; import isColumnList from './isColumnList'; +import { getRichTextFromBlock } from './getRichTextFromBlock'; // The user wants to turn code blocks into cloze deletions word becomes {{c1::word}} // This all should be tested with Jest @@ -13,35 +17,31 @@ export default async function getClozeDeletionCard( let isCloze = false; let name = ''; let index = 1; - const flashCardTypes = rules.flaschardTypeNames(); - for (const FLASHCARD of flashCardTypes) { - // @ts-ignore - const flashcardBlock = block[FLASHCARD]; - if ((!flashcardBlock && !flashcardBlock.rich_text) || isColumnList(block)) { - continue; - } - console.log(flashcardBlock); - for (const cb of flashcardBlock.rich_text) { - if (cb.annotations.code) { - const { content } = cb.text; - if (content.includes('::')) { - if (content.match(/[cC]\d+::/)) { - name += `{{${content}}}`; - } else { - const clozeIndex = `{{c${index}::`; - if (!name.includes(clozeIndex)) { - name += `{{c${index}::${content}}}`; - } - } + + const richText = getRichTextFromBlock(block); + if (!richText || isColumnList(block)) { + return undefined; + } + for (const cb of richText) { + const text = (cb as TextRichTextItemResponse).text; + if (cb.annotations.code) { + if (text?.content.includes('::')) { + if (text?.content.match(/[cC]\d+::/)) { + name += `{{${text?.content}}}`; } else { - name += `{{c${index}::${content}}}`; + const clozeIndex = `{{c${index}::`; + if (!name.includes(clozeIndex)) { + name += `{{c${index}::${text?.content}}}`; + } } - name = name.replace('{{{{', '{{').replace('}}}}', '}}'); - isCloze = true; - index++; - } else if (cb.text?.content) { - name += cb.text.content; + } else { + name += `{{c${index}::${text?.content}}}`; } + name = name.replace('{{{{', '{{').replace('}}}}', '}}'); + isCloze = true; + index++; + } else if (text?.content) { + name += text?.content; } } if (isCloze) { diff --git a/src/lib/notion/helpers/getColumn.ts b/src/lib/notion/helpers/getColumn.ts index 9fb7051b4..8add1ed9e 100644 --- a/src/lib/notion/helpers/getColumn.ts +++ b/src/lib/notion/helpers/getColumn.ts @@ -9,7 +9,6 @@ export default async function getColumn( const getBlocks = await handler.api.getBlocks(parentId); const blocks = getBlocks?.results; if (blocks?.length > 0 && blocks?.length >= index + 1) { - /* @ts-ignore */ return blocks[index]; } return null; diff --git a/src/lib/notion/helpers/getHeadingColor.ts b/src/lib/notion/helpers/getHeadingColor.ts new file mode 100644 index 000000000..3a38580bb --- /dev/null +++ b/src/lib/notion/helpers/getHeadingColor.ts @@ -0,0 +1,23 @@ +import { isFullBlock } from '@notionhq/client'; +import { + BlockObjectResponse, + Heading1BlockObjectResponse, + Heading2BlockObjectResponse, + Heading3BlockObjectResponse, +} from '@notionhq/client/build/src/api-endpoints'; + +export const getHeadingColor = (block: BlockObjectResponse) => { + if (!isFullBlock(block)) { + return 'default'; + } + switch (block.type) { + case 'heading_1': + return (block as Heading1BlockObjectResponse).heading_1.color; + case 'heading_2': + return (block as Heading2BlockObjectResponse).heading_2.color; + case 'heading_3': + return (block as Heading3BlockObjectResponse).heading_3.color; + default: + return 'default'; + } +}; diff --git a/src/lib/notion/helpers/getHeadingText.ts b/src/lib/notion/helpers/getHeadingText.ts new file mode 100644 index 000000000..66abca499 --- /dev/null +++ b/src/lib/notion/helpers/getHeadingText.ts @@ -0,0 +1,11 @@ +import { isFullBlock } from '@notionhq/client'; +import { BlockObjectResponse } from '@notionhq/client/build/src/api-endpoints'; +import { getHeading } from '../blocks/helpers/getHeading'; + +export const getHeadingText = (block: BlockObjectResponse) => { + if (!isFullBlock(block)) { + return undefined; + } + const heading = getHeading(block); + return heading?.rich_text; +}; diff --git a/src/lib/notion/helpers/getInputCard.ts b/src/lib/notion/helpers/getInputCard.ts index 270af5d45..2fbb7f3f9 100644 --- a/src/lib/notion/helpers/getInputCard.ts +++ b/src/lib/notion/helpers/getInputCard.ts @@ -1,8 +1,12 @@ -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; +import { + GetBlockResponse, + TextRichTextItemResponse, +} from '@notionhq/client/build/src/api-endpoints'; import ParserRules from '../../parser/ParserRules'; import Note from '../../parser/Note'; import isColumnList from './isColumnList'; +import { getRichTextFromBlock } from './getRichTextFromBlock'; // The user wants to turn under lines into input cards keyword becomes {{type::word}} export default async function getInputCard( @@ -12,21 +16,17 @@ export default async function getInputCard( let isInput = false; let name = ''; let answer = ''; - const flashCardTypes = rules.flaschardTypeNames(); - for (const FLASHCARD of flashCardTypes) { - // @ts-ignore - const flashcardBlock = block[FLASHCARD]; - // @ts-ignore - if (!flashcardBlock || isColumnList(block)) { - continue; - } - for (const cb of flashcardBlock.text) { - if (cb.annotations.underline || cb.annotations.bold) { - answer += cb.text.content; - isInput = true; - } else { - name += cb.text.content; - } + const flashcardBlock = getRichTextFromBlock(block); + if (!flashcardBlock || isColumnList(block)) { + return undefined; + } + for (const cb of flashcardBlock) { + const text = (cb as TextRichTextItemResponse).text; + if (cb.annotations.underline || cb.annotations.bold) { + answer += text?.content; + isInput = true; + } else { + name += text?.content; } } if (isInput) { diff --git a/src/lib/notion/helpers/getListBlock.tsx b/src/lib/notion/helpers/getListBlock.tsx new file mode 100644 index 000000000..f6329c638 --- /dev/null +++ b/src/lib/notion/helpers/getListBlock.tsx @@ -0,0 +1,26 @@ +import { + BlockObjectResponse, + BulletedListItemBlockObjectResponse, + NumberedListItemBlockObjectResponse, + PartialBlockObjectResponse, + ToDoBlockObjectResponse, +} from '@notionhq/client/build/src/api-endpoints'; +import { isFullBlock } from '@notionhq/client'; + +export function getListBlock( + result: PartialBlockObjectResponse | BlockObjectResponse +) { + if (!isFullBlock(result)) { + return undefined; + } + switch (result.type) { + case 'bulleted_list_item': + return result as BulletedListItemBlockObjectResponse; + case 'to_do': + return result as ToDoBlockObjectResponse; + case 'numbered_list_item': + return result as NumberedListItemBlockObjectResponse; + default: + return undefined; + } +} diff --git a/src/lib/notion/helpers/getListColor.ts b/src/lib/notion/helpers/getListColor.ts new file mode 100644 index 000000000..b0673ea50 --- /dev/null +++ b/src/lib/notion/helpers/getListColor.ts @@ -0,0 +1,21 @@ +import { + BlockObjectResponse, + BulletedListItemBlockObjectResponse, + NumberedListItemBlockObjectResponse, + ToDoBlockObjectResponse, +} from '@notionhq/client/build/src/api-endpoints'; + +export const getListColor = (block: BlockObjectResponse) => { + switch (block.type) { + case 'to_do': + return (block as ToDoBlockObjectResponse).to_do.color; + case 'bulleted_list_item': + return (block as BulletedListItemBlockObjectResponse).bulleted_list_item + .color; + case 'numbered_list_item': + return (block as NumberedListItemBlockObjectResponse).numbered_list_item + .color; + default: + return undefined; + } +}; diff --git a/src/lib/notion/helpers/getListItems.tsx b/src/lib/notion/helpers/getListItems.tsx index 1f4176806..60ee89e3a 100644 --- a/src/lib/notion/helpers/getListItems.tsx +++ b/src/lib/notion/helpers/getListItems.tsx @@ -1,44 +1,54 @@ -import { ListBlockChildrenResponse } from '@notionhq/client/build/src/api-endpoints'; +import { + ListBlockChildrenResponse, + ToDoBlockObjectResponse, +} from '@notionhq/client/build/src/api-endpoints'; import renderTextChildren from './renderTextChildren'; import { styleWithColors } from '../NotionColors'; import BlockHandler from '../BlockHandler'; import getChildren from './getChildren'; +import { getListBlock } from './getListBlock'; +import { getListColor } from './getListColor'; +import { getRichTextFromBlock } from './getRichTextFromBlock'; type ListType = 'numbered_list_item' | 'bulleted_list_item' | 'to_do'; export default async function getListItems( - response: ListBlockChildrenResponse, + response: ListBlockChildrenResponse | undefined, handler: BlockHandler, type: ListType ) { + if (!response) { + return []; + } return Promise.all( response.results.map(async (result) => { - /* @ts-ignore */ - const list = result[type]; + const list = getListBlock(result); if (!list) { return null; } const backSide = await getChildren(list, handler); handler.skip.push(result.id); - const isTodo = type === 'to_do'; - const checked = - isTodo && list.checked - ? 'to-do-children-checked' - : 'to-do-children-unchecked'; - const checkedClass = isTodo ? checked : ''; + const todo = + type === 'to_do' ? (list as ToDoBlockObjectResponse).to_do : null; + const checked = todo?.checked + ? 'to-do-children-checked' + : 'to-do-children-unchecked'; + const checkedClass = todo ? checked : ''; return ( -
  • - {isTodo && ( +
  • + {todo && (
    )}
    {backSide && ( diff --git a/src/lib/notion/helpers/getParagraphBlocks.ts b/src/lib/notion/helpers/getParagraphBlocks.ts new file mode 100644 index 000000000..5c8188ac9 --- /dev/null +++ b/src/lib/notion/helpers/getParagraphBlocks.ts @@ -0,0 +1,16 @@ +import { + BlockObjectResponse, + ParagraphBlockObjectResponse, + PartialBlockObjectResponse, +} from '@notionhq/client/build/src/api-endpoints'; +import { isFullBlock } from '@notionhq/client'; + +export const getParagraphBlocks = ( + results: Array +): Array => + results.filter((block) => { + if (!isFullBlock(block)) { + return false; + } + return block.type === 'paragraph'; + }) as ParagraphBlockObjectResponse[]; diff --git a/src/lib/notion/helpers/getRichTextFromBlock.ts b/src/lib/notion/helpers/getRichTextFromBlock.ts new file mode 100644 index 000000000..5d1afc08c --- /dev/null +++ b/src/lib/notion/helpers/getRichTextFromBlock.ts @@ -0,0 +1,51 @@ +import { isFullBlock } from '@notionhq/client'; +import { + BulletedListItemBlockObjectResponse, + CalloutBlockObjectResponse, + CodeBlockObjectResponse, + GetBlockResponse, + NumberedListItemBlockObjectResponse, + ParagraphBlockObjectResponse, + QuoteBlockObjectResponse, + RichTextItemResponse, + TemplateBlockObjectResponse, + ToDoBlockObjectResponse, + ToggleBlockObjectResponse, +} from '@notionhq/client/build/src/api-endpoints'; +import { getHeadingText } from './getHeadingText'; + +export const getRichTextFromBlock = ( + block: GetBlockResponse +): undefined | Array => { + if (!isFullBlock(block)) { + return undefined; + } + switch (block.type) { + case 'toggle': + return (block as ToggleBlockObjectResponse).toggle.rich_text; + case 'bulleted_list_item': + return (block as BulletedListItemBlockObjectResponse).bulleted_list_item + .rich_text; + case 'numbered_list_item': + return (block as NumberedListItemBlockObjectResponse).numbered_list_item + .rich_text; + case 'heading_1': + case 'heading_2': + case 'heading_3': + return getHeadingText(block); + case 'paragraph': + return (block as ParagraphBlockObjectResponse).paragraph.rich_text; + case 'quote': + return (block as QuoteBlockObjectResponse).quote.rich_text; + case 'to_do': + return (block as ToDoBlockObjectResponse).to_do.rich_text; + case 'template': + return (block as TemplateBlockObjectResponse).template.rich_text; + case 'code': + return (block as CodeBlockObjectResponse).code.rich_text; + case 'callout': + return (block as CalloutBlockObjectResponse).callout.rich_text; + default: + return undefined; + } +}; diff --git a/src/lib/notion/helpers/getSubDeckName.ts b/src/lib/notion/helpers/getSubDeckName.ts index d75a355bd..0db5f0ac2 100644 --- a/src/lib/notion/helpers/getSubDeckName.ts +++ b/src/lib/notion/helpers/getSubDeckName.ts @@ -1,24 +1,31 @@ -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; -import { captureException } from '@sentry/node'; -import getPlainText from './getPlainText'; +import { BlockObjectResponse } from '@notionhq/client/build/src/api-endpoints'; +import { isFullBlock } from '@notionhq/client'; +import { getChildPageBlock } from '../blocks/helpers/getChildPageBlock'; +import { getToggleBlock } from '../blocks/helpers/getToggleBlock'; +import { richObjectToString } from '../blocks/helpers/richObjectToString'; +import { getHeading } from '../blocks/helpers/getHeading'; -const getSubDeckName = (block: GetBlockResponse | Object) => { +const getSubDeckName = (block: BlockObjectResponse): string => { let subDeckName = 'Untitled'; - /* @ts-ignore */ - const text = block.text; - /* @ts-ignore */ - const title = block.title; - try { - if (text) { - subDeckName = getPlainText(text); - } else if (title) { - subDeckName = title; - } else { - /* @ts-ignore */ - subDeckName = block?.properties.title.title[0].plain_text; + + if (isFullBlock(block)) { + switch (block.type) { + case 'child_page': + return getChildPageBlock(block).title; + case 'toggle': + return richObjectToString(getToggleBlock(block)); + case 'heading_1': + case 'heading_2': + case 'heading_3': + const heading = getHeading(block); + if (heading) { + return richObjectToString(heading); + } + case 'column_list': + case 'bulleted_list_item': + case 'numbered_list_item': + return subDeckName; } - } catch (error) { - captureException(error); } return subDeckName; }; diff --git a/src/lib/notion/helpers/getToggleBlocks.ts b/src/lib/notion/helpers/getToggleBlocks.ts new file mode 100644 index 000000000..47b4d2405 --- /dev/null +++ b/src/lib/notion/helpers/getToggleBlocks.ts @@ -0,0 +1,16 @@ +import { + BlockObjectResponse, + PartialBlockObjectResponse, + ToggleBlockObjectResponse, +} from '@notionhq/client/build/src/api-endpoints'; +import { isFullBlock } from '@notionhq/client'; + +export const getToggleBlocks = ( + results: Array +): Array => + results.filter((block) => { + if (!isFullBlock(block)) { + return false; + } + return block.type === 'toggle'; + }) as ToggleBlockObjectResponse[]; diff --git a/src/lib/notion/helpers/isHeading.ts b/src/lib/notion/helpers/isHeading.ts index 9abba9f71..f43e2df5b 100644 --- a/src/lib/notion/helpers/isHeading.ts +++ b/src/lib/notion/helpers/isHeading.ts @@ -1,7 +1,16 @@ -import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; +import { + BlockObjectResponse, + PartialBlockObjectResponse, +} from '@notionhq/client/build/src/api-endpoints'; +import { isFullBlock } from '@notionhq/client'; + +export const isHeading = ( + block: BlockObjectResponse | PartialBlockObjectResponse +): boolean => { + if (!isFullBlock(block)) { + return false; + } -export default function isHeading(block: GetBlockResponse): boolean { - /* @ts-ignore */ switch (block.type) { case 'heading_1': return true; @@ -12,4 +21,4 @@ export default function isHeading(block: GetBlockResponse): boolean { default: return false; } -} +}; diff --git a/src/lib/notion/helpers/isToggle.ts b/src/lib/notion/helpers/isToggle.ts index f016939d3..1cd260d39 100644 --- a/src/lib/notion/helpers/isToggle.ts +++ b/src/lib/notion/helpers/isToggle.ts @@ -1,6 +1,9 @@ import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; +import { isFullBlock } from '@notionhq/client'; export default function isToggle(block: GetBlockResponse): boolean { - /* @ts-ignore */ + if (!isFullBlock(block)) { + return false; + } return block.type === 'toggle'; } diff --git a/src/lib/notion/helpers/renderBack.tsx b/src/lib/notion/helpers/renderBack.tsx index bf0d821ff..86bafeccf 100644 --- a/src/lib/notion/helpers/renderBack.tsx +++ b/src/lib/notion/helpers/renderBack.tsx @@ -1,30 +1,12 @@ import { isFullBlock } from '@notionhq/client'; import { captureMessage } from '@sentry/node'; -import { getImageUrl } from './getImageUrl'; -import BlockParagraph from '../blocks/BlockParagraph'; -import BlockCode from '../blocks/BlockCode'; -import { BlockHeading } from '../blocks/BlockHeadings'; -import { BlockQuote } from '../blocks/BlockQuote'; -import { BlockDivider } from '../blocks/BlockDivider'; -import { BlockChildPage } from '../blocks/BlockChildPage'; -import { BlockTodoList } from '../blocks/lists/BlockTodoList'; -import { BlockCallout } from '../blocks/BlockCallout'; -import { BlockBulletList } from '../blocks/lists/BlockBulletList'; -import { BlockNumberedList } from '../blocks/lists/BlockNumberedList'; -import { BlockToggleList } from '../blocks/lists/BlockToggleList'; -import BlockBookmark from '../blocks/media/BlockBookmark'; -import { BlockVideo } from '../blocks/media/BlockVideo'; -import { BlockEmbed } from '../blocks/media/BlockEmbed'; -import BlockColumn from '../blocks/lists/BlockColumn'; -import BlockEquation from '../blocks/BlockEquation'; -import LinkToPage from '../blocks/LinkToPage'; import BlockHandler from '../BlockHandler'; import { BlockObjectResponse, - EquationBlockObjectResponse, ListBlockChildrenResponse, PartialBlockObjectResponse, } from '@notionhq/client/build/src/api-endpoints'; +import { blockToStaticMarkup } from './blockToStaticMarkup'; export const renderBack = async ( handler: BlockHandler, @@ -47,90 +29,7 @@ export const renderBack = async ( }); continue; } - - switch (c.type) { - case 'image': - if (!handler.settings.learnMode) { - const image = await handler.embedImage(c); - back += image; - } else { - back += ``; - } - break; - case 'audio': - const audio = await handler.embedAudioFile(c); - back += audio; - break; - case 'file': - const file = await handler.embedFile(c); - back += file; - break; - case 'paragraph': - back += await BlockParagraph(c, handler); - break; - case 'code': - back += BlockCode(c, handler); - break; - case 'heading_1': - back += await BlockHeading('heading_1', c, handler); - break; - case 'heading_2': - back += await BlockHeading('heading_2', c, handler); - break; - case 'heading_3': - back += await BlockHeading('heading_3', c, handler); - break; - case 'quote': - back += BlockQuote(c, handler); - break; - case 'divider': - back += BlockDivider(); - break; - case 'child_page': - back += await BlockChildPage(c, handler); - break; - case 'to_do': - back += await BlockTodoList(c, response, handler); - break; - case 'callout': - back += BlockCallout(c, handler); - break; - case 'bulleted_list_item': - back += await BlockBulletList(c, response, handler); - break; - case 'numbered_list_item': - back += await BlockNumberedList(c, response, handler); - break; - case 'toggle': - back += await BlockToggleList(c, handler); - break; - case 'bookmark': - back += await BlockBookmark(c, handler); - break; - case 'video': - back += BlockVideo(c, handler); - break; - case 'embed': - back += BlockEmbed(c, handler); - break; - case 'column': - back += await BlockColumn(c, handler); - break; - case 'equation': - back += BlockEquation(c as EquationBlockObjectResponse); - break; - case 'link_to_page': - back += await LinkToPage(c, handler); - break; - default: - back += `unsupported: ${c.type}`; - back += BlockDivider(); - back += ` -
    -          ${JSON.stringify(c, null, 4)}
    -          
    `; - console.debug(`unsupported ${c.type}`); - } + back += await blockToStaticMarkup(handler, c, response); // Nesting applies to all not just toggles if ( diff --git a/src/lib/notion/helpers/renderFront.tsx b/src/lib/notion/helpers/renderFront.tsx deleted file mode 100644 index b5d205731..000000000 --- a/src/lib/notion/helpers/renderFront.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { - GetBlockResponse, - ImageBlockObjectResponse, - ToggleBlockObjectResponse, -} from '@notionhq/client/build/src/api-endpoints'; -import { captureException } from '@sentry/node'; - -import BlockHandler from '../BlockHandler'; -import BlockCode from '../blocks/BlockCode'; -import { BlockHeading } from '../blocks/BlockHeadings'; -import FrontFlashcard from '../blocks/FrontFlashcard'; -import BlockColumn from '../blocks/lists/BlockColumn'; -import { BlockVideo } from '../blocks/media/BlockVideo'; -import getColumn from './getColumn'; -import { getImageUrl } from './getImageUrl'; -import isHeading from './isHeading'; -import renderTextChildren from './renderTextChildren'; - -export default async function renderFront( - block: GetBlockResponse, - handler: BlockHandler -) { - /* @ts-ignore */ - const { type } = block; - if (isHeading(block)) { - return BlockHeading(type, block, handler); - } - switch (type) { - case 'column_list': - const firstColumn = await getColumn(block.id, handler, 0); - if (firstColumn) { - return BlockColumn(firstColumn, handler); - } - break; - case 'image': - // Do not add the images in default mode - const image = getImageUrl(block as ImageBlockObjectResponse); - if (handler.settings.learnMode && image) { - return ``; - } - break; - case 'code': - if (handler.settings.learnMode) { - return BlockCode(block, handler); - } - break; - case 'toggle': - const toggle = (block as ToggleBlockObjectResponse).toggle; - if (toggle && toggle.rich_text.length > 0) { - return renderTextChildren(toggle.rich_text, handler.settings); - } - break; - case 'video': - return BlockVideo(block, handler); - default: - try { - // @ts-ignore - return FrontFlashcard(block[type], handler); - } catch (error) { - captureException(error); - return `Unsupported block type in front: ${type}\n${JSON.stringify( - block, - null, - 4 - )}`; - } - } - return ''; -} diff --git a/src/lib/notion/helpers/renderIcon.tsx b/src/lib/notion/helpers/renderIcon.tsx new file mode 100644 index 000000000..ecc0b0db9 --- /dev/null +++ b/src/lib/notion/helpers/renderIcon.tsx @@ -0,0 +1,10 @@ +export default function renderIcon(icon?: string) { + if (!icon) { + return null; + } + return icon.startsWith('http') ? ( + + ) : ( + {icon} + ); +} diff --git a/src/lib/notion/helpers/renderLink.tsx b/src/lib/notion/helpers/renderLink.tsx index 4f471b386..b1ea3de9f 100644 --- a/src/lib/notion/helpers/renderLink.tsx +++ b/src/lib/notion/helpers/renderLink.tsx @@ -1,16 +1,15 @@ import { GetBlockResponse } from '@notionhq/client/build/src/api-endpoints'; import ReactDOMServer from 'react-dom/server'; +import renderIcon from './renderIcon'; export default function renderLink( title: string, block: GetBlockResponse, - icon?: { type: string; emoji: string } + icon?: string ) { return ReactDOMServer.renderToStaticMarkup( - {icon && icon.type === 'emoji' && ( - {icon.emoji} - )} + {renderIcon(icon)} {title} ); diff --git a/src/lib/notion/helpers/renderTextChildren.tsx b/src/lib/notion/helpers/renderTextChildren.tsx index acddd8b48..6d72a21ac 100644 --- a/src/lib/notion/helpers/renderTextChildren.tsx +++ b/src/lib/notion/helpers/renderTextChildren.tsx @@ -1,7 +1,6 @@ import { EquationRichTextItemResponse, RichTextItemResponse, - TextRichTextItemResponse, } from '@notionhq/client/build/src/api-endpoints'; import ReactDOMServer from 'react-dom/server'; import Settings from '../../parser/Settings'; @@ -13,10 +12,10 @@ import isText from './isText'; import preserveNewlinesIfApplicable from './preserveNewlinesIfApplicable'; export default function renderTextChildren( - text: RichTextItemResponse[], + text: RichTextItemResponse[] | undefined, settings: Settings ): string { - if (text.length === 0) { + if (!text || text?.length === 0) { return ''; } const content = text @@ -28,12 +27,7 @@ export default function renderTextChildren( if (isText(t)) { const { annotations } = t; return ReactDOMServer.renderToStaticMarkup( - <> - {HandleBlockAnnotations( - annotations, - (t as TextRichTextItemResponse).text - )} - + <>{HandleBlockAnnotations(annotations, t)} ); } diff --git a/src/lib/notion/types.ts b/src/lib/notion/types.ts index 899c347ea..c74aecb1b 100644 --- a/src/lib/notion/types.ts +++ b/src/lib/notion/types.ts @@ -1,16 +1,88 @@ -export type ParagraphBlockObject = { - paragraph: { - color: string; - text: [ - { - type: string; - text: { - content: string; - }; - annotations: { - color: string; - }; - } - ]; - }; +import { TextRichTextItemResponse } from '@notionhq/client/build/src/api-endpoints'; + +export type PropertiesPageTitle = { + title: [ + { + id: string; + type: string; + title: TextRichTextItemResponse; + } + ]; }; + +export type ValidNotionType = + | 'audio' + | 'block' + | 'bookmark' + | 'boolean' + | 'bot' + | 'breadcrumb' + | 'bulleted_list_item' + | 'callout' + | 'checkbox' + | 'child_database' + | 'child_page' + | 'code' + | 'column' + | 'column_list' + | 'comment' + | 'created_by' + | 'created_time' + | 'database' + | 'database_id' + | 'date' + | 'divider' + | 'dual_property' + | 'email' + | 'embed' + | 'emoji' + | 'equation' + | 'file' + | 'files' + | 'formula' + | 'heading_1' + | 'heading_2' + | 'heading_3' + | 'image' + | 'last_edited_by' + | 'last_edited_time' + | 'link_preview' + | 'link_to_page' + | 'mention' + | 'multi_select' + | 'number' + | 'numbered_list_item' + | 'page' + | 'page_id' + | 'page_or_database' + | 'paragraph' + | 'pdf' + | 'people' + | 'person' + | 'phone_number' + | 'property_item' + | 'quote' + | 'relation' + | 'rich_text' + | 'rollup' + | 'select' + | 'single_property' + | 'status' + | 'string' + | 'synced_block' + | 'table' + | 'table_of_contents' + | 'table_row' + | 'template' + | 'template_mention' + | 'template_mention_date' + | 'template_mention_user' + | 'text' + | 'title' + | 'to_do' + | 'toggle' + | 'unsupported' + | 'url' + | 'user' + | 'video' + | 'workspace'; diff --git a/src/lib/parser/DeckParser.ts b/src/lib/parser/DeckParser.ts index e7b3a3225..82b2c3a15 100644 --- a/src/lib/parser/DeckParser.ts +++ b/src/lib/parser/DeckParser.ts @@ -16,7 +16,6 @@ import getYouTubeID from './helpers/getYouTubeID'; import getYouTubeEmbedLink from './helpers/getYouTubeEmbedLink'; import getUniqueFileName from '../misc/getUniqueFileName'; import { captureException } from '@sentry/node'; -import { findToggleHeadings } from './helpers/findToggleHeadings'; import { isValidAudioFile } from '../anki/format'; export class DeckParser { @@ -146,7 +145,6 @@ export class DeckParser { ? contents.replace(/border-bottom:0.05em solid/g, '') : contents ); - /* @ts-ignore */ let name = deckName || dom('title').text(); let style = dom('style').html(); const pageId = dom('article').attr('id'); @@ -184,9 +182,7 @@ export class DeckParser { } this.globalTags = dom('.page-body > p > del'); - const toggleList = this.findToggleLists(dom).concat( - findToggleHeadings(dom) - ); + const toggleList = this.findToggleLists(dom); let cards: Note[] = []; toggleList.forEach((t) => { @@ -216,8 +212,7 @@ export class DeckParser { const front = parentClass ? `
    ${validSummary}
    ` : validSummary; - /* @ts-ignore */ - if (toggle || (this.settings.maxOne && toggle.text())) { + if (toggle || this.settings.maxOne) { const toggleHTML = toggle.html(); if (toggleHTML) { let b = toggleHTML.replace(summary.html() || '', ''); @@ -316,7 +311,6 @@ export class DeckParser { for (const d of decks) { d.style = d.cleanStyle(); } - /* @ts-ignore */ return new CustomExporter(this.firstDeckName, workspace); } diff --git a/src/lib/parser/ParserRules.ts b/src/lib/parser/ParserRules.ts index c561a46b7..6869c6662 100644 --- a/src/lib/parser/ParserRules.ts +++ b/src/lib/parser/ParserRules.ts @@ -16,7 +16,7 @@ class ParserRules { EMAIL_NOTIFICATION = false; /** - * Function to handle transforming flaschard types to proper names for use in traverseral + * Function to handle transforming flaschard types to proper names for use in traversal * @returns all type names for flashcards */ flaschardTypeNames(): string[] { diff --git a/src/lib/parser/helpers/findToggleHeadings.ts b/src/lib/parser/helpers/findToggleHeadings.ts deleted file mode 100644 index 8f73d1c9b..000000000 --- a/src/lib/parser/helpers/findToggleHeadings.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { TagElement, ToggleHeading } from '../types'; - -/** - * findToggleHeadings transforms the HTML format to something 2anki.net understands. - * - * Limitations: due to the way Notion has implemented toggle headings in the export, - * we are not supporting the proper formating for the toggle header. So it will not - * be semantically correct (h1, h2, etc.). We just show plain text but the back will - * perserve the formatting and be semantically correct. - * - * Hopefully Notion will improve this, adding structure for reference - *
    - *

    - *
    - * Front with heading 1 - *
    - *

    - *
    - *

    - *
    - * Back with heading 1 - *
    - *

    - *
    - * - * Known issues: We can create duplicates but Anki will coalasce the flashcards so it's - * safe to ignore those for now. - * - * @param dom cheerio - * @returns transformed toggle headings Cheerio[] - */ - -export const findToggleHeadings = (dom: cheerio.Root): cheerio.Element[] => { - const elements = dom('.page-body > *').toArray(); - let toggleHeadings: ToggleHeading[] = []; - let i = 0; - for (const element of elements) { - const el = element as TagElement; - if (el.tagName.match(/h[1-6]/)) { - toggleHeadings[i] = { - summary: dom(element).html(), - details: null, - }; - } else if (el.attribs.class === 'indented') { - if (toggleHeadings[i + 1]) { - toggleHeadings[i++].details = dom(element).html(); - } - } - } - // @ts-ignore - return toggleHeadings.map((th) => - dom( - '
    ' + - // @ts-ignore - th.summary.replace('', `${th.details}`) + - '
    ' - ) - ); -}; diff --git a/src/lib/storage/StorageHandler.ts b/src/lib/storage/StorageHandler.ts index 879802d6a..67053340d 100644 --- a/src/lib/storage/StorageHandler.ts +++ b/src/lib/storage/StorageHandler.ts @@ -34,9 +34,10 @@ class StorageHandler { return process.env.SPACES_DEFAULT_BUCKET_NAME!; } - delete(file: aws.S3.Object): Promise { - /* @ts-ignore */ - return this.deleteWith(file.Key); + delete(file: aws.S3.Object) { + if (file.Key) { + return this.deleteWith(file.Key); + } } deleteWith(key: string): Promise { @@ -59,7 +60,6 @@ class StorageHandler { getContents(): Promise { const { s3 } = this; return new Promise((resolve, reject) => { - /* @ts-ignore */ s3.listObjects( { Bucket: StorageHandler.DefaultBucketName() }, (err, data) => { diff --git a/src/lib/storage/types.ts b/src/lib/storage/types.ts new file mode 100644 index 000000000..532be07f9 --- /dev/null +++ b/src/lib/storage/types.ts @@ -0,0 +1,15 @@ +// Multer types +export interface UploadedFile extends Express.Multer.File { + key: string; +} + +// Database types (matching tables) +export interface Upload { + id: number; + owner: number; + key: string; + filename: string; + object_id: string; + size_mb: number; + external_url: string; +} diff --git a/src/routes/download/u/index.ts b/src/routes/download/u/index.ts index 0eca9ff35..f75c02f83 100644 --- a/src/routes/download/u/index.ts +++ b/src/routes/download/u/index.ts @@ -25,12 +25,9 @@ router.get('/u/:key', RequireAuthentication, async (req, res) => { res.status(404).send(); } } catch (error) { - // @ts-ignore - if (error && error.code === 'NoSuchKey') { - await DB('uploads').del().where(query); - return res.redirect('/uploads'); - } + console.error(error); console.info('unknown error'); + res.status(404).send(); captureException(error); } }); diff --git a/src/routes/notion/convert/helpers/performConversion.ts b/src/routes/notion/convert/helpers/performConversion.ts index 9935a9320..9575f1247 100644 --- a/src/routes/notion/convert/helpers/performConversion.ts +++ b/src/routes/notion/convert/helpers/performConversion.ts @@ -108,7 +108,6 @@ export default async function performConversion( object_id: id, owner, filename, - /* @ts-ignore */ key, size_mb: size, }); diff --git a/src/routes/notion/helpers/sendError.ts b/src/routes/notion/helpers/sendError.ts index 87459a8d9..c272189b8 100644 --- a/src/routes/notion/helpers/sendError.ts +++ b/src/routes/notion/helpers/sendError.ts @@ -6,12 +6,11 @@ export default function sendError( error: Error | APIResponseError | unknown, response: Response ) { + let status = 500; + let body = { message: 'Unknown error.' }; if (error instanceof APIResponseError) { - return response - .status(error.status) - .json({ message: error.message }) - .send(); + status = error.status; + body = { message: error.message }; } - /* @ts-ignore */ - return response.status(500).json({ message: error?.message }).send(); + return response.status(status).json(body).send(); } diff --git a/src/routes/notion/renderBlock.ts b/src/routes/notion/renderBlock.ts index a0bc79821..55d5e7ac5 100644 --- a/src/routes/notion/renderBlock.ts +++ b/src/routes/notion/renderBlock.ts @@ -1,12 +1,13 @@ import express from 'express'; import BlockHandler from '../../lib/notion/BlockHandler'; -import renderFront from '../../lib/notion/helpers/renderFront'; import NotionAPIWrapper from '../../lib/notion/NotionAPIWrapper'; import NotionID from '../../lib/notion/NotionID'; import CustomExporter from '../../lib/parser/CustomExporter'; import Settings from '../../lib/parser/Settings'; import Workspace from '../../lib/parser/WorkSpace'; +import { blockToStaticMarkup } from '../../lib/notion/helpers/blockToStaticMarkup'; +import { BlockObjectResponse } from '@notionhq/client/build/src/api-endpoints'; export default async function renderBlock( api: NotionAPIWrapper, @@ -23,7 +24,9 @@ export default async function renderBlock( settings ); const backSide = await handler.getBackSide(block, false); - const frontSide = await renderFront(block, handler); - + const frontSide = await blockToStaticMarkup( + handler, + block as BlockObjectResponse + ); return res.json({ backSide, frontSide }); } diff --git a/src/routes/settings/createSetting.ts b/src/routes/settings/createSetting.ts index fe07e28c7..372cd6368 100644 --- a/src/routes/settings/createSetting.ts +++ b/src/routes/settings/createSetting.ts @@ -13,7 +13,6 @@ export default async function createSetting(req: Request, res: Response) { DB('settings') .insert({ - /* @ts-ignore */ owner: access.owner, object_id: settings.object_id, payload: settings.payload, diff --git a/src/routes/settings/deleteSetting.ts b/src/routes/settings/deleteSetting.ts index 21117e873..be6986058 100644 --- a/src/routes/settings/deleteSetting.ts +++ b/src/routes/settings/deleteSetting.ts @@ -11,7 +11,6 @@ export default async function (req: Request, res: Response) { await DB('settings') .del() .where({ - /* @ts-ignore */ owner: access.owner, object_id: req.body.object_id, }) diff --git a/src/routes/templates/deleteTemplate.ts b/src/routes/templates/deleteTemplate.ts index 95b213a86..a3d218972 100644 --- a/src/routes/templates/deleteTemplate.ts +++ b/src/routes/templates/deleteTemplate.ts @@ -10,7 +10,6 @@ export default async function (req: Request, res: Response) { await DB('templates') .del() .where({ - /* @ts-ignore */ owner: access.owner, }) .then(() => { diff --git a/src/routes/upload/helpers/handleUpload.ts b/src/routes/upload/helpers/handleUpload.ts index 4e8157e32..ee3cbe8fd 100644 --- a/src/routes/upload/helpers/handleUpload.ts +++ b/src/routes/upload/helpers/handleUpload.ts @@ -14,6 +14,7 @@ import { sendBundle } from './sendBundle'; import { captureException } from '@sentry/node'; import { getPackagesFromZip } from './getPackagesFromZip'; import { DECK_NAME_SUFFIX } from '../../../lib/anki/format'; +import { UploadedFile } from '../../../lib/storage/types'; export default async function handleUpload( storage: StorageHandler, @@ -21,14 +22,13 @@ export default async function handleUpload( res: express.Response ) { try { - const files = req.files as Express.Multer.File[]; + const files = req.files as UploadedFile[]; let packages: Package[] = []; let hasMarkdown = false; for (const file of files) { const filename = file.originalname; const settings = new Settings(req.body || {}); registerUploadSize(file, res); - /* @ts-ignore */ const key = file.key; const fileContents = await storage.getFileContents(key); @@ -73,8 +73,7 @@ export default async function handleUpload( try { res.set('File-Name', encodeURIComponent(first.name)); } catch (err) { - /* @ts-ignore */ - captureException(err.toString()); + captureException(err); console.info(`failed to set name ${first.name}`); } diff --git a/src/routes/upload/helpers/registerUploadSize.ts b/src/routes/upload/helpers/registerUploadSize.ts index d2f84caf1..036004e12 100644 --- a/src/routes/upload/helpers/registerUploadSize.ts +++ b/src/routes/upload/helpers/registerUploadSize.ts @@ -2,11 +2,9 @@ import { captureException } from '@sentry/node'; import { Response } from 'express'; import { BytesToMegaBytes } from '../../../lib/misc/file'; import DB from '../../../lib/storage/db'; +import { UploadedFile } from '../../../lib/storage/types'; -export const registerUploadSize = async ( - file: Express.Multer.File, - res: Response -) => { +export const registerUploadSize = async (file: UploadedFile, res: Response) => { const isLoggedIn = res.locals.owner; if (!isLoggedIn) { return; @@ -17,7 +15,6 @@ export const registerUploadSize = async ( await DB('uploads').insert({ owner: res.locals.owner, filename, - /* @ts-ignore */ key: file.key, size_mb: BytesToMegaBytes(file.size), }); diff --git a/src/routes/users/index.ts b/src/routes/users/index.ts index a53ba8daa..57f0d4cb2 100644 --- a/src/routes/users/index.ts +++ b/src/routes/users/index.ts @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/node'; +import { captureException } from '@sentry/node'; import express from 'express'; import DB from '../../lib/storage/db'; @@ -10,7 +11,6 @@ import updatePassword from '../../lib/User/updatePassword'; import comparePassword from '../../lib/User/comparePassword'; import hashPassword from '../../lib/User/hashPassword'; import { INDEX_FILE } from '../../lib/constants'; -import { captureException } from '@sentry/node'; const router = express.Router(); @@ -52,7 +52,6 @@ router.post('/forgot-password', async (req, res, next) => { .where({ email: req.body.email }) .returning(['reset_token', 'id']) .first(); - /* @ts-ignore */ if (!user || !user.id) { console.debug('no user found'); return res.status(200).json({ message: 'ok' }); diff --git a/src/server.ts b/src/server.ts index 686145eab..3411c1c89 100644 --- a/src/server.ts +++ b/src/server.ts @@ -7,13 +7,6 @@ import cookieParser from 'cookie-parser'; import * as dotenv from 'dotenv'; import { IsDebug } from './lib/debug'; -if (IsDebug()) { - const localEnvFile = path.join(__dirname, '.env'); - if (existsSync(localEnvFile)) { - dotenv.config({ path: localEnvFile }); - } -} - import { ALLOWED_ORIGINS, BUILD_DIR, INDEX_FILE } from './lib/constants'; import ErrorHandler from './lib/misc/ErrorHandler'; @@ -37,6 +30,16 @@ import { ScheduleCleanup } from './lib/jobs/JobHandler'; import ConversionJob from './lib/jobs/ConversionJob'; import RequireAuthentication from './middleware/RequireAuthentication'; import { captureException } from '@sentry/node'; +import { Knex } from 'knex'; + +if (IsDebug()) { + const localEnvFile = path.join(__dirname, '.env'); + if (existsSync(localEnvFile)) { + dotenv.config({ path: localEnvFile }); + } +} + +import MigratorConfig = Knex.MigratorConfig; function serve() { const templateDir = path.join(__dirname, 'templates'); @@ -132,8 +135,7 @@ function serve() { process.chdir(path.join(process.env.MIGRATIONS_DIR, '..')); } ScheduleCleanup(DB); - /* @ts-ignore */ - DB.migrate.latest(KnexConfig).then(() => { + DB.migrate.latest(KnexConfig as MigratorConfig).then(() => { process.chdir(cwd); process.env.SECRET ||= 'victory'; const port = process.env.PORT || 2020;