Compare commits

...

9 Commits
0.0.9 ... 0.1.6

Author SHA1 Message Date
Jiaxin Peng
8f116956ce Update version to 0.1.6 2023-09-28 12:00:40 +01:00
Jiaxin Peng
df138d9304 Update version to 0.1.5 2023-08-29 15:18:41 +01:00
Jiaxin Peng
49b8d470fb Update version to 0.1.4 2023-08-29 14:48:50 +01:00
Jiaxin Peng
c39323c3fa Update version to 0.1.3 2023-08-29 14:29:42 +01:00
Jiaxin Peng
05dff7a389 Update version to 0.1.2 2023-08-29 14:28:22 +01:00
Jiaxin Peng
24fcadbc8b modify the action 2023-08-29 14:27:10 +01:00
Jiaxin Peng
fdb81f76a7 Update version to 0.1.1 2023-08-29 14:21:12 +01:00
Jiaxin Peng
024b7f726e Update version to 0.1.0 2023-08-29 13:39:52 +01:00
Jiaxin Peng
722ca7bb56 update packages and modify upload2notion 2023-08-29 13:39:36 +01:00
15 changed files with 757 additions and 762 deletions

View File

@@ -1,26 +0,0 @@
name: CI
on:
push:
branches:
- "*"
env:
PLUGIN_NAME: share-to-notionnext
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: 16
- name: Build
id: build
run: |
npm install
npm run build

View File

@@ -13,11 +13,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Use Node.js - name: Use Node.js
uses: actions/setup-node@v1 uses: actions/setup-node@v3
with: with:
node-version: "14.x" node-version: "16.x"
- name: Build - name: Build
id: build id: build
@@ -25,10 +25,10 @@ jobs:
npm install npm install
npm run build npm run build
mkdir ${{ env.PLUGIN_NAME }} mkdir ${{ env.PLUGIN_NAME }}
cp main.js manifest.json styles.css ${{ env.PLUGIN_NAME }} cp main.js manifest.json ${{ env.PLUGIN_NAME }}
zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }} zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }}
ls ls
echo "::set-output name=tag_name::$(git tag --sort version:refname | tail -n 1)" echo "tag_name=$(git tag --sort version:refname | tail -n 1)" >> $GITHUB_OUTPUT
- name: Create Release - name: Create Release
id: create_release id: create_release

View File

