From 24e68fc798f585fe5ba6c1c3bb5e39e68de7920b Mon Sep 17 00:00:00 2001 From: Jiaxin Peng Date: Mon, 6 Oct 2025 23:18:01 +0100 Subject: [PATCH] unify three databases with single upload file --- .gitignore | 4 + src/main.ts | 3 - src/upload/Upload2Notion.ts | 582 ++++++++++++++++++ src/upload/common/UploadBase.ts | 186 ++++++ .../getMarkdownCustom.ts | 10 +- .../getMarkdownGeneral.ts | 0 .../getMarkdownNext.ts | 0 src/upload/uploadCommand.ts | 59 +- .../BaseUpload2NotionGeneral.ts | 96 --- .../upload_general/Upload2NotionGeneral.ts | 269 -------- .../upload_next/BaseUpload2NotionNext.ts | 96 --- src/upload/upload_next/Upload2NotionNext.ts | 420 ------------- .../upoload_custom/BaseUpload2NotionCustom.ts | 53 -- .../upoload_custom/Upload2NotionCustom.ts | 348 ----------- 14 files changed, 824 insertions(+), 1302 deletions(-) create mode 100644 src/upload/Upload2Notion.ts create mode 100644 src/upload/common/UploadBase.ts rename src/upload/{upoload_custom => common}/getMarkdownCustom.ts (83%) rename src/upload/{upload_general => common}/getMarkdownGeneral.ts (100%) rename src/upload/{upload_next => common}/getMarkdownNext.ts (100%) delete mode 100644 src/upload/upload_general/BaseUpload2NotionGeneral.ts delete mode 100644 src/upload/upload_general/Upload2NotionGeneral.ts delete mode 100644 src/upload/upload_next/BaseUpload2NotionNext.ts delete mode 100644 src/upload/upload_next/Upload2NotionNext.ts delete mode 100644 src/upload/upoload_custom/BaseUpload2NotionCustom.ts delete mode 100644 src/upload/upoload_custom/Upload2NotionCustom.ts diff --git a/.gitignore b/.gitignore index 142a30f..7e815a4 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,7 @@ data.json # local data local-data + +# vitepress +doc/.vitepress/dist +doc/.vitepress/cache diff --git a/src/main.ts b/src/main.ts index ce6323f..6d0b517 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,5 @@ import { App, Editor, MarkdownView, Notice, Plugin, PluginSettingTab, Setting } from "obsidian"; import { addIcons } from 'src/ui/icon'; -import { Upload2NotionGeneral } from "src/upload/upload_general/Upload2NotionGeneral"; -import { Upload2NotionNext } from "src/upload/upload_next/Upload2NotionNext"; import { i18nConfig } from "src/lang/I18n"; import ribbonCommands from "src/commands/NotionCommands"; import { ObsidianSettingTab, PluginSettings, DEFAULT_SETTINGS, DatabaseDetails } from "src/ui/settingTabs"; @@ -86,4 +84,3 @@ export default class ObsidianSyncNotionPlugin extends Plugin { - diff --git a/src/upload/Upload2Notion.ts b/src/upload/Upload2Notion.ts new file mode 100644 index 0000000..0a2c390 --- /dev/null +++ b/src/upload/Upload2Notion.ts @@ -0,0 +1,582 @@ +import { App, TFile } from "obsidian"; +import { markdownToBlocks } from "@jxpeng98/martian"; +import * as yamlFrontMatter from "yaml-front-matter"; +import { LIMITS, paragraph } from "@jxpeng98/martian/src/notion"; +import MyPlugin from "src/main"; +import { DatabaseDetails } from "../ui/settingTabs"; +import { updateYamlInfo } from "./updateYaml"; +import { UploadBase, NotionPageResponse } from "./common/UploadBase"; + +export type DatasetType = "general" | "next" | "custom"; + +interface BaseSyncRequest { + dataset: DatasetType; + markdown: string; + nowFile: TFile; + app: App; +} + +interface GeneralSyncRequest extends BaseSyncRequest { + dataset: "general"; + title: string; + cover: string; + tags: string[]; +} + +interface NextSyncRequest extends BaseSyncRequest { + dataset: "next"; + title: string; + emoji: string; + cover: string; + tags: string[]; + type: string; + slug: string; + stats: string; + category: string; + summary: string; + password: string; + favicon: string; + datetime: string; +} + +interface CustomSyncRequest extends BaseSyncRequest { + dataset: "custom"; + cover: string; + customValues: Record; +} + +export type SyncRequest = + | GeneralSyncRequest + | NextSyncRequest + | CustomSyncRequest; + +interface GeneralParams { + title: string; + cover?: string; + tags: string[]; + childArr: any[]; + notionId?: string; +} + +interface NextParams { + title: string; + emoji: string; + cover?: string; + tags: string[]; + type: string; + slug: string; + stats: string; + category: string; + summary: string; + password: string; + favicon: string; + datetime: string; + childArr: any[]; + notionId?: string; +} + +interface CustomParams { + cover?: string; + customValues: Record; + childArr: any[]; + notionId?: string; +} + +export class Upload2Notion extends UploadBase { + + constructor(plugin: MyPlugin, dbDetails: DatabaseDetails) { + super(plugin, dbDetails); + } + + async sync(request: SyncRequest): Promise { + console.log(`[Upload2Notion] Start sync`, { + dataset: request.dataset, + file: request.nowFile.path, + database: this.dbDetails.databaseID, + }); + + let response: NotionPageResponse; + + switch (request.dataset) { + case "general": + response = await this.handleGeneral(request); + break; + case "next": + response = await this.handleNext(request); + break; + case "custom": + response = await this.handleCustom(request); + break; + default: + throw new Error(`Unsupported dataset type: ${(request as any).dataset}`); + } + + console.log(`[Upload2Notion] Notion response status`, response.response?.status); + + if (response.response && response.response.status === 200) { + console.log(`[Upload2Notion] Sync success`, { + notionPageId: response.data?.id, + notionPageUrl: response.data?.url, + }); + await updateYamlInfo( + request.markdown, + request.nowFile, + response.data, + request.app, + this.plugin, + this.dbDetails, + ); + } else { + console.log(`[Upload2Notion] Sync failed`, response.data); + } + + return response; + } + + private async handleGeneral(request: GeneralSyncRequest): Promise { + console.log(`[Upload2Notion] Handling general dataset`, { + cover: request.cover, + tags: request.tags, + }); + const blocks = this.buildBlocks(request.markdown, { + notionLimits: {truncate: false}, + }); + const notionId = this.getNotionId(request.app, request.nowFile); + + return this.upsertGeneral({ + title: request.title, + cover: request.cover, + tags: request.tags || [], + childArr: blocks, + notionId, + }); + } + + private async handleNext(request: NextSyncRequest): Promise { + console.log(`[Upload2Notion] Handling next dataset`, { + type: request.type, + slug: request.slug, + category: request.category, + }); + const blocks = this.buildBlocks(request.markdown, { + notionLimits: {truncate: false}, + }); + this.splitRichTextParagraphs(blocks); + const notionId = this.getNotionId(request.app, request.nowFile); + + return this.upsertNext({ + title: request.title, + emoji: request.emoji, + cover: request.cover, + tags: request.tags || [], + type: request.type, + slug: request.slug, + stats: request.stats, + category: request.category, + summary: request.summary, + password: request.password, + favicon: request.favicon, + datetime: request.datetime, + childArr: blocks, + notionId, + }); + } + + private async handleCustom(request: CustomSyncRequest): Promise { + console.log(`[Upload2Notion] Handling custom dataset`, { + customKeys: Object.keys(request.customValues || {}), + }); + const blocks = this.buildBlocks(request.markdown, { + strictImageUrls: true, + notionLimits: {truncate: false}, + }); + const notionId = this.getNotionId(request.app, request.nowFile); + + return this.upsertCustom({ + cover: request.cover, + customValues: request.customValues, + childArr: blocks, + notionId, + }); + } + + private buildBlocks(markdown: string, options: Record): any[] { + const yamlContent: any = yamlFrontMatter.loadFront(markdown); + const content = yamlContent.__content; + return markdownToBlocks(content, options); + } + + private getNotionId(app: App, nowFile: TFile): string | undefined { + const frontMatter = app.metadataCache.getFileCache(nowFile)?.frontmatter; + if (!frontMatter) { + return undefined; + } + const {abName} = this.dbDetails; + const notionIDKey = `NotionID-${abName}`; + const notionId = frontMatter[notionIDKey]; + return notionId ? String(notionId) : undefined; + } + + private splitRichTextParagraphs(blocks: any[]): void { + blocks.forEach((block, index) => { + if ( + block.type === "paragraph" && + block.paragraph.rich_text.length > LIMITS.RICH_TEXT_ARRAYS + ) { + console.log(`[Upload2Notion] Splitting rich text paragraph`, { + index, + length: block.paragraph.rich_text.length, + }); + const paragraphChunks = this.chunkArray(block.paragraph.rich_text, 100); + const newParagraphBlocks = paragraphChunks.map((chunk) => paragraph(chunk)); + blocks.splice(index, 1, ...newParagraphBlocks); + } + }); + } + + private chunkArray(items: T[], size: number): T[][] { + const result: T[][] = []; + for (let i = 0; i < items.length; i += size) { + result.push(items.slice(i, i + size)); + } + return result; + } + + private async upsertGeneral(params: GeneralParams): Promise { + const {firstChunk, extraChunks} = this.prepareBlocks(params.childArr); + const cover = params.notionId + ? await this.resolveCoverForUpdate(params.cover) + : params.cover; + + if (params.notionId) { + await this.deletePage(params.notionId); + } + + const body = this.buildGeneralBody({ + title: params.title, + tags: params.tags, + firstChunk, + }); + this.applyCover(body, cover); + console.log(body); + return this.submitPage(body, extraChunks); + } + + private async upsertNext(params: NextParams): Promise { + const {firstChunk, extraChunks} = this.prepareBlocks(params.childArr); + const cover = params.notionId + ? await this.resolveCoverForUpdate(params.cover) + : params.cover; + + if (params.notionId) { + await this.deletePage(params.notionId); + } + + const body = this.buildNextBody({ + firstChunk, + title: params.title, + emoji: params.emoji, + tags: params.tags, + type: params.type, + slug: params.slug, + stats: params.stats, + category: params.category, + summary: params.summary, + password: params.password, + favicon: params.favicon, + datetime: params.datetime, + }); + this.applyCover(body, cover); + console.log(body); + return this.submitPage(body, extraChunks); + } + + private async upsertCustom(params: CustomParams): Promise { + const {firstChunk, extraChunks} = this.prepareBlocks(params.childArr); + const cover = params.notionId + ? await this.resolveCoverForUpdate(params.cover) + : params.cover; + + if (params.notionId) { + await this.deletePage(params.notionId); + } + + const body = this.buildCustomBody({ + customValues: params.customValues, + firstChunk, + }); + this.applyCover(body, cover); + console.log(body); + return this.submitPage(body, extraChunks); + } + + private buildGeneralBody(params: {title: string; tags: string[]; firstChunk: any[]}): any { + const { + databaseID, + customTitleButton, + customTitleName, + tagButton, + } = this.dbDetails; + + return { + parent: { + database_id: databaseID, + }, + properties: { + [customTitleButton ? customTitleName : "title"]: { + title: [ + { + text: { + content: params.title, + }, + }, + ], + }, + ...(tagButton + ? { + tags: { + multi_select: + params.tags && params.tags.length > 0 + ? params.tags.map((tag) => ({name: tag})) + : [], + }, + } + : {}), + }, + children: params.firstChunk, + }; + } + + private buildNextBody(params: { + firstChunk: any[]; + title: string; + emoji: string; + tags: string[]; + type: string; + slug: string; + stats: string; + category: string; + summary: string; + password: string; + favicon: string; + datetime: string; + }): any { + const {databaseID} = this.dbDetails; + + const pageProperties: any = { + parent: { + database_id: databaseID, + }, + properties: { + title: { + title: [ + { + text: { + content: params.title, + }, + }, + ], + }, + type: { + select: { + name: params.type || "Post", + }, + }, + status: { + select: { + name: params.stats || "Draft", + }, + }, + category: { + select: { + name: params.category || "Obsidian", + }, + }, + password: { + rich_text: [ + { + text: { + content: params.password || "", + }, + }, + ], + }, + icon: { + rich_text: [ + { + text: { + content: params.favicon || "", + }, + }, + ], + }, + date: { + date: { + start: params.datetime || new Date().toISOString(), + }, + }, + }, + }; + + if (params.tags && params.tags.length > 0) { + pageProperties.properties.tags = { + multi_select: params.tags.map((tag) => ({name: tag})), + }; + } + + if (params.emoji) { + pageProperties.icon = { + emoji: params.emoji, + }; + } + + if (params.slug) { + pageProperties.properties.slug = { + rich_text: [ + { + text: { + content: params.slug, + }, + }, + ], + }; + } + + if (params.summary) { + pageProperties.properties.summary = { + rich_text: [ + { + text: { + content: params.summary, + }, + }, + ], + }; + } + + return { + ...pageProperties, + children: params.firstChunk, + }; + } + + private buildCustomBody(params: { + customValues: Record; + firstChunk: any[]; + }): any { + const {customProperties, databaseID} = this.dbDetails; + const properties: { [key: string]: any } = {}; + + if (customProperties) { + customProperties.forEach(({customName, customType}) => { + if (params.customValues[customName] !== undefined) { + const propertyValue = this.buildCustomProperty(customName, customType, params.customValues); + if (propertyValue !== undefined) { + properties[customName] = propertyValue; + } + } + }); + } + + return { + parent: { + database_id: databaseID, + }, + properties, + children: params.firstChunk, + }; + } + + private buildCustomProperty(customName: string, customType: string, customValues: Record) { + const value = customValues[customName] || ""; + + switch (customType) { + case "title": + return { + title: [ + { + text: { + content: value, + }, + }, + ], + }; + case "rich_text": + return { + rich_text: [ + { + text: { + content: value || "", + }, + }, + ], + }; + case "date": + return { + date: { + start: value || new Date().toISOString(), + }, + }; + case "number": + return { + number: Number(value), + }; + case "phone_number": + return { + phone_number: value, + }; + case "email": + return { + email: value, + }; + case "url": + return { + url: value, + }; + case "files": + return { + files: Array.isArray(value) + ? value.map((url: string) => ({ + name: url, + type: "external", + external: { + url, + }, + })) + : [ + { + name: value, + type: "external", + external: { + url: value, + }, + }, + ], + }; + case "checkbox": + return { + checkbox: Boolean(value) || false, + }; + case "select": + return { + select: { + name: value, + }, + }; + case "multi_select": + return { + multi_select: Array.isArray(value) + ? value.map((item: string) => ({name: item})) + : [{name: value}], + }; + case "relation": + return { + relation: Array.isArray(value) + ? value.map((item: string) => ({id: item})) + : [{id: value}], + }; + default: + return undefined; + } + } +} diff --git a/src/upload/common/UploadBase.ts b/src/upload/common/UploadBase.ts new file mode 100644 index 0000000..7b803c0 --- /dev/null +++ b/src/upload/common/UploadBase.ts @@ -0,0 +1,186 @@ +import { Notice, requestUrl } from "obsidian"; +import MyPlugin from "src/main"; +import { DatabaseDetails } from "../../ui/settingTabs"; +import { i18nConfig } from "../../lang/I18n"; + +export interface NotionPageResponse { + response: any; + data: any; +} + +interface PreparedBlocks { + firstChunk: any[]; + extraChunks: any[][]; +} + +export abstract class UploadBase { + protected plugin: MyPlugin; + protected dbDetails: DatabaseDetails; + + protected constructor(plugin: MyPlugin, dbDetails: DatabaseDetails) { + this.plugin = plugin; + this.dbDetails = dbDetails; + } + + async deletePage(notionID: string) { + const {notionAPI} = this.dbDetails; + return requestUrl({ + url: `https://api.notion.com/v1/blocks/${notionID}`, + method: "DELETE", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + notionAPI, + "Notion-Version": "2022-06-28", + }, + body: "", + }); + } + + protected prepareBlocks(childArr: any[]): PreparedBlocks { + this.stripCodeAnnotations(childArr); + + const childArrLength = childArr.length; + console.log(`Page includes ${childArrLength} blocks`); + + if (childArrLength <= 100) { + return { + firstChunk: childArr, + extraChunks: [], + }; + } + + const extraChunks: any[][] = []; + for (let i = 100; i < childArr.length; i += 100) { + extraChunks.push(childArr.slice(i, i + 100)); + } + + return { + firstChunk: childArr.slice(0, 100), + extraChunks, + }; + } + + protected applyCover(body: any, cover?: string) { + if (cover) { + body.cover = { + type: "external", + external: { + url: cover, + }, + }; + } else if (!body.cover && this.plugin.settings.bannerUrl) { + body.cover = { + type: "external", + external: { + url: this.plugin.settings.bannerUrl, + }, + }; + } + } + + protected async resolveCoverForUpdate(cover?: string): Promise { + if (cover) { + return cover; + } + const databaseCover = await this.fetchDatabaseCover(); + return databaseCover ?? undefined; + } + + protected async submitPage(body: any, extraChunks: any[][]): Promise { + const {notionAPI} = this.dbDetails; + + const response = await requestUrl({ + url: `https://api.notion.com/v1/pages`, + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + notionAPI, + "Notion-Version": "2022-06-28", + }, + body: JSON.stringify(body), + throw: false, + }); + + const data = await response.json; + + if (response.status !== 200) { + new Notice(`Error ${data.status}: ${data.code} \n ${i18nConfig["CheckConsole"]}`, 5000); + console.log(`Error message: \n ${data.message}`); + } else { + console.log(`Page created: ${data.url}`); + console.log(`Page ID: ${data.id}`); + if (extraChunks.length > 0) { + await this.appendExtraBlocks(data.id, extraChunks); + } + } + + return {response, data}; + } + + private stripCodeAnnotations(childArr: any[]) { + childArr.forEach((block: any) => { + if (block.type === "code") { + block.code.rich_text.forEach((item: any) => { + if (item.type === "text" && item.annotations) { + delete item.annotations; + } + }); + } + }); + } + + private async appendExtraBlocks(pageId: string, extraChunks: any[][]) { + const {notionAPI} = this.dbDetails; + + for (let i = 0; i < extraChunks.length; i++) { + const chunk = extraChunks[i]; + const extraBlocks = { + children: chunk, + }; + + console.log(extraBlocks); + + const extraResponse = await requestUrl({ + url: `https://api.notion.com/v1/blocks/${pageId}/children`, + method: "PATCH", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + notionAPI, + "Notion-Version": "2022-06-28", + }, + body: JSON.stringify(extraBlocks), + }); + + const extraData: any = await extraResponse.json; + + if (extraResponse.status !== 200) { + new Notice(`Error ${extraData.status}: ${extraData.code} \n ${i18nConfig["CheckConsole"]}`, 5000); + console.log(`Error message: \n ${extraData.message}`); + } else { + console.log(`${i18nConfig["ExtraBlockUploaded"]} to page: ${pageId}`); + if (i === extraChunks.length - 1) { + console.log(`${i18nConfig["BlockUploaded"]} to page: ${pageId}`); + new Notice(`${i18nConfig["BlockUploaded"]} page: ${pageId}`, 5000); + } + } + } + } + + private async fetchDatabaseCover(): Promise { + const {notionAPI, databaseID} = this.dbDetails; + const response = await requestUrl({ + url: `https://api.notion.com/v1/databases/${databaseID}`, + method: "GET", + headers: { + Authorization: "Bearer " + notionAPI, + "Notion-Version": "2022-06-28", + }, + }); + + if (response.json.cover && response.json.cover.external) { + return response.json.cover.external.url; + } + + return null; + } +} diff --git a/src/upload/upoload_custom/getMarkdownCustom.ts b/src/upload/common/getMarkdownCustom.ts similarity index 83% rename from src/upload/upoload_custom/getMarkdownCustom.ts rename to src/upload/common/getMarkdownCustom.ts index c4bd7c4..5aed429 100644 --- a/src/upload/upoload_custom/getMarkdownCustom.ts +++ b/src/upload/common/getMarkdownCustom.ts @@ -1,6 +1,6 @@ -import { App, Notice } from "obsidian"; -import { i18nConfig } from "../../lang/I18n"; -import { DatabaseDetails } from "../../ui/settingTabs"; +import {App, Notice} from "obsidian"; +import {i18nConfig} from "../../lang/I18n"; +import {DatabaseDetails} from "../../ui/settingTabs"; export async function getNowFileMarkdownContentCustom( app: App, @@ -24,6 +24,7 @@ export async function getNowFileMarkdownContentCustom( .map(property => property.customName); // Extract custom values from the front matter based on the names + // Only collect data 'Relation' should be handled separately in the function buildBodyString customPropertyNames.forEach(propertyName => { if (FileCache.frontmatter && FileCache.frontmatter[propertyName] !== undefined) { customValues[propertyName] = FileCache.frontmatter[propertyName]; @@ -31,7 +32,8 @@ export async function getNowFileMarkdownContentCustom( }); // Check if any of the customProperties has a customType of 'title' - const titleProperty = dbDetails.customProperties.find(property => property.customType === 'title'); + const titleProperty = dbDetails.customProperties + .find(property => property.customType === 'title'); // If a 'title' type property exists, use the file's basename as its value if (titleProperty) { diff --git a/src/upload/upload_general/getMarkdownGeneral.ts b/src/upload/common/getMarkdownGeneral.ts similarity index 100% rename from src/upload/upload_general/getMarkdownGeneral.ts rename to src/upload/common/getMarkdownGeneral.ts diff --git a/src/upload/upload_next/getMarkdownNext.ts b/src/upload/common/getMarkdownNext.ts similarity index 100% rename from src/upload/upload_next/getMarkdownNext.ts rename to src/upload/common/getMarkdownNext.ts diff --git a/src/upload/uploadCommand.ts b/src/upload/uploadCommand.ts index eb272f3..879e0c6 100644 --- a/src/upload/uploadCommand.ts +++ b/src/upload/uploadCommand.ts @@ -1,13 +1,11 @@ import {i18nConfig} from "../lang/I18n"; import {App, Notice} from "obsidian"; -import {Upload2NotionNext} from "./upload_next/Upload2NotionNext"; -import {Upload2NotionGeneral} from "./upload_general/Upload2NotionGeneral"; -import {Upload2NotionCustom} from "./upoload_custom/Upload2NotionCustom"; +import {Upload2Notion} from "./Upload2Notion"; import {DatabaseDetails, PluginSettings} from "../ui/settingTabs"; import ObsidianSyncNotionPlugin from "../main"; -import {getNowFileMarkdownContentNext} from "./upload_next/getMarkdownNext"; -import {getNowFileMarkdownContentGeneral} from "./upload_general/getMarkdownGeneral"; -import {getNowFileMarkdownContentCustom} from "./upoload_custom/getMarkdownCustom"; +import {getNowFileMarkdownContentNext} from "./common/getMarkdownNext"; +import {getNowFileMarkdownContentGeneral} from "./common/getMarkdownGeneral"; +import {getNowFileMarkdownContentCustom} from "./common/getMarkdownCustom"; export async function uploadCommandNext( plugin: ObsidianSyncNotionPlugin, @@ -17,6 +15,7 @@ export async function uploadCommandNext( ) { const {notionAPI, databaseID} = dbDetails; + console.log(`[uploadCommandNext] ${new Date().toISOString()} Triggered for file`, app.workspace.getActiveFile()?.path); // Check if the user has set up the Notion API and database ID if (notionAPI === "" || databaseID === "") { @@ -44,8 +43,25 @@ export async function uploadCommandNext( if (markDownData) { const {basename} = nowFile; - const upload = new Upload2NotionNext(plugin, dbDetails); - const res = await upload.syncMarkdownToNotionNext(basename, emoji, cover, tags, type, slug, stats, category, summary, paword, favicon, datetime, markDownData, nowFile, this.app); + const upload = new Upload2Notion(plugin, dbDetails); + const res = await upload.sync({ + dataset: "next", + title: basename, + emoji: emoji || "", + cover: cover || "", + tags: tags || [], + type: type || "", + slug: slug || "", + stats: stats || "", + category: category || "", + summary: summary || "", + password: paword || "", + favicon: favicon || "", + datetime: datetime || "", + markdown: markDownData, + nowFile, + app, + }); const {response} = res; if (response.status === 200) { @@ -68,6 +84,7 @@ export async function uploadCommandGeneral( ) { const {notionAPI, databaseID} = dbDetails; + console.log(`[uploadCommandGeneral] ${new Date().toISOString()} Triggered for file`, app.workspace.getActiveFile()?.path); // Check if the user has set up the Notion API and database ID if (notionAPI === "" || databaseID === "") { @@ -84,8 +101,16 @@ export async function uploadCommandGeneral( if (markDownData) { const {basename} = nowFile; - const upload = new Upload2NotionGeneral(plugin, dbDetails); - const res = await upload.syncMarkdownToNotionGeneral(basename, cover, tags, markDownData, nowFile, this.app); + const upload = new Upload2Notion(plugin, dbDetails); + const res = await upload.sync({ + dataset: "general", + title: basename, + cover: cover || "", + tags: tags || [], + markdown: markDownData, + nowFile, + app, + }); const {response} = res; if (response.status === 200) { @@ -107,7 +132,8 @@ export async function uploadCommandCustom( app: App, ) { - const {notionAPI, databaseID} = settings; + const {notionAPI, databaseID} = dbDetails; + console.log(`[uploadCommandCustom] ${new Date().toISOString()} Triggered for file`, app.workspace.getActiveFile()?.path); // Check if the user has set up the Notion API and database ID if (notionAPI === "" || databaseID === "") { @@ -124,8 +150,15 @@ export async function uploadCommandCustom( if (markDownData) { const {basename} = nowFile; - const upload = new Upload2NotionCustom(plugin, dbDetails); - const res = await upload.syncMarkdownToNotionCustom(cover, customValues, markDownData, nowFile, this.app); + const upload = new Upload2Notion(plugin, dbDetails); + const res = await upload.sync({ + dataset: "custom", + cover: cover || "", + customValues, + markdown: markDownData, + nowFile, + app, + }); const {response} = res; diff --git a/src/upload/upload_general/BaseUpload2NotionGeneral.ts b/src/upload/upload_general/BaseUpload2NotionGeneral.ts deleted file mode 100644 index 11efd19..0000000 --- a/src/upload/upload_general/BaseUpload2NotionGeneral.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { App, Notice, requestUrl, TFile } from "obsidian"; -import { Client } from '@notionhq/client'; -import { markdownToBlocks, } from "@jxpeng98/martian"; -import * as yamlFrontMatter from "yaml-front-matter"; -// import * as yaml from "yaml" -import MyPlugin from "src/main"; -import { DatabaseDetails } from "../../ui/settingTabs"; - -export class UploadBaseGeneral { - plugin: MyPlugin; - notion: Client; - agent: any; - dbDetails: DatabaseDetails - - constructor(plugin: MyPlugin, dbDetails: DatabaseDetails) { - this.plugin = plugin; - this.dbDetails = dbDetails - } - - async deletePage(notionID: string) { - - const { notionAPI } = this.dbDetails - return requestUrl({ - url: `https://api.notion.com/v1/blocks/${notionID}`, - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer ' + notionAPI, - 'Notion-Version': '2022-06-28', - }, - body: '' - }); - } - - async getDataBase(databaseID: string) { - const { notionAPI } = this.dbDetails - const response = await requestUrl({ - url: `https://api.notion.com/v1/databases/${databaseID}`, - method: 'GET', - headers: { - 'Authorization': 'Bearer ' + notionAPI, - 'Notion-Version': '2022-06-28', - } - } - ) - - // Check if cover is present in the JSON response and then get the URL - if (response.json.cover && response.json.cover.external) { - return response.json.cover.external.url; - } else { - return null; // or some other default value, if you prefer - } - } - - // async updateYamlInfo(yamlContent: string, nowFile: TFile, res: any, app: App, settings: any) { - // let {url, id} = res.json - // // replace www to notionID - // const {notionUser} = this.plugin.settings; - // - // if (notionUser !== "") { - // // replace url str "www" to notionID - // url = url.replace("www.notion.so", `${notionUser}.notion.site`) - // } - // - // await app.fileManager.processFrontMatter(nowFile, yamlContent => { - // if (yamlContent['notionID']) { - // delete yamlContent['notionID'] - // } - // if (yamlContent['link']) { - // delete yamlContent['link'] - // } - // // add new notionID and link - // yamlContent.notionID = id; - // yamlContent.link = url; - // }); - // - // try { - // await navigator.clipboard.writeText(url) - // } catch (error) { - // new Notice(`复制链接失败,请手动复制${error}`) - // } - // // const __content = yamlContent.__content; - // // delete yamlContent.__content - // // const yamlhead = yaml.stringify(yamlContent) - // // // if yamlhead hava last \n remove it - // // const yamlhead_remove_n = yamlhead.replace(/\n$/, '') - // // // if __content have start \n remove it - // // const __content_remove_n = __content.replace(/^\n/, '') - // // const content = '---\n' +yamlhead_remove_n +'\n---\n' + __content_remove_n; - // // try { - // // await nowFile.vault.modify(nowFile, content) - // // } catch (error) { - // // new Notice(`write file error ${error}`) - // // } - // } -} diff --git a/src/upload/upload_general/Upload2NotionGeneral.ts b/src/upload/upload_general/Upload2NotionGeneral.ts deleted file mode 100644 index 80b1f1b..0000000 --- a/src/upload/upload_general/Upload2NotionGeneral.ts +++ /dev/null @@ -1,269 +0,0 @@ -import {App, Notice, TFile, requestUrl} from "obsidian"; -import {markdownToBlocks} from "@jxpeng98/martian"; -import * as yamlFrontMatter from "yaml-front-matter"; -import MyPlugin from "src/main"; -import {DatabaseDetails, PluginSettings} from "../../ui/settingTabs"; -import {UploadBaseGeneral} from "./BaseUpload2NotionGeneral"; -import {updateYamlInfo} from "../updateYaml"; -import {i18nConfig} from "../../lang/I18n"; - -interface CreatePageResponse { - response: any; - data: any; -} - -export class Upload2NotionGeneral extends UploadBaseGeneral { - settings: PluginSettings; - dbDetails: DatabaseDetails; - - constructor(plugin: MyPlugin, dbDetails: DatabaseDetails) { - super(plugin, dbDetails); - this.dbDetails = dbDetails; - } - - // 因为需要解析notion的block进行对比,非常的麻烦, - // 暂时就直接删除,新建一个page - async updatePage( - notionID: string, - title: string, - cover: string, - tags: string[], - childArr: any, - ) { - await this.deletePage(notionID); - - const {databaseID} = this.dbDetails; - - const databaseCover = await this.getDataBase( - databaseID, - ); - - if (cover == null) { - cover = databaseCover; - } - - return await this.createPage(title, cover, tags, childArr); - } - - async createPage( - title: string, - cover: string, - tags: string[], - childArr: any, - ): Promise { - - const { - databaseID, - customTitleButton, - customTitleName, - tagButton, - notionAPI - } = this.dbDetails; - - // remove the annotations from the childArr if type is code block - childArr.forEach((block: any) => { - if (block.type === "code") { - block.code.rich_text.forEach((item: any) => { - if (item.type === "text" && item.annotations) { - delete item.annotations; - } - } - ); - } - } - ); - - // check the length of the childArr and split it into chunks of 100 - const childArrLength = childArr.length; - let extraArr: any[] = []; - let firstArr: any; - let pushCount = 0; - - console.log(`Page includes ${childArrLength} blocks`) - - if (childArrLength > 100) { - for (let i = 0; i < childArr.length; i += 100) { - if (i == 0) { - firstArr = childArr.slice(0, 100); - } else { - const chunk = childArr.slice(i, i + 100); - extraArr.push(chunk); - pushCount++; - } - } - } else { - firstArr = childArr; - } - - const bodyString: any = { - parent: { - database_id: databaseID, - }, - properties: { - [customTitleButton - ? customTitleName - : "title"]: { - title: [ - { - text: { - content: title, - }, - }, - ], - }, - ...(tagButton - ? { - tags: { - multi_select: tags && true ? tags.map((tag) => ({name: tag})) : [], - }, - } - : {}), - }, - children: firstArr, - }; - - if (cover) { - bodyString.cover = { - type: "external", - external: { - url: cover, - }, - }; - } - - if (!bodyString.cover && this.plugin.settings.bannerUrl) { - bodyString.cover = { - type: "external", - external: { - url: this.plugin.settings.bannerUrl, - }, - }; - } - - console.log(bodyString) - - let response: any; - let data: any; - - response = await requestUrl({ - url: `https://api.notion.com/v1/pages`, - method: "POST", - headers: { - "Content-Type": "application/json", - // 'User-Agent': 'obsidian.md', - Authorization: - "Bearer " + notionAPI, - "Notion-Version": "2022-06-28", - }, - body: JSON.stringify(bodyString), - throw: false - }); - - data = await response.json; - - // console.log(data) - // console.log(response.status) - - if (response.status !== 200) { - new Notice(`Error ${data.status}: ${data.code} \n ${i18nConfig["CheckConsole"]}`, 5000); - console.log(`Error message: \n ${data.message}`); - } else { - console.log(`Page created: ${data.url}`); - console.log(`Page ID: ${data.id}`); - } - // - // upload the rest of the blocks - if (pushCount > 0) { - for (let i = 0; i < pushCount; i++) { - const extraBlocks = { - children: extraArr[i], - }; - - console.log(extraBlocks) - - const extraResponse = await requestUrl({ - url: `https://api.notion.com/v1/blocks/${data.id}/children`, - method: "PATCH", - headers: { - "Content-Type": "application/json", - "Authorization": "Bearer " + notionAPI, - "Notion-Version": "2022-06-28", - }, - body: JSON.stringify(extraBlocks), - }); - - const extraData: any = await extraResponse.json; - - if (extraResponse.status !== 200) { - new Notice(`Error ${extraData.status}: ${extraData.code} \n ${i18nConfig["CheckConsole"]}`, 5000); - console.log(`Error message: \n ${extraData.message}`); - } else { - console.log(`${i18nConfig["ExtraBlockUploaded"]} to page: ${data.id}`); - if (i === pushCount - 1) { - console.log(`${i18nConfig["BlockUploaded"]} to page: ${data.id}`); - new Notice(`${i18nConfig["BlockUploaded"]} page: ${data.id}`, 5000); - } - } - } - } - - return { - response, - data - } - } - - async syncMarkdownToNotionGeneral( - title: string, - cover: string, - tags: string[], - markdown: string, - nowFile: TFile, - app: App, - ): Promise { - const options = { - notionLimits: { - truncate: false, - } - } - let res: any; - const yamlContent: any = yamlFrontMatter.loadFront(markdown); - const __content = yamlContent.__content; - const file2Block = markdownToBlocks(__content, options); - const frontMatter = - app.metadataCache.getFileCache(nowFile)?.frontmatter; - const {abName} = this.dbDetails - const notionIDKey = `NotionID-${abName}`; - const notionID = frontMatter ? frontMatter[notionIDKey] : null; - - - if (notionID) { - res = await this.updatePage( - notionID, - title, - cover, - tags, - file2Block, - ); - } else { - res = await this.createPage(title, cover, tags, file2Block); - } - - let {response, data} = res; - - // console.log(response) - - if (response && response.status === 200) { - await updateYamlInfo( - markdown, - nowFile, - data, - app, - this.plugin, - this.dbDetails, - ); - } - - return res; - } -} diff --git a/src/upload/upload_next/BaseUpload2NotionNext.ts b/src/upload/upload_next/BaseUpload2NotionNext.ts deleted file mode 100644 index 906f92a..0000000 --- a/src/upload/upload_next/BaseUpload2NotionNext.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { App, Notice, requestUrl, TFile } from "obsidian"; -import { Client } from '@notionhq/client'; -import { markdownToBlocks, } from "@jxpeng98/martian"; -import * as yamlFrontMatter from "yaml-front-matter"; -// import * as yaml from "yaml" -import MyPlugin from "src/main"; -import { DatabaseDetails } from "../../ui/settingTabs"; - -export class UploadBaseNext { - plugin: MyPlugin; - notion: Client; - agent: any; - dbDetails: DatabaseDetails - - constructor(plugin: MyPlugin, dbDetails: DatabaseDetails) { - this.plugin = plugin; - this.dbDetails = dbDetails - } - - async deletePage(notionID: string) { - const { notionAPI } = this.dbDetails - return requestUrl({ - url: `https://api.notion.com/v1/blocks/${notionID}`, - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer ' + notionAPI, - 'Notion-Version': '2022-06-28', - }, - body: '' - }); - } - - async getDataBase(databaseID: string) { - const { notionAPI } = this.dbDetails - - const response = await requestUrl({ - url: `https://api.notion.com/v1/databases/${databaseID}`, - method: 'GET', - headers: { - 'Authorization': 'Bearer ' + notionAPI, - 'Notion-Version': '2022-06-28', - } - } - ) - - // Check if cover is present in the JSON response and then get the URL - if (response.json.cover && response.json.cover.external) { - return response.json.cover.external.url; - } else { - return null; // or some other default value, if you prefer - } - } - - // async updateYamlInfo(yamlContent: string, nowFile: TFile, res: any, app: App, settings: any) { - // let {url, id} = res.json - // // replace www to notionID - // const { notionUser} = this.plugin.settings; - // - // if (notionUser !== "") { - // // replace url str "www" to notionID - // url = url.replace("www.notion.so", `${notionUser}.notion.site`) - // } - // - // await app.fileManager.processFrontMatter(nowFile, yamlContent => { - // if (yamlContent['notionID']) { - // delete yamlContent['notionID'] - // } - // if (yamlContent['link']) { - // delete yamlContent['link'] - // } - // // add new notionID and link - // yamlContent.notionID = id; - // yamlContent.link = url; - // }); - // - // try { - // await navigator.clipboard.writeText(url) - // } catch (error) { - // new Notice(`复制链接失败,请手动复制${error}`) - // } - // // const __content = yamlContent.__content; - // // delete yamlContent.__content - // // const yamlhead = yaml.stringify(yamlContent) - // // // if yamlhead hava last \n remove it - // // const yamlhead_remove_n = yamlhead.replace(/\n$/, '') - // // // if __content have start \n remove it - // // const __content_remove_n = __content.replace(/^\n/, '') - // // const content = '---\n' +yamlhead_remove_n +'\n---\n' + __content_remove_n; - // // try { - // // await nowFile.vault.modify(nowFile, content) - // // } catch (error) { - // // new Notice(`write file error ${error}`) - // // } - // } -} diff --git a/src/upload/upload_next/Upload2NotionNext.ts b/src/upload/upload_next/Upload2NotionNext.ts deleted file mode 100644 index 1bea427..0000000 --- a/src/upload/upload_next/Upload2NotionNext.ts +++ /dev/null @@ -1,420 +0,0 @@ -import {UploadBaseNext} from "./BaseUpload2NotionNext"; -import {App, Notice, TFile, requestUrl} from "obsidian"; -import {markdownToBlocks} from "@jxpeng98/martian"; -import * as yamlFrontMatter from "yaml-front-matter"; -import MyPlugin from "src/main"; -import {DatabaseDetails, PluginSettings} from "../../ui/settingTabs"; -import {updateYamlInfo} from "../updateYaml"; -import {LIMITS, paragraph} from "@jxpeng98/martian/src/notion"; -import {i18nConfig} from "../../lang/I18n"; - -interface CreatePageResponse { - response: any; - data: any; -} - -export class Upload2NotionNext extends UploadBaseNext { - settings: PluginSettings; - dbDetails: DatabaseDetails; - - constructor(plugin: MyPlugin, dbDetails: DatabaseDetails) { - super(plugin, dbDetails); - this.dbDetails = dbDetails; - } - - // 因为需要解析notion的block进行对比,非常的麻烦, - // 暂时就直接删除,新建一个page - async updatePage( - notionID: string, - title: string, - emoji: string, - cover: string, - tags: string[], - type: string, - slug: string, - stats: string, - category: string, - summary: string, - paword: string, - favicon: string, - datetime: string, - childArr: any, - ) { - await this.deletePage(notionID); - - const {databaseID} = this.dbDetails; - - const databaseCover = await this.getDataBase(databaseID); - - if (cover == null) { - cover = databaseCover; - } - - return await this.createPage( - title, - emoji, - cover, - tags, - type, - slug, - stats, - category, - summary, - paword, - favicon, - datetime, - childArr, - ); - } - - async createPage( - title: string, - emoji: string, - cover: string, - tags: string[], - type: string, - slug: string, - stats: string, - category: string, - summary: string, - pawrod: string, - favicon: string, - datetime: string, - childArr: any, - ): Promise { - const {databaseID, notionAPI} = this.dbDetails; - - // remove the annotations from the childArr if type is code block - childArr.forEach((block: any) => { - if (block.type === "code") { - block.code.rich_text.forEach((item: any) => { - if (item.type === "text" && item.annotations) { - delete item.annotations; - } - } - ); - } - } - ); - - // check the length of the childArr - // if it is too long, split it into multiple pages - const childArrLength = childArr.length; - let extraArr: any[] = []; - let firstArr: any; - let pushCount = 0; - - console.log(`Page includes ${childArrLength} blocks`) - - if (childArrLength > 100) { - for (let i = 0; i < childArr.length; i += 100) { - if (i == 0) { - firstArr = childArr.slice(0, 100); - } else { - const chunk = childArr.slice(i, i + 100); - extraArr.push(chunk); - pushCount++; - } - } - } else { - firstArr = childArr; - } - - const pageProperties: any = { - parent: { - database_id: databaseID, - }, - properties: { - title: { - title: [ - { - text: { - content: title, - }, - }, - ], - }, - type: { - select: { - name: type || "Post", - }, - }, - status: { - select: { - name: stats || "Draft", - }, - }, - category: { - select: { - name: category || "Obsidian", - }, - }, - - password: { - rich_text: [ - { - text: { - content: pawrod || "", - }, - }, - ], - }, - icon: { - rich_text: [ - { - text: { - content: favicon || "", - }, - }, - ], - }, - date: { - date: { - start: datetime || new Date().toISOString(), - }, - }, - }, - }; - - // add tags - if (tags) { - pageProperties.properties.tags = { - multi_select: tags.map((tag) => { - return {name: tag}; - }), - }; - } - - // add title icon - if (emoji) { - pageProperties.icon = { - emoji: emoji, - }; - } - - // add slug - if (slug) { - pageProperties.properties.slug = { - rich_text: [ - { - text: { - content: slug, - }, - }, - ], - }; - } - - // check if summary is available - if (summary) { - pageProperties.properties.summary = { - rich_text: [ - { - text: { - content: summary, - }, - }, - ], - }; - } - - if (cover) { - pageProperties.cover = { - type: "external", - external: { - url: cover, - }, - }; - } - - if (!pageProperties.cover && this.plugin.settings.bannerUrl) { - pageProperties.cover = { - type: "external", - external: { - url: this.plugin.settings.bannerUrl, - }, - }; - } - - const bodyString: any = { - ...pageProperties, - children: firstArr, - }; - - console.log(bodyString); - - let response: any; - let data: any; - - response = await requestUrl({ - url: `https://api.notion.com/v1/pages`, - method: "POST", - headers: { - "Content-Type": "application/json", - // 'User-Agent': 'obsidian.md', - Authorization: - "Bearer " + notionAPI, - "Notion-Version": "2022-06-28", - }, - body: JSON.stringify(bodyString), - throw: false - }); - - data = await response.json; - - // console.log(data) - // console.log(response.status) - - if (response.status !== 200) { - new Notice(`Error ${data.status}: ${data.code} \n ${i18nConfig["CheckConsole"]}`, 5000); - console.log(`Error message: \n ${data.message}`); - } else { - console.log(`Page created: ${data.url}`); - console.log(`Page ID: ${data.id}`); - } - // - // upload the rest of the blocks - if (pushCount > 0) { - for (let i = 0; i < pushCount; i++) { - const extraBlocks = { - children: extraArr[i], - }; - - console.log(extraBlocks) - - const extraResponse = await requestUrl({ - url: `https://api.notion.com/v1/blocks/${data.id}/children`, - method: "PATCH", - headers: { - "Content-Type": "application/json", - "Authorization": "Bearer " + notionAPI, - "Notion-Version": "2022-06-28", - }, - body: JSON.stringify(extraBlocks), - }); - - const extraData: any = await extraResponse.json; - - if (extraResponse.status !== 200) { - new Notice(`Error ${extraData.status}: ${extraData.code} \n ${i18nConfig["CheckConsole"]}`, 5000); - console.log(`Error message: \n ${extraData.message}`); - } else { - console.log(`${i18nConfig["ExtraBlockUploaded"]} to page: ${data.id}`); - if (i === pushCount - 1) { - console.log(`${i18nConfig["BlockUploaded"]} to page: ${data.id}`); - new Notice(`${i18nConfig["BlockUploaded"]} page: ${data.id}`, 5000); - } - } - } - } - - return { - response, - data - }; - } - - async syncMarkdownToNotionNext( - title: string, - emoji: string, - cover: string, - tags: string[], - type: string, - slug: string, - stats: string, - category: string, - summary: string, - paword: string, - favicon: string, - datetime: string, - markdown: string, - nowFile: TFile, - app: App, - ): Promise { - const options = { - notionLimits: { - truncate: false, - }, - }; - let res: any; - const yamlContent: any = yamlFrontMatter.loadFront(markdown); - const __content = yamlContent.__content; - const file2Block = markdownToBlocks(__content, options); - const frontmatter = - app.metadataCache.getFileCache(nowFile)?.frontmatter; - const {abName} = this.dbDetails; - const notionIDKey = `NotionID-${abName}`; - const notionID = frontmatter ? frontmatter[notionIDKey] : null; - - // increase the limits - // Motivated by https://github.com/tryfabric/martian/issues/51 - file2Block.forEach((block, index) => { - if ( - block.type === "paragraph" && - block.paragraph.rich_text.length > LIMITS.RICH_TEXT_ARRAYS - ) { - const newParagraphBlocks: any[] = []; - const chunk: any = []; - const richTextChunks = chunk(block.paragraph.rich_text, 100); - - richTextChunks.forEach((chunk: any) => { - newParagraphBlocks.push(paragraph(chunk)); - }); - - file2Block.splice(index, 1, ...newParagraphBlocks); - } - }); - - if (notionID) { - res = await this.updatePage( - notionID, - title, - emoji, - cover, - tags, - type, - slug, - stats, - category, - summary, - paword, - favicon, - datetime, - file2Block, - ); - } else { - res = await this.createPage( - title, - emoji, - cover, - tags, - type, - slug, - stats, - category, - summary, - paword, - favicon, - datetime, - file2Block, - ); - } - - let {response, data} = res; - - // console.log(response) - - if (response && response.status === 200) { - await updateYamlInfo( - markdown, - nowFile, - data, - app, - this.plugin, - this.dbDetails, - ); - } - - return res; - } -} diff --git a/src/upload/upoload_custom/BaseUpload2NotionCustom.ts b/src/upload/upoload_custom/BaseUpload2NotionCustom.ts deleted file mode 100644 index cf65c5f..0000000 --- a/src/upload/upoload_custom/BaseUpload2NotionCustom.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { App, Notice, requestUrl, TFile } from "obsidian"; -import { Client } from '@notionhq/client'; -import { markdownToBlocks, } from "@jxpeng98/martian"; -import * as yamlFrontMatter from "yaml-front-matter"; -// import * as yaml from "yaml" -import MyPlugin from "src/main"; -import { DatabaseDetails } from "../../ui/settingTabs"; - -export class UploadBaseCustom { - plugin: MyPlugin; - notion: Client; - agent: any; - dbDetails: DatabaseDetails - - constructor(plugin: MyPlugin, dbDetails: DatabaseDetails) { - this.plugin = plugin; - this.dbDetails = dbDetails - } - - async deletePage(notionID: string) { - const { notionAPI } = this.dbDetails - return requestUrl({ - url: `https://api.notion.com/v1/blocks/${notionID}`, - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer ' + notionAPI, - 'Notion-Version': '2022-06-28', - }, - body: '' - }); - } - - async getDataBase(databaseID: string) { - const { notionAPI } = this.dbDetails - const response = await requestUrl({ - url: `https://api.notion.com/v1/databases/${databaseID}`, - method: 'GET', - headers: { - 'Authorization': 'Bearer ' + notionAPI, - 'Notion-Version': '2022-06-28', - } - } - ) - - // Check if cover is present in the JSON response and then get the URL - if (response.json.cover && response.json.cover.external) { - return response.json.cover.external.url; - } else { - return null; // or some other default value, if you prefer - } - } -} diff --git a/src/upload/upoload_custom/Upload2NotionCustom.ts b/src/upload/upoload_custom/Upload2NotionCustom.ts deleted file mode 100644 index 9ff7910..0000000 --- a/src/upload/upoload_custom/Upload2NotionCustom.ts +++ /dev/null @@ -1,348 +0,0 @@ -import {App, Notice, TFile, requestUrl} from "obsidian"; -import {markdownToBlocks} from "@jxpeng98/martian"; -import * as yamlFrontMatter from "yaml-front-matter"; -import MyPlugin from "src/main"; -import {DatabaseDetails, PluginSettings} from "../../ui/settingTabs"; -import {updateYamlInfo} from "../updateYaml"; -import {UploadBaseCustom} from "./BaseUpload2NotionCustom"; -import {i18nConfig} from "../../lang/I18n"; - - -interface CreatePageResponse { - response: any; - data: any; -} - -export class Upload2NotionCustom extends UploadBaseCustom { - settings: PluginSettings; - dbDetails: DatabaseDetails; - - constructor(plugin: MyPlugin, dbDetails: DatabaseDetails) { - super(plugin, dbDetails); - this.dbDetails = dbDetails; - } - - // 因为需要解析notion的block进行对比,非常的麻烦, - // 暂时就直接删除,新建一个page - async updatePage( - notionID: string, - cover: string, - customValues: Record, - childArr: any, - ) { - await this.deletePage(notionID); - - const {databaseID} = this.dbDetails; - - const databaseCover = await this.getDataBase( - databaseID - ); - - if (cover == null) { - cover = databaseCover; - } - - return await this.createPage(cover, customValues, childArr); - } - - async createPage( - cover: string, - customValues: Record, - childArr: any, - ): Promise { - - const { - databaseID, - customProperties, - notionAPI - } = this.dbDetails; - - // remove the annotations from the childArr if type is code block - childArr.forEach((block: any) => { - if (block.type === "code") { - block.code.rich_text.forEach((item: any) => { - if (item.type === "text" && item.annotations) { - delete item.annotations; - } - } - ); - } - } - ); - - // check the length of the childArr and split it into chunks of 100 - const childArrLength = childArr.length; - let extraArr: any[] = []; - let firstArr: any; - let pushCount = 0; - - console.log(`Page includes ${childArrLength} blocks`) - - if (childArrLength > 100) { - for (let i = 0; i < childArr.length; i += 100) { - if (i == 0) { - firstArr = childArr.slice(0, 100); - } else { - const chunk = childArr.slice(i, i + 100); - extraArr.push(chunk); - pushCount++; - } - } - } else { - firstArr = childArr; - } - - const bodyString: any = this.buildBodyString(customProperties, customValues, firstArr); - - if (cover) { - bodyString.cover = { - type: "external", - external: { - url: cover, - }, - }; - } - - if (!bodyString.cover && this.plugin.settings.bannerUrl) { - bodyString.cover = { - type: "external", - external: { - url: this.plugin.settings.bannerUrl, - }, - }; - } - - console.log(bodyString) - - let response: any; - let data: any; - - response = await requestUrl({ - url: `https://api.notion.com/v1/pages`, - method: "POST", - headers: { - "Content-Type": "application/json", - // 'User-Agent': 'obsidian.md', - Authorization: - "Bearer " + notionAPI, - "Notion-Version": "2022-06-28", - }, - body: JSON.stringify(bodyString), - throw: false - }); - - data = await response.json; - - // console.log(data) - // console.log(response.status) - - if (response.status !== 200) { - new Notice(`Error ${data.status}: ${data.code} \n ${i18nConfig["CheckConsole"]}`, 5000); - console.log(`Error message: \n ${data.message}`); - } else { - console.log(`Page created: ${data.url}`); - console.log(`Page ID: ${data.id}`); - } - // - // upload the rest of the blocks - if (pushCount > 0) { - for (let i = 0; i < pushCount; i++) { - const extraBlocks = { - children: extraArr[i], - }; - - console.log(extraBlocks) - - const extraResponse = await requestUrl({ - url: `https://api.notion.com/v1/blocks/${data.id}/children`, - method: "PATCH", - headers: { - "Content-Type": "application/json", - "Authorization": "Bearer " + notionAPI, - "Notion-Version": "2022-06-28", - }, - body: JSON.stringify(extraBlocks), - }); - - const extraData: any = await extraResponse.json; - - if (extraResponse.status !== 200) { - new Notice(`Error ${extraData.status}: ${extraData.code} \n ${i18nConfig["CheckConsole"]}`, 5000); - console.log(`Error message: \n ${extraData.message}`); - } else { - console.log(`${i18nConfig["ExtraBlockUploaded"]} to page: ${data.id}`); - if (i === pushCount - 1) { - console.log(`${i18nConfig["BlockUploaded"]} to page: ${data.id}`); - new Notice(`${i18nConfig["BlockUploaded"]} page: ${data.id}`, 5000); - } - } - } - } - - return { - response, // for status code - data // for id and url - } - } - - async syncMarkdownToNotionCustom( - cover: string, - customValues: Record, - markdown: string, - nowFile: TFile, - app: App, - ): Promise { - const options = { - strictImageUrls: true, - notionLimits: { - truncate: false, - } - } - let res: any; - const yamlContent: any = yamlFrontMatter.loadFront(markdown); - const __content = yamlContent.__content; - const file2Block = markdownToBlocks(__content, options); - const frontMatter = - app.metadataCache.getFileCache(nowFile)?.frontmatter; - const {abName} = this.dbDetails - const notionIDKey = `NotionID-${abName}`; - const notionID = frontMatter ? frontMatter[notionIDKey] : null; - - if (notionID) { - res = await this.updatePage( - notionID, - cover, - customValues, - file2Block, - ); - } else { - res = await this.createPage(cover, customValues, file2Block); - } - - let {response, data} = res; - - // console.log(response) - - if (response && response.status === 200) { - await updateYamlInfo( - markdown, - nowFile, - data, - app, - this.plugin, - this.dbDetails, - ); - } - - return res; - } - - private buildPropertyObject(customName: string, customType: string, customValues: Record) { - const value = customValues[customName] || ''; - - switch (customType) { - case "title": - return { - title: [ - { - text: { - content: value, - }, - }, - ], - }; - case "rich_text": - return { - rich_text: [ - { - text: { - content: value || '', - }, - }, - ], - }; - case "date": - return { - date: { - start: value || new Date().toISOString(), - }, - }; - case "number": - return { - number: Number(value), - }; - case "phone_number": - return { - phone_number: value, - }; - case "email": - return { - email: value, - }; - case "url": - return { - url: value, - }; - case "files": - return { - files: Array.isArray(value) ? value.map(url => ({ - name: url, - type: "external", - external: { - url: url, - }, - })) : [ - { - name: value, - type: "external", - external: { - url: value, - }, - }, - ], - }; - case "checkbox": - return { - checkbox: Boolean(value) || false, - }; - case "select": - return { - select: { - name: value, - }, - }; - case "multi_select": - return { - multi_select: Array.isArray(value) ? value.map(item => ({name: item})) : [{name: value}], - }; - } - } - - private buildBodyString( - customProperties: { customName: string; customType: string }[], - customValues: Record, - childArr: any, - ) { - - const properties: { [key: string]: any } = {}; - - // Only include custom properties that have values - customProperties.forEach(({customName, customType}) => { - if (customValues[customName] !== undefined) { - properties[customName] = this.buildPropertyObject(customName, customType, customValues); - } - } - ); - - // console.log(properties) - - return { - parent: { - database_id: this.dbDetails.databaseID, - }, - properties, - children: childArr, - }; - } - -}