From 9376b1f1c278770f40aed3ae8ca2946cfe577f01 Mon Sep 17 00:00:00 2001 From: TreyDong <1346650911@qq.com> Date: Mon, 8 Apr 2024 22:00:36 +0800 Subject: [PATCH 1/2] feat: add long text support --- Upload2Notion.ts | 81 +++++++++++++++++---- main.ts | 186 ++++++++++++++++++++++++++++++----------------- 2 files changed, 185 insertions(+), 82 deletions(-) diff --git a/Upload2Notion.ts b/Upload2Notion.ts index c7e22d6..e3de855 100644 --- a/Upload2Notion.ts +++ b/Upload2Notion.ts @@ -23,6 +23,8 @@ export class Upload2Notion { }, body: '' }) + console.log('deletePAGE') + console.log(response) return response; } @@ -34,6 +36,31 @@ export class Upload2Notion { return res } + async appendPage(pageId:string, childArr: any){ + const bodyString:any = { + children: childArr, + } + if (pageId === undefined){ + return + } + try { + const response = await requestUrl({ + url: `https://api.notion.com/v1/blocks/${pageId}/children`, + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + // 'User-Agent': 'obsidian.md', + 'Authorization': 'Bearer ' + this.app.settings.notionAPI, + 'Notion-Version': '2021-08-16', + }, + body: JSON.stringify(bodyString), + }) + return response; + } catch (error) { + new Notice(`network error ${error}`) + } + } + async createPage(title:string, allowTags:boolean, tags:string[], childArr: any) { const bodyString:any = { parent: { @@ -85,37 +112,33 @@ export class Upload2Notion { } } - async syncMarkdownToNotion(title:string, allowTags:boolean, tags:string[], markdown: string, nowFile: TFile, app:App, settings:any): Promise { + async syncMarkdownToNotion(title:string, allowTags:boolean, tags:string[], markdown: string,pageId:string): Promise { let res:any const yamlObj:any = yamlFrontMatter.loadFront(markdown); const __content = yamlObj.__content const file2Block = markdownToBlocks(__content); - const frontmasster =await app.metadataCache.getFileCache(nowFile)?.frontmatter - const notionID = frontmasster ? frontmasster.notionID : null - - if(notionID){ - res = await this.updatePage(notionID, title, allowTags, tags, file2Block); - } else { - res = await this.createPage(title, allowTags, tags, file2Block); - } - if (res.status === 200) { - await this.updateYamlInfo(markdown, nowFile, res, app, settings) - } else { - new Notice(`${res.text}`) + // 如果是第一块chunk,就直接新增 + if( pageId){ + res = await this.appendPage(pageId,file2Block) + }else{ + res = await this.createPage(title, allowTags, tags, file2Block); } return res + } async updateYamlInfo(yamlContent: string, nowFile: TFile, res: any,app:App, settings:any) { const yamlObj:any = yamlFrontMatter.loadFront(yamlContent); - let {url, id} = res.json + let {url, id} = res // replace www to notionID - const {notionID} = settings; + const {notionID,allowNotionLink} = settings; if(notionID!=="") { // replace url str "www" to notionID url = url.replace("www.notion.so", `${notionID}.notion.site`) } - yamlObj.link = url; + if (allowNotionLink){ + yamlObj.NotionLink = url; + } try { await navigator.clipboard.writeText(url) } catch (error) { @@ -136,4 +159,30 @@ export class Upload2Notion { new Notice(`write file error ${error}`) } } + + splitLongString(str: string) { + if (str.length <= 4000) { + return [str]; + } + + const chunks = []; + let startIndex = 0; + let endIndex = 4000; + + while (endIndex < str.length) { + if (str[endIndex] !== '\n') { + while (endIndex > startIndex && str[endIndex] !== '\n') { + endIndex--; + } + } + + chunks.push(str.substring(startIndex, endIndex)); + startIndex = endIndex + 1; + endIndex = startIndex + 4000; + } + + chunks.push(str.substring(startIndex)); + + return chunks; + } } diff --git a/main.ts b/main.ts index 421a50d..41a284c 100644 --- a/main.ts +++ b/main.ts @@ -9,10 +9,10 @@ import { Setting, normalizePath } from "obsidian"; -import {addIcons} from 'icon'; -import { Upload2Notion } from "Upload2Notion"; +import {addIcons} from 'icon'; +import {Upload2Notion} from "Upload2Notion"; import {NoticeMConfig} from "Message"; -import { CLIENT_RENEG_LIMIT } from "tls"; +import {CLIENT_RENEG_LIMIT} from "tls"; // Remember to rename these classes and interfaces! @@ -24,9 +24,11 @@ interface PluginSettings { notionID: string; proxy: string; allowTags: boolean; + allowNotionLink: boolean; + folderPath: string; } -const langConfig = NoticeMConfig( window.localStorage.getItem('language') || 'en') +const langConfig = NoticeMConfig(window.localStorage.getItem('language') || 'en') const DEFAULT_SETTINGS: PluginSettings = { notionAPI: "", @@ -34,11 +36,14 @@ const DEFAULT_SETTINGS: PluginSettings = { bannerUrl: "", notionID: "", proxy: "", - allowTags: false + allowTags: false, + allowNotionLink: false, + folderPath: "", }; export default class ObsidianSyncNotionPlugin extends Plugin { settings: PluginSettings; + async onload() { await this.loadSettings(); addIcons(); @@ -70,46 +75,69 @@ export default class ObsidianSyncNotionPlugin extends Plugin { } - onunload() {} - - async upload(){ - const { notionAPI, databaseID, allowTags } = this.settings; - if (notionAPI === "" || databaseID === "") { - new Notice( - "Please set up the notion API and database ID in the settings tab." - ); - return; - } - const { markDownData, nowFile, tags } =await this.getNowFileMarkdownContent(this.app); - + onunload() { + } - if (markDownData) { - const { basename } = nowFile; - const upload = new Upload2Notion(this); - const res = await upload.syncMarkdownToNotion(basename, allowTags, tags, markDownData, nowFile, this.app, this.settings) - if(res.status === 200){ - new Notice(`${langConfig["sync-success"]}${basename}`) - }else { - new Notice(`${langConfig["sync-fail"]}${basename}`, 5000) + async upload() { + const {notionAPI, databaseID, allowTags} = this.settings; + if (notionAPI === "" || databaseID === "") { + new Notice( + "Please set up the notion API and database ID in the settings tab." + ); + return; + } + const upload = new Upload2Notion(this); + const {markDownData, chunks, nowFile, tags} = await this.getNowFileMarkdownContent(this.app); + const frontmasster = app.metadataCache.getFileCache(nowFile)?.frontmatter + const notionID = frontmasster ? frontmasster.notionID : null + // 存在就先删除 + if (notionID) { + await upload.deletePage(notionID) + } + try { + if (chunks) { + const {basename} = nowFile; + // create page + const res = await upload.syncMarkdownToNotion(basename, allowTags, tags, chunks[0], null) + let {id} = res.json + // append page + for (let i = 1; i < chunks.length; i++) { + const response = await upload.syncMarkdownToNotion(basename, allowTags, tags, chunks[i], id) + if (response.status != 200) { + new Notice(`${langConfig["sync-fail"]}${basename},please retry!`, 5000) + break } } + await upload.updateYamlInfo(markDownData, nowFile, res, app, this.settings) + new Notice(`${langConfig["sync-success"]}${basename}`) + + } else { + new Notice('sync-fail,please check your file in obsidian') + } + } catch (Exception) { + new Notice('sync-fail,please retry') + } } async getNowFileMarkdownContent(app: App) { const nowFile = app.workspace.getActiveFile(); - const { allowTags } = this.settings; + const {allowTags} = this.settings; let tags = [] try { - if(allowTags) { + if (allowTags) { tags = app.metadataCache.getFileCache(nowFile).frontmatter.tags; } } catch (error) { new Notice(langConfig["set-tags-fail"]); } + if (nowFile) { - const markDownData = await nowFile.vault.read(nowFile); + let markDownData = await nowFile.vault.read(nowFile); + const upload = new Upload2Notion(this); + let chunks = upload.splitLongString(markDownData) return { markDownData, + chunks, nowFile, tags }; @@ -141,50 +169,68 @@ class SampleSettingTab extends PluginSettingTab { } display(): void { - const { containerEl } = this; + const {containerEl} = this; containerEl.empty(); - containerEl.createEl("h2", { - text: "Settings for obsidian to notion plugin.", + containerEl.createEl("h1", { + text: "Public Settings", }); + new Setting(containerEl) .setName("Notion API Token") .setDesc("It's a secret") - .addText((text) =>{ + .addText((text) => { let t = text - .setPlaceholder("Enter your Notion API Token") - .setValue(this.plugin.settings.notionAPI) - .onChange(async (value) => { - this.plugin.settings.notionAPI = value; - await this.plugin.saveSettings(); - }) + .setPlaceholder("Enter your Notion API Token") + .setValue(this.plugin.settings.notionAPI) + .onChange(async (value) => { + this.plugin.settings.notionAPI = value; + await this.plugin.saveSettings(); + }) // t.inputEl.type = 'password' return t }); + new Setting(containerEl) + .setName("Notion ID(optional)") + .setDesc("Your notion ID(optional),share link likes:https://username.notion.site/,your notion id is [username]") + .addText((text) => + text + .setPlaceholder("Enter notion ID(options) ") + .setValue(this.plugin.settings.notionID) + .onChange(async (value) => { + this.plugin.settings.notionID = value; + await this.plugin.saveSettings(); + }) + ); + + + containerEl.createEl("h1", { + text: "Settings for Obsidian to Notion plugin", + }); + const notionDatabaseID = new Setting(containerEl) .setName("Database ID") .setDesc("It's a secret") .addText((text) => { - let t = text - .setPlaceholder("Enter your Database ID") - .setValue(this.plugin.settings.databaseID) - .onChange(async (value) => { - this.plugin.settings.databaseID = value; - await this.plugin.saveSettings(); - }) - // t.inputEl.type = 'password' - return t - } - + let t = text + .setPlaceholder("Enter your Database ID") + .setValue(this.plugin.settings.databaseID) + .onChange(async (value) => { + this.plugin.settings.databaseID = value; + await this.plugin.saveSettings(); + }) + // t.inputEl.type = 'password' + return t + } ); - // notionDatabaseID.controlEl.querySelector('input').type='password' + // notionDatabaseID.controlEl.querySelector('input').type='password' - new Setting(containerEl) + new Setting(containerEl) .setName("Banner url(optional)") .setDesc("page banner url(optional), default is empty, if you want to show a banner, please enter the url(like:https://raw.githubusercontent.com/EasyChris/obsidian-to-notion/ae7a9ac6cf427f3ca338a409ce6967ced9506f12/doc/2.png)") .addText((text) => @@ -198,21 +244,7 @@ class SampleSettingTab extends PluginSettingTab { ); - new Setting(containerEl) - .setName("Notion ID(optional)") - .setDesc("Your notion ID(optional),share link likes:https://username.notion.site/,your notion id is [username]") - .addText((text) => - text - .setPlaceholder("Enter notion ID(options) ") - .setValue(this.plugin.settings.notionID) - .onChange(async (value) => { - this.plugin.settings.notionID = value; - await this.plugin.saveSettings(); - }) - ); - - - new Setting(containerEl) + new Setting(containerEl) .setName("Convert tags(optional)") .setDesc("Transfer the Obsidian tags to the Notion table. It requires the column with the name 'Tags'") .addToggle((toggle) => @@ -224,5 +256,27 @@ class SampleSettingTab extends PluginSettingTab { }) ); + new Setting(containerEl) + .setName("Insert Notion Link to YAML") + .setDesc("When complete share,it will add NotionLink to YAML") + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.allowNotionLink) + .onChange(async (value) => { + this.plugin.settings.allowNotionLink = value; + await this.plugin.saveSettings(); + })) + + new Setting(containerEl) + .setName("Auto shared folder path") + .setDesc("The file under this path,will auto shared to Notion") + .addText((text) => + text.setValue(this.plugin.settings.folderPath) + .onChange(async (value) => { + this.plugin.settings.folderPath = value; + await this.plugin.saveSettings(); + })) + + } } From 4e951d000ce9cc533e64a8bc78f454556ee28b95 Mon Sep 17 00:00:00 2001 From: TreyDong <1346650911@qq.com> Date: Tue, 9 Apr 2024 07:13:22 +0800 Subject: [PATCH 2/2] feat: add long text support --- Upload2Notion.ts | 61 +++++++++---------- main.ts | 153 +++++++++++++++++++---------------------------- 2 files changed, 89 insertions(+), 125 deletions(-) diff --git a/Upload2Notion.ts b/Upload2Notion.ts index e3de855..a151154 100644 --- a/Upload2Notion.ts +++ b/Upload2Notion.ts @@ -23,8 +23,6 @@ export class Upload2Notion { }, body: '' }) - console.log('deletePAGE') - console.log(response) return response; } @@ -36,31 +34,6 @@ export class Upload2Notion { return res } - async appendPage(pageId:string, childArr: any){ - const bodyString:any = { - children: childArr, - } - if (pageId === undefined){ - return - } - try { - const response = await requestUrl({ - url: `https://api.notion.com/v1/blocks/${pageId}/children`, - method: 'PATCH', - headers: { - 'Content-Type': 'application/json', - // 'User-Agent': 'obsidian.md', - 'Authorization': 'Bearer ' + this.app.settings.notionAPI, - 'Notion-Version': '2021-08-16', - }, - body: JSON.stringify(bodyString), - }) - return response; - } catch (error) { - new Notice(`network error ${error}`) - } - } - async createPage(title:string, allowTags:boolean, tags:string[], childArr: any) { const bodyString:any = { parent: { @@ -129,16 +102,14 @@ export class Upload2Notion { async updateYamlInfo(yamlContent: string, nowFile: TFile, res: any,app:App, settings:any) { const yamlObj:any = yamlFrontMatter.loadFront(yamlContent); - let {url, id} = res + let {url, id} = res.json // replace www to notionID - const {notionID,allowNotionLink} = settings; + const {notionID} = settings; if(notionID!=="") { // replace url str "www" to notionID url = url.replace("www.notion.so", `${notionID}.notion.site`) } - if (allowNotionLink){ - yamlObj.NotionLink = url; - } + yamlObj.link = url; try { await navigator.clipboard.writeText(url) } catch (error) { @@ -185,4 +156,30 @@ export class Upload2Notion { return chunks; } + + async appendPage(pageId:string, childArr: any){ + const bodyString:any = { + children: childArr, + } + if (pageId === undefined){ + return + } + try { + const response = await requestUrl({ + url: `https://api.notion.com/v1/blocks/${pageId}/children`, + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + // 'User-Agent': 'obsidian.md', + 'Authorization': 'Bearer ' + this.app.settings.notionAPI, + 'Notion-Version': '2021-08-16', + }, + body: JSON.stringify(bodyString), + }) + return response; + } catch (error) { + new Notice(`network error ${error}`) + } + } + } diff --git a/main.ts b/main.ts index 41a284c..cbc7bcf 100644 --- a/main.ts +++ b/main.ts @@ -9,10 +9,10 @@ import { Setting, normalizePath } from "obsidian"; -import {addIcons} from 'icon'; -import {Upload2Notion} from "Upload2Notion"; +import {addIcons} from 'icon'; +import { Upload2Notion } from "Upload2Notion"; import {NoticeMConfig} from "Message"; -import {CLIENT_RENEG_LIMIT} from "tls"; +import { CLIENT_RENEG_LIMIT } from "tls"; // Remember to rename these classes and interfaces! @@ -24,11 +24,9 @@ interface PluginSettings { notionID: string; proxy: string; allowTags: boolean; - allowNotionLink: boolean; - folderPath: string; } -const langConfig = NoticeMConfig(window.localStorage.getItem('language') || 'en') +const langConfig = NoticeMConfig( window.localStorage.getItem('language') || 'en') const DEFAULT_SETTINGS: PluginSettings = { notionAPI: "", @@ -36,14 +34,11 @@ const DEFAULT_SETTINGS: PluginSettings = { bannerUrl: "", notionID: "", proxy: "", - allowTags: false, - allowNotionLink: false, - folderPath: "", + allowTags: false }; export default class ObsidianSyncNotionPlugin extends Plugin { settings: PluginSettings; - async onload() { await this.loadSettings(); addIcons(); @@ -75,27 +70,28 @@ export default class ObsidianSyncNotionPlugin extends Plugin { } - onunload() { - } + onunload() {} + + async upload(){ + const { notionAPI, databaseID, allowTags } = this.settings; + if (notionAPI === "" || databaseID === "") { + new Notice( + "Please set up the notion API and database ID in the settings tab." + ); + return; + } + const { markDownData, nowFile, tags } =await this.getNowFileMarkdownContent(this.app); - async upload() { - const {notionAPI, databaseID, allowTags} = this.settings; - if (notionAPI === "" || databaseID === "") { - new Notice( - "Please set up the notion API and database ID in the settings tab." - ); - return; - } const upload = new Upload2Notion(this); - const {markDownData, chunks, nowFile, tags} = await this.getNowFileMarkdownContent(this.app); const frontmasster = app.metadataCache.getFileCache(nowFile)?.frontmatter const notionID = frontmasster ? frontmasster.notionID : null + let chunks = upload.splitLongString(markDownData) // 存在就先删除 if (notionID) { await upload.deletePage(notionID) } try { - if (chunks) { + if (chunks.length >0) { const {basename} = nowFile; // create page const res = await upload.syncMarkdownToNotion(basename, allowTags, tags, chunks[0], null) @@ -108,6 +104,7 @@ export default class ObsidianSyncNotionPlugin extends Plugin { break } } + // update YamlInfo await upload.updateYamlInfo(markDownData, nowFile, res, app, this.settings) new Notice(`${langConfig["sync-success"]}${basename}`) @@ -121,23 +118,19 @@ export default class ObsidianSyncNotionPlugin extends Plugin { async getNowFileMarkdownContent(app: App) { const nowFile = app.workspace.getActiveFile(); - const {allowTags} = this.settings; + const { allowTags } = this.settings; let tags = [] try { - if (allowTags) { + if(allowTags) { tags = app.metadataCache.getFileCache(nowFile).frontmatter.tags; } } catch (error) { new Notice(langConfig["set-tags-fail"]); } - if (nowFile) { - let markDownData = await nowFile.vault.read(nowFile); - const upload = new Upload2Notion(this); - let chunks = upload.splitLongString(markDownData) + const markDownData = await nowFile.vault.read(nowFile); return { markDownData, - chunks, nowFile, tags }; @@ -169,68 +162,50 @@ class SampleSettingTab extends PluginSettingTab { } display(): void { - const {containerEl} = this; + const { containerEl } = this; containerEl.empty(); - containerEl.createEl("h1", { - text: "Public Settings", + containerEl.createEl("h2", { + text: "Settings for obsidian to notion plugin.", }); - new Setting(containerEl) .setName("Notion API Token") .setDesc("It's a secret") - .addText((text) => { + .addText((text) =>{ let t = text - .setPlaceholder("Enter your Notion API Token") - .setValue(this.plugin.settings.notionAPI) - .onChange(async (value) => { - this.plugin.settings.notionAPI = value; - await this.plugin.saveSettings(); - }) + .setPlaceholder("Enter your Notion API Token") + .setValue(this.plugin.settings.notionAPI) + .onChange(async (value) => { + this.plugin.settings.notionAPI = value; + await this.plugin.saveSettings(); + }) // t.inputEl.type = 'password' return t }); - new Setting(containerEl) - .setName("Notion ID(optional)") - .setDesc("Your notion ID(optional),share link likes:https://username.notion.site/,your notion id is [username]") - .addText((text) => - text - .setPlaceholder("Enter notion ID(options) ") - .setValue(this.plugin.settings.notionID) - .onChange(async (value) => { - this.plugin.settings.notionID = value; - await this.plugin.saveSettings(); - }) - ); - - - containerEl.createEl("h1", { - text: "Settings for Obsidian to Notion plugin", - }); - const notionDatabaseID = new Setting(containerEl) .setName("Database ID") .setDesc("It's a secret") .addText((text) => { - let t = text - .setPlaceholder("Enter your Database ID") - .setValue(this.plugin.settings.databaseID) - .onChange(async (value) => { - this.plugin.settings.databaseID = value; - await this.plugin.saveSettings(); - }) - // t.inputEl.type = 'password' - return t - } + let t = text + .setPlaceholder("Enter your Database ID") + .setValue(this.plugin.settings.databaseID) + .onChange(async (value) => { + this.plugin.settings.databaseID = value; + await this.plugin.saveSettings(); + }) + // t.inputEl.type = 'password' + return t + } + ); - // notionDatabaseID.controlEl.querySelector('input').type='password' + // notionDatabaseID.controlEl.querySelector('input').type='password' - new Setting(containerEl) + new Setting(containerEl) .setName("Banner url(optional)") .setDesc("page banner url(optional), default is empty, if you want to show a banner, please enter the url(like:https://raw.githubusercontent.com/EasyChris/obsidian-to-notion/ae7a9ac6cf427f3ca338a409ce6967ced9506f12/doc/2.png)") .addText((text) => @@ -244,7 +219,21 @@ class SampleSettingTab extends PluginSettingTab { ); - new Setting(containerEl) + new Setting(containerEl) + .setName("Notion ID(optional)") + .setDesc("Your notion ID(optional),share link likes:https://username.notion.site/,your notion id is [username]") + .addText((text) => + text + .setPlaceholder("Enter notion ID(options) ") + .setValue(this.plugin.settings.notionID) + .onChange(async (value) => { + this.plugin.settings.notionID = value; + await this.plugin.saveSettings(); + }) + ); + + + new Setting(containerEl) .setName("Convert tags(optional)") .setDesc("Transfer the Obsidian tags to the Notion table. It requires the column with the name 'Tags'") .addToggle((toggle) => @@ -256,27 +245,5 @@ class SampleSettingTab extends PluginSettingTab { }) ); - new Setting(containerEl) - .setName("Insert Notion Link to YAML") - .setDesc("When complete share,it will add NotionLink to YAML") - .addToggle((toggle) => - toggle - .setValue(this.plugin.settings.allowNotionLink) - .onChange(async (value) => { - this.plugin.settings.allowNotionLink = value; - await this.plugin.saveSettings(); - })) - - new Setting(containerEl) - .setName("Auto shared folder path") - .setDesc("The file under this path,will auto shared to Notion") - .addText((text) => - text.setValue(this.plugin.settings.folderPath) - .onChange(async (value) => { - this.plugin.settings.folderPath = value; - await this.plugin.saveSettings(); - })) - - } }