Compare commits

...

20 Commits
2.3.5 ... 2.4.3

Author SHA1 Message Date
Jiaxin Peng
1344c14933 Update version to 2.4.3 2024-10-22 10:29:09 +01:00
Jiaxin Peng
c1f4ecd14b Update version to 2.4.3 2024-10-22 10:27:49 +01:00
Jiaxin Peng
5942876c92 fix: cannot parse title from front matter 2024-10-22 10:27:33 +01:00
Jiaxin Peng
3218eb9fba Update version to 2.4.2 2024-09-19 13:50:51 +01:00
Jiaxin Peng
48566eca20 Merge pull request #37 from jxpeng98/pluggin-does-not-appear-on-mac
fix: obsidian language not set in English, Chinese and Japanese
2024-09-19 13:49:08 +01:00
Jiaxin Peng
8bd8346423 fix: obsidian language not set in English, Chinese and Japanese 2024-09-19 13:47:57 +01:00
Jiaxin Peng
00411e9599 Merge pull request #35 from jxpeng98/mobile-fail-to-start
feat: separate mobile sync from desktop version
2024-09-03 12:19:23 +01:00
Jiaxin Peng
296125f6b4 chore: remove process from dependencies 2024-09-03 12:15:36 +01:00
Jiaxin Peng
1b385ccd0a solve: the fail to load of mobile caused by the node version 2024-09-01 23:26:22 +01:00
Jiaxin Peng
a90e4871bb Backup mobile requesturl after version 2.4.1 2024-09-01 22:57:03 +01:00
Jiaxin Peng
885ccba027 fix: test the desktop function for next and general 2024-08-31 23:43:19 +01:00
Jiaxin Peng
f282dde1b7 fix: test the desktop function of custom database
with platform function
2024-08-31 00:00:09 +01:00
Jiaxin Peng
bc166d8db1 Update version to 2.4.1 2024-08-28 20:31:28 +01:00
Jiaxin Peng
7612637951 Merge pull request #34 from jxpeng98/code-highlight-issue
fix code highlight issue
2024-08-28 20:30:49 +01:00
Jiaxin Peng
e8b1617916 fix item type error for iteration 2024-08-28 20:30:05 +01:00
Jiaxin Peng
fd29ed820e fix code highlight issue 2024-08-28 20:26:06 +01:00
Jiaxin Peng
1f58670906 Update version to 2.4.0 2024-08-19 11:24:23 +01:00
Jiaxin Peng
fc47d36e8d Merge pull request #32 from jxpeng98/long-notes-support
Long notes support
2024-08-18 22:39:00 +01:00
Jiaxin Peng
cd4e610c46 modify type of pageProperties 2024-08-18 18:42:59 +01:00
Jiaxin Peng
b05c248c44 feat: support long notes syncing 2024-08-18 17:16:08 +01:00
16 changed files with 816 additions and 413 deletions

5
.gitignore vendored
View File

@@ -20,4 +20,7 @@ data.json
# Exclude macOS Finder (System Explorer) View States # Exclude macOS Finder (System Explorer) View States
.DS_Store .DS_Store
.history .history
# local data
local-data

View File

@@ -1,4 +1,4 @@
## Improvements ## Fix
- update the Markdown parser to ensure that no extra `\\` is added to the end of the line when having equations in the Markdown file - Fix a bug that the plugin cannot read the title from the markdown front matter if using custom database.
- 输入公式的时候,不会在公式后面加上多余的`\\`符号 - 修复当使用自定义数据库时插件无法从markdown front matter中读取标题的问题。

View File