@@ -77,11 +77,10 @@ Contents Below
``` ```
模板使用如下: 模板使用如下:
![](https://img.jxpeng.dev/2023/08/5ba5e7aeb86650c060c620786371717c.mp4) ![](https://minioapi.pjx.ac.cn/img1/2023/09/354b950e5777b48832c2475e4a31f2cc.gif)
**插件预览如下** **插件预览如下**
![](https://img.jxpeng.dev/2023/08/7edefe36899b7431a65db891f429a137.mp4) ![](https://minioapi.pjx.ac.cn/img1/2023/09/d6199619b68fab218fca8ae0cebece78.gif)
--- ---

View File

@@ -28,7 +28,7 @@ Before installing the plugin, you must have set up the following:
- password - password
- icon - icon
In version 0.0.6, I also add the following contents: From version 0.0.6, I also add the following contents:
- titleicon: the unique icon for each post, it can only support emoji currently. - titleicon: the unique icon for each post, it can only support emoji currently.
- cover url: the cover image for each post, it should be end with a type of image, such as .jpg, .png, .gif, etc. - cover url: the cover image for each post, it should be end with a type of image, such as .jpg, .png, .gif, etc.
@@ -76,10 +76,10 @@ Contents Below
``` ```
Usage of the template is as follows: Usage of the template is as follows:
![](https://img.jxpeng.dev/2023/08/189384bf03c562030f4828343e1b7a6c.gif) ![](https://minioapi.pjx.ac.cn/img1/2023/09/354b950e5777b48832c2475e4a31f2cc.gif)
**Plugin preview is shown below** **Plugin preview is shown below**
![](https://img.jxpeng.dev/2023/08/6063dd06eea4ee6939b5058dd129a698.gif) ![](https://minioapi.pjx.ac.cn/img1/2023/09/d6199619b68fab218fca8ae0cebece78.gif)
--- ---

View File

@@ -1,230 +0,0 @@
import {App, Notice, requestUrl, TFile} from "obsidian";
import {Client} from "@notionhq/client";
import {markdownToBlocks,} from "@tryfabric/martian";
import * as yamlFrontMatter from "yaml-front-matter";
import * as yaml from "yaml"
import MyPlugin from "main";
export class Upload2Notion {
app: MyPlugin;
notion: Client;
agent: any;
constructor(app: MyPlugin) {
this.app = app;
}
async deletePage(notionID:string){
return await requestUrl({
url: `https://api.notion.com/v1/blocks/${notionID}`,
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.app.settings.notionAPI,
'Notion-Version': '2022-06-28',
},
body: ''
})
}
async getDataBase(databaseID:string){
const response = await requestUrl({
url: `https://api.notion.com/v1/databases/${databaseID}`,
method: 'GET',
headers: {
'Authorization': 'Bearer ' + this.app.settings.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
}
}
// 因为需要解析notion的block进行对比非常的麻烦
// 暂时就直接删除新建一个page
async updatePage(notionID:string, title:string, allowTags:boolean, 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 databasecover = await this.getDataBase(this.app.settings.databaseID)
if (cover == null) {
cover = databasecover
}
return await this.createPage(title, allowTags, emoji, cover, tags, type, slug, stats, category, summary, paword, favicon, datetime, childArr)
}
async createPage(title:string, allowTags:boolean, emoji:string, cover:string, tags:string[], type:string, slug:string, stats:string, category:string, summary:string, pawrod:string, favicon:string, datetime:string, childArr: any) {
const bodyString:any = {
parent: {
database_id: this.app.settings.databaseID
},
icon: {
emoji: emoji || '📜'
},
properties: {
title: {
title: [
{
text: {
content: title
},
},
],
},
tags: {
multi_select: allowTags && tags !== undefined ? tags.map(tag => {
return {"name": tag}
}) : [],
},
type: {
select: {
name: type || 'Post'
}
},
slug: {
rich_text: [
{
text: {
content: slug || ''
}
}
]
},
status: {
select: {
name: stats || 'Draft'
}
},
category: {
select: {
name: category || 'Obsidian'
}
},
summary: {
rich_text: [
{
text: {
content: summary || ''
}
}
]
},
password: {
rich_text: [
{
text: {
content: pawrod || ''
}
}
]
},
icon: {
rich_text: [
{
text: {
content: favicon || ''
}
}
]
},
date: {
date: {
start: datetime || new Date().toISOString()
}
}
},
children: childArr,
}
if (cover) {
bodyString.cover = {
type: "external",
external: {
url: cover
}
}
}
if (!bodyString.cover && this.app.settings.bannerUrl) {
bodyString.cover = {
type: "external",
external: {
url: this.app.settings.bannerUrl
}
}
}
try {
return await requestUrl({
url: `https://api.notion.com/v1/pages`,
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'User-Agent': 'obsidian.md',
'Authorization': 'Bearer ' + this.app.settings.notionAPI,
'Notion-Version': '2022-06-28',
},
body: JSON.stringify(bodyString),
})
} catch (error) {
new Notice(`network error ${error}`)
}
}
async syncMarkdownToNotion(title:string, allowTags:boolean, 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, settings:any): Promise<any> {
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, emoji, cover, tags, type, slug, stats, category, summary, paword, favicon, datetime, file2Block);
} else {
res = await this.createPage(title, allowTags, emoji, cover, tags, type, slug, stats, category, summary, paword, favicon, datetime, file2Block);
}
if (res.status === 200) {
await this.updateYamlInfo(markdown, nowFile, res, app, settings)
} else {
new Notice(`${res.text}`)
}
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
// replace www to notionID
const {notionID} = settings;
if(notionID!=="") {
// replace url str "www" to notionID
url = url.replace("www.notion.so", `${notionID}.notion.site`)
}
yamlObj.link = url;
try {
await navigator.clipboard.writeText(url)
} catch (error) {
new Notice(`复制链接失败,请手动复制${error}`)
}
yamlObj.notionID = id;
const __content = yamlObj.__content;
delete yamlObj.__content
const yamlhead = yaml.stringify(yamlObj)
// 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}`)
}
}
}

View File

@@ -8,14 +8,13 @@ THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin if you want to view the source, please visit the github repository of this plugin
*/ */
`; `;
const prod = (process.argv[2] === 'production'); const prod = (process.argv[2] === 'production');
esbuild.build({ const ctx = await esbuild.context({
banner: { banner: {
js: banner, js: banner,
}, },
entryPoints: ['main.ts'], entryPoints: ['src/main.ts'],
bundle: true, bundle: true,
external: [ external: [
'obsidian', 'obsidian',
@@ -41,12 +40,19 @@ esbuild.build({
'@codemirror/text', '@codemirror/text',
'@codemirror/tooltip', '@codemirror/tooltip',
'@codemirror/view', '@codemirror/view',
...builtins], ...builtins
],
format: 'cjs', format: 'cjs',
watch: !prod,
target: 'es2016', target: 'es2016',
logLevel: "info", logLevel: "info",
sourcemap: prod ? false : 'inline', sourcemap: prod ? false : 'inline',
treeShaking: true, treeShaking: true,
outfile: 'main.js', outfile: 'main.js',
}).catch(() => process.exit(1)); });
if (!prod) {
await ctx.watch();
} else {
await ctx.rebuild()
ctx.dispose().then(r => console.log('Build completed'), e => console.error(e));
}

View File

@@ -1,10 +1,10 @@
{ {
"id": "share-to-notionnext", "id": "share-to-notionnext",
"name": "Share to NotionNext", "name": "Share to NotionNext",
"version": "0.0.9", "version": "0.1.6",
"minAppVersion": "0.0.1", "minAppVersion": "0.0.1",
"description": "This is a plugin for Obsidian. This plugin shares obsidian md file to notion with notion api for NotionNext web deploy.", "description": "Shares obsidian md file to notion with notion api for NotionNext web deploy, originally created by EasyChris/obsidian-to-notion.",
"author": "jxpeng98", "author": "EasyChris, jxpeng98",
"authorUrl": "https://github.com/jxpeng98/obsidian-to-NotionNext", "authorUrl": "https://github.com/jxpeng98",
"isDesktopOnly": false "isDesktopOnly": false
} }

View File

@@ -1,8 +1,8 @@
{ {
"name": "share-to-notionnext", "name": "share-to-notionnext",
"version": "0.0.9", "version": "0.1.6",
"type": "module", "type": "module",
"description": "This is a plugin for Obsidian. This plugin share obsidian md file to notion with notion api for NotionNext web deploy.", "description": "Shares obsidian md file to notion with notion api for NotionNext web deploy, 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",
@@ -13,19 +13,20 @@
"author": "", "author": "",
"license": "GNU GPLv3", "license": "GNU GPLv3",
"devDependencies": { "devDependencies": {
"@types/node": "^17.0.35", "@types/node": "^20.5.7",
"@types/yaml-front-matter": "^4.1.0", "@types/yaml-front-matter": "^4.1.0",
"@typescript-eslint/eslint-plugin": "^5.2.0", "@typescript-eslint/eslint-plugin": "^6.5.0",
"@typescript-eslint/parser": "^5.2.0", "@typescript-eslint/parser": "^6.5.0",
"builtin-modules": "^3.2.0", "builtin-modules": "^3.2.0",
"esbuild": "0.13.12", "esbuild": "0.19.2",
"obsidian": "latest", "obsidian": "latest",
"tslib": "2.3.1", "tslib": "2.6.2",
"typescript": "4.4.4" "typescript": "5.2.2"
}, },
"dependencies": { "dependencies": {
"@tryfabric/martian": "^1.2.0", "@tryfabric/martian": "^1.2.0",
"https-proxy-agent": "^5.0.1", "https-proxy-agent": "^7.0.1",
"process": "^0.11.10",
"yaml": "^2.2.2", "yaml": "^2.2.2",
"yaml-front-matter": "^4.1.1" "yaml-front-matter": "^4.1.1"
} }

239
src/Upload2Notion.ts Normal file
View File

@@ -0,0 +1,239 @@
import { App, Notice, requestUrl, TFile } from "obsidian";
import { Client } from '@notionhq/client';
import { markdownToBlocks, } from "@tryfabric/martian";
import * as yamlFrontMatter from "yaml-front-matter";
// import * as yaml from "yaml"
import MyPlugin from "src/main";
export class Upload2Notion {
plugin: MyPlugin;
notion: Client;
agent: any;
constructor(plugin: MyPlugin) {
this.plugin = plugin;
}
async deletePage(notionID: string) {
return requestUrl({
url: `https://api.notion.com/v1/blocks/${notionID}`,
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.plugin.settings.notionAPI,
'Notion-Version': '2022-06-28',
},
body: ''
});
}
async getDataBase(databaseID: string) {
const response = await requestUrl({
url: `https://api.notion.com/v1/databases/${databaseID}`,
method: 'GET',
headers: {
'Authorization': 'Bearer ' + this.plugin.settings.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
}
}
// 因为需要解析notion的block进行对比非常的麻烦
// 暂时就直接删除新建一个page
async updatePage(notionID: string, title: string, allowTags: boolean, 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 databasecover = await this.getDataBase(this.plugin.settings.databaseID)
if (cover == null) {
cover = databasecover
}
return await this.createPage(title, allowTags, emoji, cover, tags, type, slug, stats, category, summary, paword, favicon, datetime, childArr)
}
async createPage(title: string, allowTags: boolean, emoji: string, cover: string, tags: string[], type: string, slug: string, stats: string, category: string, summary: string, pawrod: string, favicon: string, datetime: string, childArr: any) {
const bodyString: any = {
parent: {
database_id: this.plugin.settings.databaseID
},
icon: {
emoji: emoji || '📜'
},
properties: {
title: {
title: [
{
text: {
content: title
},
},
],
},
tags: {
multi_select: allowTags && tags !== undefined ? tags.map(tag => {
return { "name": tag }
}) : [],
},
type: {
select: {
name: type || 'Post'
}
},
slug: {
rich_text: [
{
text: {
content: slug || ''
}
}
]
},
status: {
select: {
name: stats || 'Draft'
}
},
category: {
select: {
name: category || 'Obsidian'
}
},
summary: {
rich_text: [
{
text: {
content: summary || ''
}
}
]
},
password: {
rich_text: [
{
text: {
content: pawrod || ''
}
}
]
},
icon: {
rich_text: [
{
text: {
content: favicon || ''
}
}
]
},
date: {
date: {
start: datetime || new Date().toISOString()
}
}
},
children: childArr,
}
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
}
}
}
try {
return await requestUrl({
url: `https://api.notion.com/v1/pages`,
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'User-Agent': 'obsidian.md',
'Authorization': 'Bearer ' + this.plugin.settings.notionAPI,
'Notion-Version': '2022-06-28',
},
body: JSON.stringify(bodyString),
})
} catch (error) {
new Notice(`network error ${error}`)
}
}
async syncMarkdownToNotion(title: string, allowTags: boolean, 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, settings: any): Promise<any> {
let res: any
const yamlContent: any = yamlFrontMatter.loadFront(markdown);
const __content = yamlContent.__content
const file2Block = markdownToBlocks(__content);
const frontmasster = app.metadataCache.getFileCache(nowFile)?.frontmatter
const notionID = frontmasster ? frontmasster.notionID : null
if (notionID) {
res = await this.updatePage(notionID, title, allowTags, emoji, cover, tags, type, slug, stats, category, summary, paword, favicon, datetime, file2Block);
} else {
res = await this.createPage(title, allowTags, emoji, cover, tags, type, slug, stats, category, summary, paword, favicon, datetime, file2Block);
}
if (res.status === 200) {
await this.updateYamlInfo(markdown, nowFile, res, app, settings)
} else {
new Notice(`${res.text}`)
}
return res
}
async updateYamlInfo(yamlContent: string, nowFile: TFile, res: any, app: App, settings: any) {
let { url, id } = res.json
// replace www to notionID
const { notionID } = settings;
if (notionID !== "") {
// replace url str "www" to notionID
url = url.replace("www.notion.so", `${notionID}.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}`)
// }
}
}

View File

@@ -1,7 +1,7 @@
import {App, Editor, MarkdownView, Notice, Plugin, PluginSettingTab, Setting} from "obsidian"; import {App, Editor, MarkdownView, Notice, Plugin, PluginSettingTab, Setting} from "obsidian";
import {addIcons} from 'icon'; import {addIcons} from 'src/icon';
import {Upload2Notion} from "Upload2Notion"; import {Upload2Notion} from "src/Upload2Notion";
import {NoticeMConfig} from "Message"; import {NoticeMConfig} from "src/Message";
// Remember to rename these classes and interfaces! // Remember to rename these classes and interfaces!
@@ -55,7 +55,7 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
// This adds a settings tab so the user can configure various aspects of the plugin // This adds a settings tab so the user can configure various aspects of the plugin
this.addSettingTab(new SampleSettingTab(this.app, this)); this.addSettingTab(new ObsidianSettingTab(this.app, this));
} }
@@ -152,7 +152,7 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
} }
} }
class SampleSettingTab extends PluginSettingTab { class ObsidianSettingTab extends PluginSettingTab {
plugin: ObsidianSyncNotionPlugin; plugin: ObsidianSyncNotionPlugin;
constructor(app: App, plugin: ObsidianSyncNotionPlugin) { constructor(app: App, plugin: ObsidianSyncNotionPlugin) {
@@ -165,15 +165,11 @@ class SampleSettingTab extends PluginSettingTab {
containerEl.empty(); containerEl.empty();
containerEl.createEl("h2", {
text: "Settings for obsidian to NotionNext plugin.",
});
new Setting(containerEl) new Setting(containerEl)
.setName("Notion API Token") .setName("Notion API Token")
.setDesc("It's a secret") .setDesc("It's a secret")
.addText((text) =>{ .addText((text) =>{
// t.inputEl.type = 'password' text.inputEl.type = 'password';
return text return text
.setPlaceholder("Enter your Notion API Token") .setPlaceholder("Enter your Notion API Token")
.setValue(this.plugin.settings.notionAPI) .setValue(this.plugin.settings.notionAPI)
@@ -188,7 +184,7 @@ class SampleSettingTab extends PluginSettingTab {
.setName("Database ID") .setName("Database ID")
.setDesc("It's a secret") .setDesc("It's a secret")
.addText((text) => { .addText((text) => {
// t.inputEl.type = 'password' text.inputEl.type = 'password';
return text return text
.setPlaceholder("Enter your Database ID") .setPlaceholder("Enter your Database ID")
.setValue(this.plugin.settings.databaseID) .setValue(this.plugin.settings.databaseID)
@@ -197,7 +193,6 @@ class SampleSettingTab extends PluginSettingTab {
await this.plugin.saveSettings(); await this.plugin.saveSettings();
}) })
} }
); );
// notionDatabaseID.controlEl.querySelector('input').type='password' // notionDatabaseID.controlEl.querySelector('input').type='password'

View File

View File

@@ -18,5 +18,7 @@
}, },
"include": [ "include": [
"**/*.ts" "**/*.ts"
, "Upload2Notion.ts" ] ,
"src/Upload2Notion.ts"
]
} }

865
yarn.lock

File diff suppressed because it is too large Load Diff