@@ -9,27 +9,28 @@
[//]: # ([中文文档](README-zh.md)) [//]: # ([中文文档](README-zh.md))
**Now, support both NotionNext and General databases with customised properties.** **Now, support All Notion databases, including NotionNext and General Notion databases, and customise the database list.**
**现在支持NotionNext和普通Notion数据库自定义数据库列表。** **现在支持所有Notion数据库包括NotionNext和一般Notion数据库以及自定义数据库列表。**
## Precautions ## Precautions
### For customised database users ### For customised database users before version 2.3.0
**⚠️⚠️⚠️: The existing customised database should be recreated if you want to update to version 2.3.0. The new version has a new database structure, and the old database structure is not compatible with the new version to build the index properly.** **⚠️⚠️⚠️: The existing customised database should be recreated if you want to update to version 2.3.0. The new version has a new database structure, and the old database structure is not compatible with the new version to build the index properly.**
### 自定义数据库用户 ### 在2.3.0版本之前的自定义数据库用户
**⚠️⚠️⚠️: 如果你想要更新到2.3.0版本,你需要重新创建自定义数据库。新版本有一个新的数据库结构,旧的数据库结构无法构建索引。** **⚠️⚠️⚠️: 如果你想要更新到2.3.0版本,你需要重新创建自定义数据库。新版本有一个新的数据库结构,旧的数据库结构无法构建索引。**
## TODO List ## TODO List
- [x] ~~Modify the Edit function for the custom properties. 改进自定义属性的编辑功能~~ - [x] Modify the Edit function for the custom properties. 改进自定义属性的编辑功能
- [x] Support sync with long notes (From `v2.4.0`). 从`v2.4.0`开始支持长笔记同步
- [x] Support custom properties for Notion General database. 支持自定义属性
- [x] Support preview for database details in plugin settings. 支持预览数据库详情
- [x] Support edit for database details in plugin settings. 支持编辑数据库详情
- [ ] Support group upload with one click 支持一键多数据库上传 - [ ] Support group upload with one click 支持一键多数据库上传
- [x] ~~Support custom properties for Notion General database. 支持自定义属性~~
- [x] ~~Support preview for database details in plugin settings. 支持预览数据库详情~~
- [x] ~~Support edit for database details in plugin settings. 支持编辑数据库详情~~
## How to use ## How to use

View File

@@ -1,7 +1,7 @@
{ {
"id": "share-to-notionnext", "id": "share-to-notionnext",
"name": "Share to NotionNext", "name": "Share to NotionNext",
"version": "2.3.5", "version": "2.4.3",
"minAppVersion": "0.0.1", "minAppVersion": "0.0.1",
"description": "Shares obsidian md file to notion with notion api for NotionNext web deploy, originally created by EasyChris/obsidian-to-notion.", "description": "Shares obsidian md file to notion with notion api for NotionNext web deploy, originally created by EasyChris/obsidian-to-notion.",
"author": "EasyChris, jxpeng98", "author": "EasyChris, jxpeng98",

View File

@@ -1,8 +1,8 @@
{ {
"name": "share-to-notionnext", "name": "share-to-notionnext",
"version": "2.3.5", "version": "2.4.3",
"type": "module", "type": "module",
"description": "Shares obsidian md file to notion with notion api for NotionNext web deploy, originally created by EasyChris/obsidian-to-notion.", "description": "Share files to any Notion database using the Notion API, originally created by EasyChris/obsidian-to-notion.",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
"dev": "node esbuild.config.mjs", "dev": "node esbuild.config.mjs",
@@ -27,7 +27,6 @@
"@jxpeng98/martian": "^1.2.7", "@jxpeng98/martian": "^1.2.7",
"https-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.2",
"node-fetch": "^3.3.2", "node-fetch": "^3.3.2",
"process": "^0.11.10",
"remark-math": "^6.0.0", "remark-math": "^6.0.0",
"yaml": "^2.3.4", "yaml": "^2.3.4",
"yaml-front-matter": "^4.1.1" "yaml-front-matter": "^4.1.1"

View File

@@ -8,10 +8,42 @@ export const I18n: {[key: string]:any} = {
ja, ja,
}; };
export const I18nConfig = (lang: string): any => { class I18nManager {
return I18n[lang]; private currentLanguage: string;
};
const storedLanguage = window.localStorage.getItem("language"); constructor() {
this.currentLanguage = this.detectLanguage();
}
export const i18nConfig = I18n[storedLanguage || window.navigator.language.split('-')[0] || "en"]; // return the language to use
private detectLanguage(): string {
const storedLanguage = window.localStorage.getItem("language");
if (storedLanguage && this.isLanguageSupported(storedLanguage)) {
console.log(`Using stored language: ${storedLanguage}`);
return storedLanguage;
}
const browserLanguage = window.navigator.language.split("-")[0];
if (this.isLanguageSupported(browserLanguage)) {
console.log(`Using browser language: ${browserLanguage}`);
return browserLanguage;
}
// Default to English if no match is found
console.log("Using default language: en");
return "en";
}
private isLanguageSupported(lang: string): boolean {
return Object.prototype.hasOwnProperty.call(I18n, lang);
}
// Get the i18n configuration for the current language
public getConfig(): any {
return I18n[this.currentLanguage];
}
}
export const i18nManager = new I18nManager();
export const i18nConfig = i18nManager.getConfig();

View File

@@ -76,4 +76,8 @@ export const en = {
AddNewProperty: "Add New Property", AddNewProperty: "Add New Property",
AddNewPropertyDesc: "Add new property match with your notion database", AddNewPropertyDesc: "Add new property match with your notion database",
CopyErrorMessage: "Auto copy failed, please copy it manually", CopyErrorMessage: "Auto copy failed, please copy it manually",
BlockUploaded: "All blocks uploaded",
ExtraBlockUploaded: "Extra blocks uploaded",
CheckConsole: "Check the console for more information \n opt+cmd+i/ctrl+shift+i",
"reach-mobile-limit": "The number of blocks exceeds the limit of 100, please use the desktop plugin",
} }

View File

@@ -68,4 +68,7 @@ export const ja = {
AddNewProperty: "新しいプロパティを追加", AddNewProperty: "新しいプロパティを追加",
AddNewPropertyDesc: "新しいプロパティを追加してください", AddNewPropertyDesc: "新しいプロパティを追加してください",
CopyErrorMessage: "自動コピーに失敗しました", CopyErrorMessage: "自動コピーに失敗しました",
BlockUploaded: "ブロックがアップロードされました",
ExtraBlockUploaded: "追加ブロックがアップロードされました",
CheckConsole: "詳細情報を確認するには、コンソールを開いてください \n opt+cmd+i/ctrl+shift+i",
}; };

View File

@@ -71,4 +71,7 @@ export const zh = {
AddNewProperty: "添加新属性", AddNewProperty: "添加新属性",
AddNewPropertyDesc: "添加一个和Notion数据库匹配的新属性", AddNewPropertyDesc: "添加一个和Notion数据库匹配的新属性",
CopyErrorMessage: "复制链接失败,请手动复制", CopyErrorMessage: "复制链接失败,请手动复制",
BlockUploaded: "所有内容已成功上传",
ExtraBlockUploaded: "额外内容已成功上传",
CheckConsole: "opt+cmd+i/ctrl+shift+i\n打开控制台查看更多信息",
} }

View File

@@ -20,7 +20,7 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
addIcons(); addIcons();
// This creates an icon in the left ribbon. // This creates an icon in the left ribbon.
const ribbonIconEl = this.addRibbonIcon( this.addRibbonIcon(
"notion-logo", "notion-logo",
i18nConfig.ribbonIcon, i18nConfig.ribbonIcon,
async (evt: MouseEvent) => { async (evt: MouseEvent) => {

View File

@@ -1,13 +1,13 @@
import { i18nConfig } from "../lang/I18n"; import {i18nConfig} from "../lang/I18n";
import { App, Notice } from "obsidian"; import {App, Notice} from "obsidian";
import { Upload2NotionNext } from "./upload_next/Upload2NotionNext"; import {Upload2NotionNext} from "./upload_next/Upload2NotionNext";
import { Upload2NotionGeneral } from "./upload_general/Upload2NotionGeneral"; import {Upload2NotionGeneral} from "./upload_general/Upload2NotionGeneral";
import { Upload2NotionCustom } from "./upoload_custom/Upload2NotionCustom"; import {Upload2NotionCustom} from "./upoload_custom/Upload2NotionCustom";
import { DatabaseDetails, PluginSettings } from "../ui/settingTabs"; import {DatabaseDetails, PluginSettings} from "../ui/settingTabs";
import ObsidianSyncNotionPlugin from "../main"; import ObsidianSyncNotionPlugin from "../main";
import { getNowFileMarkdownContentNext } from "./upload_next/getMarkdownNext"; import {getNowFileMarkdownContentNext} from "./upload_next/getMarkdownNext";
import { getNowFileMarkdownContentGeneral } from "./upload_general/getMarkdownGeneral"; import {getNowFileMarkdownContentGeneral} from "./upload_general/getMarkdownGeneral";
import { getNowFileMarkdownContentCustom } from "./upoload_custom/getMarkdownCustom"; import {getNowFileMarkdownContentCustom} from "./upoload_custom/getMarkdownCustom";
export async function uploadCommandNext( export async function uploadCommandNext(
plugin: ObsidianSyncNotionPlugin, plugin: ObsidianSyncNotionPlugin,
@@ -16,7 +16,7 @@ export async function uploadCommandNext(
app: App, app: App,
) { ) {
const { notionAPI, databaseID } = dbDetails; const {notionAPI, databaseID} = dbDetails;
// Check if the user has set up the Notion API and database ID // Check if the user has set up the Notion API and database ID
if (notionAPI === "" || databaseID === "") { if (notionAPI === "" || databaseID === "") {
@@ -42,12 +42,12 @@ export async function uploadCommandNext(
} = await getNowFileMarkdownContentNext(app, settings) } = await getNowFileMarkdownContentNext(app, settings)
if (markDownData) { if (markDownData) {
const { basename } = nowFile; const {basename} = nowFile;
const upload = new Upload2NotionNext(plugin, dbDetails); 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 res = await upload.syncMarkdownToNotionNext(basename, emoji, cover, tags, type, slug, stats, category, summary, paword, favicon, datetime, markDownData, nowFile, this.app);
const { response } = res; const {response} = res;
if (response.status === 200) { if (response.status === 200) {
new Notice(`${i18nConfig["sync-preffix"]} ${basename} ${i18nConfig["sync-success"]}`).noticeEl.style.color = "green"; new Notice(`${i18nConfig["sync-preffix"]} ${basename} ${i18nConfig["sync-success"]}`).noticeEl.style.color = "green";
} else { } else {
@@ -65,7 +65,7 @@ export async function uploadCommandGeneral(
app: App, app: App,
) { ) {
const { notionAPI, databaseID } = dbDetails; const {notionAPI, databaseID} = dbDetails;
// Check if the user has set up the Notion API and database ID // Check if the user has set up the Notion API and database ID
if (notionAPI === "" || databaseID === "") { if (notionAPI === "" || databaseID === "") {
@@ -74,15 +74,18 @@ export async function uploadCommandGeneral(
return; return;
} }
const { markDownData, nowFile, cover, tags } = await getNowFileMarkdownContentGeneral(app, settings) const {markDownData, nowFile, cover, tags} = await getNowFileMarkdownContentGeneral(app, settings)
new Notice(`Start upload ${nowFile.basename}`);
console.log(`Start upload ${nowFile.basename}`);
if (markDownData) { if (markDownData) {
const { basename } = nowFile; const {basename} = nowFile;
const upload = new Upload2NotionGeneral(plugin, dbDetails); const upload = new Upload2NotionGeneral(plugin, dbDetails);
const res = await upload.syncMarkdownToNotionGeneral(basename, cover, tags, markDownData, nowFile, this.app); const res = await upload.syncMarkdownToNotionGeneral(basename, cover, tags, markDownData, nowFile, this.app);
const { response } = res; const {response} = res;
if (response.status === 200) { if (response.status === 200) {
new Notice(`${i18nConfig["sync-preffix"]} ${basename} ${i18nConfig["sync-success"]}`).noticeEl.style.color = "green"; new Notice(`${i18nConfig["sync-preffix"]} ${basename} ${i18nConfig["sync-success"]}`).noticeEl.style.color = "green";
} else { } else {
@@ -100,7 +103,7 @@ export async function uploadCommandCustom(
app: App, app: App,
) { ) {
const { notionAPI, databaseID } = settings; const {notionAPI, databaseID} = settings;
// Check if the user has set up the Notion API and database ID // Check if the user has set up the Notion API and database ID
if (notionAPI === "" || databaseID === "") { if (notionAPI === "" || databaseID === "") {
@@ -109,15 +112,18 @@ export async function uploadCommandCustom(
return; return;
} }
const { markDownData, nowFile, cover, customValues } = await getNowFileMarkdownContentCustom(app, dbDetails) const {markDownData, nowFile, cover, customValues} = await getNowFileMarkdownContentCustom(app, dbDetails)
new Notice(`Start upload ${nowFile.basename}`);
console.log(`Start upload ${nowFile.basename}`);
if (markDownData) { if (markDownData) {
const { basename } = nowFile; const {basename} = nowFile;
const upload = new Upload2NotionCustom(plugin, dbDetails); const upload = new Upload2NotionCustom(plugin, dbDetails);
const res = await upload.syncMarkdownToNotionCustom(cover, customValues, markDownData, nowFile, this.app); const res = await upload.syncMarkdownToNotionCustom(cover, customValues, markDownData, nowFile, this.app);
const { response } = res; const {response} = res;
if (response.status === 200) { if (response.status === 200) {
new Notice(`${i18nConfig["sync-preffix"]} ${basename} ${i18nConfig["sync-success"]}`).noticeEl.style.color = "green"; new Notice(`${i18nConfig["sync-preffix"]} ${basename} ${i18nConfig["sync-success"]}`).noticeEl.style.color = "green";
} else { } else {

View File

@@ -1,13 +1,17 @@
import { App, Notice, TFile } from "obsidian"; import {App, Notice, TFile, Platform, requestUrl} from "obsidian";
import { Client } from "@notionhq/client"; import {markdownToBlocks} from "@jxpeng98/martian";
import { markdownToBlocks } from "@jxpeng98/martian";
import * as yamlFrontMatter from "yaml-front-matter"; import * as yamlFrontMatter from "yaml-front-matter";
// import * as yaml from "yaml"
import MyPlugin from "src/main"; import MyPlugin from "src/main";
import { DatabaseDetails, PluginSettings } from "../../ui/settingTabs"; import {DatabaseDetails, PluginSettings} from "../../ui/settingTabs";
import { UploadBaseGeneral } from "./BaseUpload2NotionGeneral"; import {UploadBaseGeneral} from "./BaseUpload2NotionGeneral";
import { updateYamlInfo } from "../updateYaml"; import {updateYamlInfo} from "../updateYaml";
import fetch from 'node-fetch'; import fetch from 'node-fetch';
import {i18nConfig} from "../../lang/I18n";
interface CreatePageResponse {
response: any;
data: any;
}
export class Upload2NotionGeneral extends UploadBaseGeneral { export class Upload2NotionGeneral extends UploadBaseGeneral {
settings: PluginSettings; settings: PluginSettings;
@@ -29,7 +33,7 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
) { ) {
await this.deletePage(notionID); await this.deletePage(notionID);
const { databaseID } = this.dbDetails; const {databaseID} = this.dbDetails;
const databaseCover = await this.getDataBase( const databaseCover = await this.getDataBase(
databaseID, databaseID,
@@ -47,7 +51,7 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
cover: string, cover: string,
tags: string[], tags: string[],
childArr: any, childArr: any,
) { ): Promise<CreatePageResponse> {
const { const {
databaseID, databaseID,
@@ -57,6 +61,41 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
notionAPI notionAPI
} = this.dbDetails; } = 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 = { const bodyString: any = {
parent: { parent: {
database_id: databaseID, database_id: databaseID,
@@ -76,12 +115,12 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
...(tagButton ...(tagButton
? { ? {
tags: { tags: {
multi_select: tags && true ? tags.map((tag) => ({ name: tag })) : [], multi_select: tags && true ? tags.map((tag) => ({name: tag})) : [],
}, },
} }
: {}), : {}),
}, },
children: childArr, children: firstArr,
}; };
if (cover) { if (cover) {
@@ -103,29 +142,89 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
} }
console.log(bodyString) console.log(bodyString)
console.log(Platform.isDesktopApp)
const response = await fetch("https://api.notion.com/v1/pages", { let response: any;
method: "POST", let data: any;
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + notionAPI,
"Notion-Version": "2022-06-28",
},
body: JSON.stringify(bodyString),
});
const data: any = await response.json(); if (Platform.isMobileApp) {
if (!response.ok) { if(childArrLength > 100) {
new Notice(`Error ${data.status}: ${data.code} \n Check the console for more information \n opt+cmd+i/ctrl+shift+i`, 5000); new Notice(i18nConfig["reach-mobile-limit"], 5000);
console.log(`Error message: \n ${data.message}`); } else {
} else { response = await requestUrl({
console.log(`Page created: ${data.url}`); url: `https://api.notion.com/v1/pages`,
console.log(`Page ID: ${data.id}`); method: "POST",
headers: {
"Content-Type": "application/json",
// 'User-Agent': 'obsidian.md',
Authorization:
"Bearer " + notionAPI,
"Notion-Version": "2022-06-28",
},
body: JSON.stringify(bodyString),
});
}
}
if (Platform.isDesktopApp) {
response = await fetch("https://api.notion.com/v1/pages", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + notionAPI,
"Notion-Version": "2022-06-28",
},
body: JSON.stringify(bodyString),
});
data = await response.json();
if (!response.ok) {
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 fetch(`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.ok) {
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 { return {
response, // for status code response,
data // for id and url data
} }
} }
@@ -146,11 +245,11 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
const yamlContent: any = yamlFrontMatter.loadFront(markdown); const yamlContent: any = yamlFrontMatter.loadFront(markdown);
const __content = yamlContent.__content; const __content = yamlContent.__content;
const file2Block = markdownToBlocks(__content, options); const file2Block = markdownToBlocks(__content, options);
const frontmasster = const frontMatter =
app.metadataCache.getFileCache(nowFile)?.frontmatter; app.metadataCache.getFileCache(nowFile)?.frontmatter;
const { abName } = this.dbDetails const {abName} = this.dbDetails
const notionIDKey = `NotionID-${abName}`; const notionIDKey = `NotionID-${abName}`;
const notionID = frontmasster ? frontmasster[notionIDKey] : null; const notionID = frontMatter ? frontMatter[notionIDKey] : null;
if (notionID) { if (notionID) {
@@ -167,10 +266,30 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
let {response, data} = res; let {response, data} = res;
// console.log(response) if (Platform.isDesktopApp) {
if (response && response.status === 200) {
await updateYamlInfo(
markdown,
nowFile,
data,
app,
this.plugin,
this.dbDetails
);
}
}
if (response && response.status === 200) { if (Platform.isMobileApp) {
await updateYamlInfo(markdown, nowFile, data, app, this.plugin, this.dbDetails); if (response && response.status === 200) {
await updateYamlInfo(
markdown,
nowFile,
response,
app,
this.plugin,
this.dbDetails,
);
}
} }
return res; return res;

View File

@@ -1,334 +1,447 @@
import { UploadBaseNext } from "./BaseUpload2NotionNext"; import {UploadBaseNext} from "./BaseUpload2NotionNext";
import { App, Notice, TFile } from "obsidian"; import {App, Notice, TFile, Platform, requestUrl} from "obsidian";
import { Client } from '@notionhq/client'; import {markdownToBlocks} from "@jxpeng98/martian";
import { markdownToBlocks, } from "@jxpeng98/martian";
import * as yamlFrontMatter from "yaml-front-matter"; import * as yamlFrontMatter from "yaml-front-matter";
// import * as yaml from "yaml"
import MyPlugin from "src/main"; import MyPlugin from "src/main";
import { DatabaseDetails, PluginSettings } from "../../ui/settingTabs"; import {DatabaseDetails, PluginSettings} from "../../ui/settingTabs";
import { updateYamlInfo } from "../updateYaml"; import {updateYamlInfo} from "../updateYaml";
import { LIMITS, paragraph } from "@jxpeng98/martian/src/notion"; import {LIMITS, paragraph} from "@jxpeng98/martian/src/notion";
import fetch from 'node-fetch'; import fetch from 'node-fetch';
import {i18nConfig} from "../../lang/I18n";
interface CreatePageResponse {
response: any;
data: any;
}
export class Upload2NotionNext extends UploadBaseNext { export class Upload2NotionNext extends UploadBaseNext {
settings: PluginSettings; settings: PluginSettings;
dbDetails: DatabaseDetails dbDetails: DatabaseDetails;
constructor(plugin: MyPlugin, dbDetails: DatabaseDetails) { constructor(plugin: MyPlugin, dbDetails: DatabaseDetails) {
super(plugin, dbDetails); super(plugin, dbDetails);
this.dbDetails = dbDetails this.dbDetails = dbDetails;
} }
// 因为需要解析notion的block进行对比非常的麻烦 // 因为需要解析notion的block进行对比非常的麻烦
// 暂时就直接删除新建一个page // 暂时就直接删除新建一个page
async updatePage( async updatePage(
notionID: string, notionID: string,
title: string, title: string,
emoji: string, emoji: string,
cover: string, cover: string,
tags: string[], tags: string[],
type: string, type: string,
slug: string, slug: string,
stats: string, stats: string,
category: string, category: string,
summary: string, summary: string,
paword: string, paword: string,
favicon: string, favicon: string,
datetime: string, datetime: string,
childArr: any childArr: any,
) { ) {
await this.deletePage(notionID) await this.deletePage(notionID);
const { databaseID } = this.dbDetails const {databaseID} = this.dbDetails;
const databaseCover = await this.getDataBase(databaseID) const databaseCover = await this.getDataBase(databaseID);
if (cover == null) { if (cover == null) {
cover = databaseCover cover = databaseCover;
} }
return await this.createPage( return await this.createPage(
title, title,
emoji, emoji,
cover, cover,
tags, tags,
type, type,
slug, slug,
stats, stats,
category, category,
summary, summary,
paword, paword,
favicon, favicon,
datetime, datetime,
childArr) childArr,
} );
}
async createPage( async createPage(
title: string, title: string,
emoji: string, emoji: string,
cover: string, cover: string,
tags: string[], tags: string[],
type: string, type: string,
slug: string, slug: string,
stats: string, stats: string,
category: string, category: string,
summary: string, summary: string,
pawrod: string, pawrod: string,
favicon: string, favicon: string,
datetime: string, datetime: string,
childArr: any childArr: any,
) { ): Promise<CreatePageResponse> {
const {databaseID, notionAPI} = this.dbDetails;
const { // remove the annotations from the childArr if type is code block
databaseID, childArr.forEach((block: any) => {
notionAPI if (block.type === "code") {
} = this.dbDetails 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;
const bodyString: any = { console.log(`Page includes ${childArrLength} blocks`)
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: { if (childArrLength > 100) {
rich_text: [ for (let i = 0; i < childArr.length; i += 100) {
{ if (i == 0) {
text: { firstArr = childArr.slice(0, 100);
content: pawrod || '' } else {
} const chunk = childArr.slice(i, i + 100);
} extraArr.push(chunk);
] pushCount++;
}, }
icon: { }
rich_text: [
{
text: {
content: favicon || ''
}
}
]
},
date: {
date: {
start: datetime || new Date().toISOString()
}
}
},
children: childArr,
}
// add tags
if (tags) {
bodyString.properties.tags = {
multi_select: tags.map(tag => {
return { "name": tag }
})
}
}
// add title icon
if (emoji) {
bodyString.icon = {
emoji: emoji
}
}
// add slug
if (slug) {
bodyString.properties.slug = {
rich_text: [
{
text: {
content: slug
}
}
]
}
}
// check if summary is available
if (summary) {
bodyString.properties.summary = {
rich_text: [
{
text: {
content: summary
}
}
]
}
}
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)
const response = await fetch("https://api.notion.com/v1/pages", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + notionAPI,
"Notion-Version": "2022-06-28",
},
body: JSON.stringify(bodyString),
});
const data: any = await response.json();
// can use response.ok or response.status === 200
if (!response.ok) {
new Notice(`Error ${data.status}: ${data.code} \n Check the console for more information \n opt+cmd+i/ctrl+shift+i`, 5000);
console.log(`Error message: \n ${data.message}`);
} else { } else {
console.log(`Page created: ${data.url}`); firstArr = childArr;
console.log(`Page ID: ${data.id}`); }
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;
if (Platform.isMobileApp) {
if(childArrLength > 100) {
new Notice(i18nConfig["reach-mobile-limit"], 5000);
} else {
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),
});
}
}
if (Platform.isDesktopApp) {
response = await fetch("https://api.notion.com/v1/pages", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + notionAPI,
"Notion-Version": "2022-06-28",
},
body: JSON.stringify(bodyString),
});
data = await response.json();
if (!response.ok) {
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 fetch(`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.ok) {
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 { return {
response, // for status code response,
data // for id and url 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<any> {
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,
);
} }
}
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<any> {
const options = {
notionLimits: {
truncate: false,
}
}
let res: any
const yamlContent: any = yamlFrontMatter.loadFront(markdown);
const __content = yamlContent.__content
const file2Block = markdownToBlocks(__content, options);
const frontmasster = app.metadataCache.getFileCache(nowFile)?.frontmatter
const { abName } = this.dbDetails
const notionIDKey = `NotionID-${abName}`;
const notionID = frontmasster ? frontmasster[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; let {response, data} = res;
if (response && response.status === 200) { if (Platform.isDesktopApp) {
await updateYamlInfo(markdown, nowFile, data, app, this.plugin, this.dbDetails); if (response && response.status === 200) {
await updateYamlInfo(
markdown,
nowFile,
data,
app,
this.plugin,
this.dbDetails
);
}
}
if (Platform.isMobileApp) {
if (response && response.status === 200) {
await updateYamlInfo(
markdown,
nowFile,
response,
app,
this.plugin,
this.dbDetails,
);
}
} }
return res; return res;
} }
} }

View File

@@ -1,14 +1,19 @@
import {App, Notice, TFile} from "obsidian"; import {App, Notice, Platform, TFile, requestUrl} from "obsidian";
import {markdownToBlocks} from "@jxpeng98/martian"; import {markdownToBlocks} from "@jxpeng98/martian";
import * as yamlFrontMatter from "yaml-front-matter"; import * as yamlFrontMatter from "yaml-front-matter";
// import * as yaml from "yaml"
import MyPlugin from "src/main"; import MyPlugin from "src/main";
import {DatabaseDetails, PluginSettings} from "../../ui/settingTabs"; import {DatabaseDetails, PluginSettings} from "../../ui/settingTabs";
import {updateYamlInfo} from "../updateYaml"; import {updateYamlInfo} from "../updateYaml";
import {UploadBaseCustom} from "./BaseUpload2NotionCustom"; import {UploadBaseCustom} from "./BaseUpload2NotionCustom";
import fetch from 'node-fetch'; import fetch from 'node-fetch';
import {i18nConfig} from "../../lang/I18n";
interface CreatePageResponse {
response: any;
data: any;
}
export class Upload2NotionCustom extends UploadBaseCustom { export class Upload2NotionCustom extends UploadBaseCustom {
settings: PluginSettings; settings: PluginSettings;
dbDetails: DatabaseDetails; dbDetails: DatabaseDetails;
@@ -45,7 +50,7 @@ export class Upload2NotionCustom extends UploadBaseCustom {
cover: string, cover: string,
customValues: Record<string, string>, customValues: Record<string, string>,
childArr: any, childArr: any,
) { ): Promise<CreatePageResponse> {
const { const {
databaseID, databaseID,
@@ -53,7 +58,42 @@ export class Upload2NotionCustom extends UploadBaseCustom {
notionAPI notionAPI
} = this.dbDetails; } = this.dbDetails;
const bodyString: any = this.buildBodyString(customProperties, customValues, childArr); // 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) { if (cover) {
bodyString.cover = { bodyString.cover = {
@@ -74,24 +114,84 @@ export class Upload2NotionCustom extends UploadBaseCustom {
} }
console.log(bodyString) console.log(bodyString)
console.log(Platform.isDesktopApp)
const response = await fetch("https://api.notion.com/v1/pages", { let response: any;
method: "POST", let data: any;
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + notionAPI,
"Notion-Version": "2022-06-28",
},
body: JSON.stringify(bodyString),
});
const data: any = await response.json(); if (Platform.isMobileApp) {
if (!response.ok) { if(childArrLength > 100) {
new Notice(`Error ${data.status}: ${data.code} \n Check the console for more information \n opt+cmd+i/ctrl+shift+i`, 5000); new Notice(i18nConfig["reach-mobile-limit"], 5000);
console.log(`Error message: \n ${data.message}`); } else {
} else { response = await requestUrl({
console.log(`Page created: ${data.url}`); url: `https://api.notion.com/v1/pages`,
console.log(`Page ID: ${data.id}`); method: "POST",
headers: {
"Content-Type": "application/json",
// 'User-Agent': 'obsidian.md',
Authorization:
"Bearer " + notionAPI,
"Notion-Version": "2022-06-28",
},
body: JSON.stringify(bodyString),
});
}
}
if (Platform.isDesktopApp) {
response = await fetch("https://api.notion.com/v1/pages", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + notionAPI,
"Notion-Version": "2022-06-28",
},
body: JSON.stringify(bodyString),
});
data = await response.json();
if (!response.ok) {
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 fetch(`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.ok) {
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 { return {
@@ -117,11 +217,11 @@ export class Upload2NotionCustom extends UploadBaseCustom {
const yamlContent: any = yamlFrontMatter.loadFront(markdown); const yamlContent: any = yamlFrontMatter.loadFront(markdown);
const __content = yamlContent.__content; const __content = yamlContent.__content;
const file2Block = markdownToBlocks(__content, options); const file2Block = markdownToBlocks(__content, options);
const frontmasster = const frontMatter =
app.metadataCache.getFileCache(nowFile)?.frontmatter; app.metadataCache.getFileCache(nowFile)?.frontmatter;
const {abName} = this.dbDetails const {abName} = this.dbDetails
const notionIDKey = `NotionID-${abName}`; const notionIDKey = `NotionID-${abName}`;
const notionID = frontmasster ? frontmasster[notionIDKey] : null; const notionID = frontMatter ? frontMatter[notionIDKey] : null;
if (notionID) { if (notionID) {
res = await this.updatePage( res = await this.updatePage(
@@ -138,8 +238,30 @@ export class Upload2NotionCustom extends UploadBaseCustom {
// console.log(response) // console.log(response)
if (response && response.status === 200) { if (Platform.isDesktopApp) {
await updateYamlInfo(markdown, nowFile, data, app, this.plugin, this.dbDetails); if (response && response.status === 200) {
await updateYamlInfo(
markdown,
nowFile,
data,
app,
this.plugin,
this.dbDetails
);
}
}
if (Platform.isMobileApp) {
if (response && response.status === 200) {
await updateYamlInfo(
markdown,
nowFile,
response,
app,
this.plugin,
this.dbDetails,
);
}
} }
return res; return res;

View File

@@ -35,7 +35,10 @@ export async function getNowFileMarkdownContentCustom(
// If a 'title' type property exists, use the file's basename as its value // If a 'title' type property exists, use the file's basename as its value
if (titleProperty) { if (titleProperty) {
customValues[titleProperty.customName] = nowFile.basename; // Use 'basename' for the file name without extension customValues[titleProperty.customName] =
(FileCache.frontmatter && FileCache.frontmatter[titleProperty.customName]) ?
FileCache.frontmatter[titleProperty.customName] : // use the front matter value if it exists
nowFile.basename; // Use 'basename' for the file name without extension
} }
} catch (error) { } catch (error) {

View File

@@ -1245,11 +1245,6 @@ picomatch@^2.3.1:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
process@^0.11.10:
version "0.11.10"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
queue-microtask@^1.2.2: queue-microtask@^1.2.2:
version "1.2.3" version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"