mirror of
https://github.com/jxpeng98/obsidian-to-NotionNext
synced 2026-07-30 00:48:36 +08:00
Compare commits
253 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
800e218dee | ||
|
|
3b774552af | ||
| b8a02c49f1 | |||
|
|
046a67f22c | ||
| 426f05dfbd | |||
|
|
0addaa0d7f | ||
| 1d5792c966 | |||
|
|
193d3f6f87 | ||
| e9c2b4aada | |||
| b5ac42e6e3 | |||
| 60d002677b | |||
| 3c3ec6881e | |||
|
|
a80155fafc | ||
| 3a814b8390 | |||
| bb522db9a2 | |||
| 201f72ae0e | |||
| be6f192dd8 | |||
| 4ce1019094 | |||
| 095c608511 | |||
| 2d8d7fb11a | |||
| 36725cb699 | |||
|
|
654e7e854d | ||
|
|
fa67f08512 | ||
|
|
7e42ef49a7 | ||
|
|
5a95aa0acc | ||
|
|
ded01ce678 | ||
|
|
3535d9fc70 | ||
|
|
9c93e38aff | ||
|
|
bfde5dfe1a | ||
|
|
d8eb3c8984 | ||
|
|
1cee2b67a6 | ||
|
|
82529ce56a | ||
|
|
70f30f0713 | ||
|
|
24ebb0bf8a | ||
|
|
d7372c7c55 | ||
|
|
f7bcf71020 | ||
|
|
72146afe48 | ||
|
|
9bcdb42edb | ||
|
|
16e0827991 | ||
|
|
863a6fb0dc | ||
|
|
5799ffe79b | ||
|
|
a8ebab7fcb | ||
|
|
e0ec27ad07 | ||
|
|
85220105b0 | ||
|
|
9afdb24a82 | ||
|
|
3c7ab13995 | ||
|
|
31b72f3bd3 | ||
|
|
ee5aada1b9 | ||
|
|
9e15044f6a | ||
|
|
9d891dae55 | ||
|
|
bc665253ec | ||
|
|
125c75f7c0 | ||
|
|
8248640b5f | ||
|
|
0198898086 | ||
|
|
8c6aea8250 | ||
|
|
a179d3da52 | ||
|
|
569d8eb699 | ||
|
|
2a58dd3258 | ||
|
|
8c65e471eb | ||
|
|
388693d5b4 | ||
|
|
dda6710fe9 | ||
|
|
76ea9a4aee | ||
|
|
856f83e8a1 | ||
|
|
f670dd972a | ||
|
|
e6de2d036a | ||
|
|
dd131f5df1 | ||
|
|
a9fb81fc1b | ||
|
|
4f21e3ff91 | ||
|
|
4abbbecd37 | ||
|
|
35746f1c51 | ||
|
|
9ac64268fc | ||
|
|
3123d91e97 | ||
|
|
cf08703fea | ||
|
|
fb463ceaae | ||
|
|
306573d81e | ||
|
|
fb5f7b502d | ||
|
|
59c15f1206 | ||
|
|
370444fb94 | ||
|
|
11aaf14c0b | ||
|
|
f3fb251cf7 | ||
|
|
913fa4dd03 | ||
|
|
f638310f31 | ||
|
|
c2421f7a84 | ||
|
|
6c30d0c2b4 | ||
|
|
ccb5a20abe | ||
|
|
630e3a20c2 | ||
|
|
040b72b760 | ||
|
|
7df4b5e9c8 | ||
|
|
3f35a56b6e | ||
|
|
3c7e266a35 | ||
|
|
8201c706ea | ||
|
|
4433ea449d | ||
|
|
96126d1334 | ||
|
|
b3702a9cc1 | ||
|
|
ba9b477db7 | ||
|
|
fb6951cfdf | ||
|
|
6454c0417d | ||
|
|
35f19a9f43 | ||
|
|
8e32e8f0f5 | ||
|
|
880f256ae1 | ||
|
|
bf9c3f2933 | ||
|
|
2ac354299a | ||
|
|
8317abb667 | ||
|
|
d74634aca6 | ||
|
|
d400b92263 | ||
|
|
e7fa597e5a | ||
|
|
6ce689ba96 | ||
|
|
b978cf7e01 | ||
|
|
4e3c88f129 | ||
|
|
6550bca70f | ||
|
|
662edbb84c | ||
|
|
671936ee41 | ||
|
|
2b16ea76c6 | ||
|
|
399a02187f | ||
|
|
eee349509a | ||
|
|
57c0941fed | ||
|
|
8734af6dbf | ||
|
|
041a78fb52 | ||
|
|
afec5d8364 | ||
|
|
75926a38d4 | ||
|
|
793dd67e0a | ||
|
|
f235c80d18 | ||
|
|
f33315a4b6 | ||
|
|
b62bd4ba2e | ||
|
|
402d15a38b | ||
|
|
87f1ce0146 | ||
|
|
398b785e03 | ||
|
|
8617878582 | ||
|
|
ae9e0c14fb | ||
|
|
9be9b18efb | ||
|
|
588f57205b | ||
|
|
cfbedea91d | ||
|
|
4cd6557444 | ||
|
|
407688c303 | ||
|
|
84b998aa93 | ||
|
|
4b6f1a1f1d | ||
|
|
4a2c59d13b | ||
|
|
8f116956ce | ||
|
|
df138d9304 | ||
|
|
49b8d470fb | ||
|
|
c39323c3fa | ||
|
|
05dff7a389 | ||
|
|
24fcadbc8b | ||
|
|
fdb81f76a7 | ||
|
|
024b7f726e | ||
|
|
722ca7bb56 | ||
|
|
fb7294d607 | ||
|
|
4f06d81588 | ||
|
|
2434ddd532 | ||
|
|
55804e491a | ||
|
|
679e7ec4f1 | ||
|
|
9be679f456 | ||
|
|
a0f5d2d51b | ||
|
|
a68a3920ad | ||
|
|
76d3fc9872 | ||
|
|
e3fb50b205 | ||
|
|
5411b0bac6 | ||
|
|
f974d6dc66 | ||
|
|
de110334e6 | ||
|
|
d8c8e78625 | ||
|
|
feab049033 | ||
|
|
04d73fdccc | ||
|
|
f4ab15c0d5 | ||
|
|
0bd4427f7a | ||
|
|
221cf62fcc | ||
|
|
7a2610c4b9 | ||
|
|
f9f30df7ea | ||
|
|
1d22f2e741 | ||
|
|
935ecacd2b | ||
|
|
6724399628 | ||
|
|
8fd6f7c701 | ||
|
|
f3e3ccdeb1 | ||
|
|
c65cb6a4df | ||
|
|
7c3028dd63 | ||
|
|
d2a40694e3 | ||
|
|
9f7298a880 | ||
|
|
b8860beb85 | ||
|
|
5e8c8f6f63 | ||
|
|
1887ad8f3d | ||
|
|
517426ea7a | ||
|
|
b6d5ff0093 | ||
|
|
41ebff9067 | ||
|
|
96b83b6da0 | ||
|
|
63ded7d50e | ||
|
|
8ef9a007e3 | ||
|
|
2d57952c38 | ||
|
|
e3a74d61cc | ||
|
|
47e902a6ee | ||
|
|
098110d9ba | ||
|
|
82629c122f | ||
|
|
7be8f1c799 | ||
|
|
df58b7acb6 | ||
|
|
0fedf4e83f | ||
|
|
444ccfcd0f | ||
|
|
05a8b789d9 | ||
|
|
d845dc7945 | ||
|
|
26a326bb0d | ||
|
|
539a2d1731 | ||
|
|
3eb9b0305a | ||
|
|
a6df90dc56 | ||
|
|
46e5477509 | ||
|
|
2364a6c756 | ||
|
|
21133862ff | ||
|
|
68a0796e9b | ||
|
|
058d45e834 | ||
|
|
fdc0b3e756 | ||
|
|
27d88a9b94 | ||
|
|
089dca83bb | ||
|
|
fc0954540a | ||
|
|
f3fe8e7835 | ||
|
|
f60bc598a8 | ||
|
|
0752c5bbd9 | ||
|
|
22f12063e4 | ||
|
|
e2defbd248 | ||
|
|
0b7d5b3435 | ||
|
|
2a4e03e319 | ||
|
|
c0d9f41555 | ||
|
|
bc65eb2781 | ||
|
|
22d2f79612 | ||
|
|
c62482d83b | ||
|
|
bf512fe2a3 | ||
|
|
ae7a9ac6cf | ||
|
|
595935ffae | ||
|
|
cf3129b90b | ||
|
|
6d5ece76b6 | ||
|
|
021584dac6 | ||
|
|
e6fc5e522b | ||
|
|
e6099a94d5 | ||
|
|
04b8ba65de | ||
|
|
43a7165983 | ||
|
|
3740680bdd | ||
|
|
5ac25c3de9 | ||
|
|
8bc84b8d61 | ||
|
|
7aae1b23e9 | ||
|
|
511a3e2289 | ||
|
|
22bf322127 | ||
|
|
4497d9cd7f | ||
|
|
d4aeaaa0f9 | ||
|
|
bf6ef7af0c | ||
|
|
27c8e0f610 | ||
|
|
c9a5c72c6c | ||
|
|
7bc0b43d4f | ||
|
|
d5f908ed34 | ||
|
|
f87e024e77 | ||
|
|
e667bec7e0 | ||
|
|
845cb7bf66 | ||
|
|
7d096e1cfb | ||
|
|
f3346c455e | ||
|
|
208fb40f72 | ||
|
|
e3669e7a83 | ||
|
|
617bb370dc | ||
|
|
310cf3043c | ||
|
|
c701acbfaa |
57
.github/workflows/release.yml
vendored
57
.github/workflows/release.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Release Obsidian plugin
|
name: Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -6,18 +6,27 @@ on:
|
|||||||
- "*"
|
- "*"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PLUGIN_NAME: your-plugin-id # Change this to match the id of your plugin.
|
PLUGIN_NAME: share-to-notionnext # Change this to match the id of your plugin.
|
||||||
|
CHANGELOG_FILENAME: CHANGELOG.md
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
name: release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- 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: "18"
|
||||||
|
|
||||||
|
# - name: Generate changelog
|
||||||
|
# id: changelog
|
||||||
|
# uses: saadmk11/changelog-ci@v1.1.2
|
||||||
|
# with:
|
||||||
|
# github_token: ${{ secrets.REPO_ACCESS_TOKEN }}
|
||||||
|
# release_version: ${{ github.ref }}
|
||||||
|
# changelog_filename: CHANGELOG.md
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
id: build
|
id: build
|
||||||
@@ -25,20 +34,21 @@ 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
|
||||||
uses: actions/create-release@v1
|
uses: actions/create-release@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
|
||||||
VERSION: ${{ github.ref }}
|
VERSION: ${{ github.ref }}
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ github.ref }}
|
tag_name: ${{ github.ref }}
|
||||||
release_name: ${{ github.ref }}
|
release_name: ${{ github.ref }}
|
||||||
|
body_path: ${{ env.CHANGELOG_FILENAME }}
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
|
|
||||||
@@ -46,7 +56,7 @@ jobs:
|
|||||||
id: upload-zip
|
id: upload-zip
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: ./${{ env.PLUGIN_NAME }}.zip
|
asset_path: ./${{ env.PLUGIN_NAME }}.zip
|
||||||
@@ -57,7 +67,7 @@ jobs:
|
|||||||
id: upload-main
|
id: upload-main
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: ./main.js
|
asset_path: ./main.js
|
||||||
@@ -68,20 +78,31 @@ jobs:
|
|||||||
id: upload-manifest
|
id: upload-manifest
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: ./manifest.json
|
asset_path: ./manifest.json
|
||||||
asset_name: manifest.json
|
asset_name: manifest.json
|
||||||
asset_content_type: application/json
|
asset_content_type: application/json
|
||||||
|
|
||||||
- name: Upload styles.css
|
- name: Upload markdown template
|
||||||
id: upload-css
|
id: upload-md
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
asset_path: ./styles.css
|
asset_path: ./obsidian_template.md
|
||||||
asset_name: styles.css
|
asset_name: template.md
|
||||||
asset_content_type: text/css
|
asset_content_type: text/markdown
|
||||||
|
|
||||||
|
# - name: Upload styles.css
|
||||||
|
# id: upload-css
|
||||||
|
# uses: actions/upload-release-asset@v1
|
||||||
|
# env:
|
||||||
|
# GITHUB_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
|
||||||
|
# with:
|
||||||
|
# upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
# asset_path: ./styles.css
|
||||||
|
# asset_name: styles.css
|
||||||
|
# asset_content_type: text/css
|
||||||
|
|||||||
32
.github/workflows/test.yml
vendored
Normal file
32
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
name: Test and Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
|
||||||
|
env:
|
||||||
|
PLUGIN_NAME: share-to-notionnext # Change this to match the id of your plugin.
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Use Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: "18"
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
id: build
|
||||||
|
run: |
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
mkdir ${{ env.PLUGIN_NAME }}
|
||||||
|
cp main.js manifest.json ${{ env.PLUGIN_NAME }}
|
||||||
|
zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }}
|
||||||
|
ls
|
||||||
|
echo "tag_name=$(git tag --sort version:refname | tail -n 1)" >> $GITHUB_OUTPUT
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -10,7 +10,7 @@ node_modules
|
|||||||
|
|
||||||
# Don't include the compiled main.js file in the repo.
|
# Don't include the compiled main.js file in the repo.
|
||||||
# They should be uploaded to GitHub releases instead.
|
# They should be uploaded to GitHub releases instead.
|
||||||
# main.js
|
main.js
|
||||||
|
|
||||||
# Exclude sourcemaps
|
# Exclude sourcemaps
|
||||||
*.map
|
*.map
|
||||||
@@ -20,3 +20,4 @@ data.json
|
|||||||
|
|
||||||
# Exclude macOS Finder (System Explorer) View States
|
# Exclude macOS Finder (System Explorer) View States
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.history
|
||||||
4
CHANGELOG.md
Normal file
4
CHANGELOG.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
## Improvements
|
||||||
|
|
||||||
|
- update the Markdown parser to ensure that no extra `\\` is added to the end of the line when having equations in the Markdown file
|
||||||
|
- 输入公式的时候,不会在公式后面加上多余的`\\`符号
|
||||||
307
README-zh.md
Normal file
307
README-zh.md
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
# Obsidian share to NotionNext
|
||||||
|
|
||||||
|
感谢[原作者](https://github.com/EasyChris/obsidian-to-notion)开发出了这么好用的插件,能够将obsidian同步到notion。 但是原仓库只能同步Name和Tags信息,如果像我一样通过[NotionNext](https://github.com/tangly1024/NotionNext)来搭建自己的网页,就有一些局限性。每次导入之后都需要进行大量的修改。
|
||||||
|
|
||||||
|
所以我在[原作者](https://github.com/EasyChris/obsidian-to-notion)的基础之上,增加了匹配[NotionNext](https://github.com/tangly1024/NotionNext)模板的功能。这样可以直接在Obsidian编辑,整理好之后一键发布。
|
||||||
|
|
||||||
|
## 更新说明
|
||||||
|
### 1.1.2
|
||||||
|
- 修复了一个拼写错误,导致无法同步`status`到NotionNext。现在你可以使用`stats`或者`status`来同步文章的状态到NotionNext。这个更新不会影响到同步到General数据库的功能。
|
||||||
|
- **`stats`和`status`都可以使用,但是你只能使用其中一个。**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
stats: Draft # Draft, Invisible, Published, default is Draft, 默认是Draft
|
||||||
|
# or
|
||||||
|
status: Draft # Draft, Invisible, Published, default is Draft, 默认是Draft
|
||||||
|
# both of them will work, but you can only use one of them.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.1.1
|
||||||
|
- 修复了在日语系统下,无法显示设置按钮的错误。
|
||||||
|
- 增加日语翻译。
|
||||||
|
### 1.1.0
|
||||||
|
- 修复设置中自定义标题的显示问题。
|
||||||
|
- 增加了标签同步的控制按钮。以减少空标签集造成的上传失败。
|
||||||
|
|
||||||
|
如果你不想使用同步标签功能,你可以在设置中关闭。不论你在md文件表头添加了tags与否,都不会将标签同步到Notion数据库中。
|
||||||
|
|
||||||
|
如果你需要同步标签到Notion数据库中,**你需要通过如下格式添加表填到md表头中。**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
tags: #空标签,选项1
|
||||||
|
tags: [test,test1,test2] # 用中括号将所有的标签括起来,选项2
|
||||||
|
tags:
|
||||||
|
- test
|
||||||
|
- test1
|
||||||
|
- test2 # 每个标签单独一行,选项3
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.0.1
|
||||||
|
|
||||||
|
- 修复了自定义标题在设置菜单中显示错误的问题。
|
||||||
|
|
||||||
|
### 1.0.0 (大更新)
|
||||||
|
|
||||||
|
- 这个版本开始,你可以**任意修改第一列表头的名字 (标题列,默认:'title')**。 (**注意:你必须保证你的Notion数据库中有'tags‘这一列的存在,并且在markdown文件中把`tags:`添加到你的YAML表头中。否则会返回`400 错误`。但是你可以`tags:`设置为空。**)
|
||||||
|
|
||||||
|

|
||||||
|
例如这样:
|
||||||
|

|
||||||
|
- 增加了一个控制按钮来控制是否开启当前数据库同步命令的功能。如果你关闭了这个按钮,你在上传命令列表中就不会出现当前数据库的选项。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 0.2.6
|
||||||
|
|
||||||
|
- 增加一个按钮用来控制是否在名列列表中显示如下数据库。
|
||||||
|
|
||||||
|

|
||||||
|
如果你关闭普通数据库的按钮,你在上传命令列表中就不会出现普通数据库的选项。如果你关闭NotionNext数据库的按钮,你在上传命令列表中就不会出现NotionNext数据库的选项。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 0.2.3
|
||||||
|
|
||||||
|
- 修复了一个bug,现在可以正常更新了。
|
||||||
|
|
||||||
|
### 0.2.2
|
||||||
|
|
||||||
|
- 支持NotionNext 和 普通Notion 数据库的同步。
|
||||||
|
- 你可以有一个NotionNext和一个普通Notion数据库。
|
||||||
|
- 普通Notion数据库的同步方式和原作者的一样,只同步`title`和`tags`。**如果使用普通数据库功能,你需要保证你的数据库中有`title`和`tags`这两个表头。必须使用小写。**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 0.2.1
|
||||||
|
|
||||||
|
- 重构代码。
|
||||||
|
|
||||||
|
### 0.2.0
|
||||||
|
|
||||||
|
- 从这个版本开始,重构了交互逻辑。现在当你点击左侧的按钮,会弹出可同步的数据库选项。你可以选择你想要同步的数据库,然后点击同步按钮。**但是,现在仅支持同步单个数据库,如果你的数据库不是NotionNext,你可以不用更新。**
|
||||||
|
|
||||||
|
### 0.1.10
|
||||||
|
|
||||||
|
- 修正了设置中的中文显示。
|
||||||
|
|
||||||
|
### 0.1.8
|
||||||
|
|
||||||
|
- 重新整理了代码,增加了一个仓库转换按钮为之后的多数据库支持作准备。**但是现在还不支持多数据库,如果你的数据库不是NotionNext,你可以不用更新。**
|
||||||
|
|
||||||
|
### 0.1.7
|
||||||
|
|
||||||
|
- [x] 删除了设置中`convert tags`的选项。 你现在可以直接添加tags,而不需要考虑是否开启tags选项。如果你不需要tags,可以直接删除tags选项或者留白。
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
|
||||||
|
本插件现在暂时只能用于匹配[NotionNext](https://github.com/tangly1024/NotionNext)。如果你不是使用这个模板,你只会无限返回`error 400`.
|
||||||
|
|
||||||
|
所以没有NotionNext需求的,请使用原作者的[Obsidian-to-notion](https://github.com/EasyChris/obsidian-to-notion)
|
||||||
|
|
||||||
|
### 准备安装
|
||||||
|
|
||||||
|
在安装插件之前,你必须配置好了以下内容:
|
||||||
|
|
||||||
|
1. 你的NotionNext数据库。
|
||||||
|
2. 根据原作者的readme.md,配置好了Notion API,并且已经和你的NotionNext仓库关联。
|
||||||
|
3. NotionNext Database ID
|
||||||
|
4. 你的NotionNext数据库有如下内容:
|
||||||
|
- type
|
||||||
|
- title
|
||||||
|
- slug
|
||||||
|
- category
|
||||||
|
- tags
|
||||||
|
- date
|
||||||
|
- status
|
||||||
|
- summary
|
||||||
|
- password
|
||||||
|
- icon
|
||||||
|
|
||||||
|
在最新的0.0.6版本,我还增加了以下内容:
|
||||||
|
|
||||||
|
- titleicon: 标题的emoji图标,暂时只能支持emoji
|
||||||
|
- coverurl: 封面图片的url, url可以是任何一种图片的url, 例如jpg,png,svg等等。
|
||||||
|
|
||||||
|
**如果你是直接复制的NotionNext的模板,这些内容应该已经有了。我在原作者的基础之上对本插件的内容进行了更改,所以你只需要保证你的数据库有如上内容,并且所有的字母都是小写!!!**
|
||||||
|
|
||||||
|
**⚠️⚠️⚠️:表头全部小写!!!顺序无所谓!**
|
||||||
|
|
||||||
|
### 安装插件
|
||||||
|
|
||||||
|
#### 社区插件市场下载
|
||||||
|
|
||||||
|
打开`Obsidian -> 设置 -> 社区插件 -> 浏览 -> 搜索Share to NotionNext`, 然后点击安装。
|
||||||
|
|
||||||
|
#### 手动安装
|
||||||
|
|
||||||
|
1. 关闭Obsidian
|
||||||
|
2. 从Release下载插件文件,解压到你的obsidian插件目录下。
|
||||||
|
3. 重新打开Obsidian,进入设置,启用插件。
|
||||||
|
4. 在设置中,找到Obsidian to NotionNext,填入你的NotionNext Database ID 和 API token。
|
||||||
|
|
||||||
|
### 使用插件
|
||||||
|
|
||||||
|
在仓库中我上传了一个模板,你可以直接复制到你的模板文件夹中。然后使用Obsidian的模板功能一键生成新笔记。
|
||||||
|
|
||||||
|
如果你不想使用模板,你也可以直接在Obsidian中创建一个新的文件,然后复制下边的内容。然后保存。
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
# default value has been set.
|
||||||
|
# for any unwanted value, you can delete it or set it to empty.
|
||||||
|
# for example, if you donot want to set password, you can delete password: "1234" or set it to password: ""
|
||||||
|
# 我已经在插件中设置了默认值,如果有不需要的选项,可以直接删除。
|
||||||
|
# 例如你不需要密码选项,你可以将password: "1234"删除,或者将它设置为空。
|
||||||
|
# !!!!!!!!!!!!
|
||||||
|
# 现在阶段一定不要修改表头的名字, please do not change the name of the header in YAML front matter
|
||||||
|
# !!!!!!!!!!!!
|
||||||
|
titleicon: 📎 # emoji icon, default is 📜, 默认是📜
|
||||||
|
date: 2023-07-23 # default is today, 默认是今天。 Format is YYYY-MM-DD, 格式是YYYY-MM-DD
|
||||||
|
coverurl: https://img.jxpeng.dev/2023/08/843e27a210847f05a0f7cfb121fec100.jpg # default is empty, 默认是空
|
||||||
|
type: Post # Post or Page, default is Post, 默认是Post
|
||||||
|
slug: test # slug for url, default is empty, 默认是空
|
||||||
|
stats: Draft # Draft, Invisible, Published, default is Draft, 默认是Draft
|
||||||
|
category: test # default is 'Obsidian', 默认是'Obsidian'
|
||||||
|
summary: this is a summary for test post # default is empty, 默认是空
|
||||||
|
icon: fa-solid fa-camera # you can ignore this, default is empty, 默认是空,可直接删除
|
||||||
|
password: "1234" # if you donot want to set password, you can delete this line, default is empty, 默认是空,可直接删除
|
||||||
|
# 现在必须开启tags选项,否则会报错
|
||||||
|
tags:
|
||||||
|
- test # tags for post
|
||||||
|
- web # add more tags if you want
|
||||||
|
---
|
||||||
|
|
||||||
|
Contents Below
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
模板使用如下:
|
||||||
|

|
||||||
|
|
||||||
|
**插件预览如下**
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**以下为原README.md**
|
||||||
|
|
||||||
|
非常感谢原作者的开发,我只是在原作者的基础之上进行了修改,增加了一些功能。如果你觉得这个插件对你有帮助,可以给[原作者](https://github.com/EasyChris/obsidian-to-notion)一个star。
|
||||||
|
|
||||||
|
# Obsidian to Notion
|
||||||
|
|
||||||
|
[](https://github.com/Easychris/obsidian-to-notion/actions/workflows/CI.yml)
|
||||||
|
[](https://github.com/Easychris/obsidian-to-notion/actions/workflows/release.yml)
|
||||||
|
[](https://raw.githubusercontent.com/EasyChris/obsidian-to-notion/master/LICENSE)
|
||||||
|
[](https://GitHub.com/Easychris/obsidian-to-notion/releases/)
|
||||||
|
[](https://github.com/Easychris/obsidian-to-notion/releases)
|
||||||
|
|
||||||
|
Obsidian share to Notion [English](README.md)
|
||||||
|
|
||||||
|
将obsidian文件一键分享到Notion,并在obsidian中添加Notion分享链接
|
||||||
|
|
||||||
|
如果能对你有所帮助,欢迎给一个star支持。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 使用方式
|
||||||
|
|
||||||
|
## 安装插件
|
||||||
|
|
||||||
|
### 市场下载
|
||||||
|
|
||||||
|
插件市场搜索 noiton 即可下载
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### BRAT
|
||||||
|
|
||||||
|
插件中中心搜索 BRAT
|
||||||
|
添加 `EasyChris/obsidian-to-notion` 到 BRAT 插件安装列表中
|
||||||
|
返回插件中心启用即可
|
||||||
|
|
||||||
|
### 手动安装
|
||||||
|
|
||||||
|
```
|
||||||
|
cd YOUR_OBSIDIAN_FOLDER/.obsidian/plugins/
|
||||||
|
git clone https://github.com/EasyChris/obsidian-to-notion.git
|
||||||
|
```
|
||||||
|
|
||||||
|
## 申请 Notion API
|
||||||
|
|
||||||
|
官方参考文档:[https://developers.notion.com/docs](https://developers.notion.com/docs)
|
||||||
|
|
||||||
|
### 第 1 步:创建integration
|
||||||
|
|
||||||
|
转到 [https://www.notion.com/my-integrations](https://www.notion.com/my-integrations)
|
||||||
|
创建完成之后,复制`secrets toekn`
|
||||||
|

|
||||||
|
|
||||||
|
### 第2步:与你的集成共享一个数据库
|
||||||
|
|
||||||
|
新建一个的page(权限为公开)
|
||||||
|
在page中新建一个数据库 -> 需要`full page database`
|
||||||
|

|
||||||
|
|
||||||
|
将`integration`添加到你的新建的数据库中
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 注意
|
||||||
|
|
||||||
|
数据库的第一个自定义名称必须是 "Name",否则同步会失败。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 第三步:复制database ID
|
||||||
|
|
||||||
|
```
|
||||||
|
https://www.notion.so/myworkspace/a8aec43384f447ed84390e8e42c2e089?v=...
|
||||||
|
|--------- Database ID --------|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## 打开插件配置
|
||||||
|
|
||||||
|
将得到的 `NOTION_API_KEY` 和 `DATABASE_ID`填入配置当中
|
||||||
|

|
||||||
|
|
||||||
|
## 上传文件内容到notion
|
||||||
|
|
||||||
|
点击上传notion的按钮
|
||||||
|

|
||||||
|
上传成功之后会自动生成一个分享链接
|
||||||
|

|
||||||
|
|
||||||
|
## 页面 Banner 链接[可选]
|
||||||
|
|
||||||
|
默认可以不填写
|
||||||
|
横幅URL必须是图像URL,例如:https://i.imgur.com/xxx.jpg
|
||||||
|
|
||||||
|
## Notion ID [可选]
|
||||||
|
|
||||||
|
Notion ID是你想分享文件的页面ID。
|
||||||
|
如果你不写它,notion将分享到默认的链接,如:<https://www.notion.so/myworkspace/a8aec43384f447ed84390,访问这个页面将重定向到你的网站页面。>
|
||||||
|
如果你写了Notion ID,它将分享到页面链接如:<https://your_user_name.notion.site/myworkspace/a8aec43384f447ed84390。不需要重定向网址。>
|
||||||
|
|
||||||
|
## 同步图片
|
||||||
|
|
||||||
|
使用 [Obsidian Image Auto Upload Plugin](https://github.com/renmu123/obsidian-image-auto-upload-plugin) 插件,配置你自己的 cos 或者 oss,将图片存储到你自己的云存储,然后在 obsidian 中使用图片链接即可。该插件会自动帮你上传图片,并替换成链接。
|
||||||
|
|
||||||
|
# 请我喝杯咖啡
|
||||||
|
|
||||||
|
[顿顿饭](https://dun.mianbaoduo.com/@easy)
|
||||||
|
|
||||||
|
# 感谢
|
||||||
|
|
||||||
|
[开发流程 | Obsidian 插件开发文档](https://luhaifeng666.github.io/obsidian-plugin-docs-zh/zh/getting-started/development-workflow.html)
|
||||||
|
|
||||||
|
[GitHub - devbean/obsidian-wordpress: An obsidian plugin for publishing docs to WordPress.](https://github.com/devbean/obsidian-wordpress)
|
||||||
|
|
||||||
|
[GitHub - obsidianmd/obsidian-api](https://github.com/obsidianmd/obsidian-api)
|
||||||
|
|
||||||
|
[GitHub - zhaohongxuan/obsidian-weread-plugin: Obsidian Weread Plugin is an plugin to sync Weread(微信读书) hightlights and annotations into your Obsidian Vault.](https://github.com/zhaohongxuan/obsidian-weread-plugin)
|
||||||
452
README.md
452
README.md
@@ -1,66 +1,452 @@
|
|||||||
Obsidian share to Notion
|
# Obsidian to NotionNext
|
||||||
|
|
||||||
将obsidian文件一键分享到Notion,并在obsidian中添加Notion分享链接
|
[](https://github.com/jxpeng98/obsidian-to-NotionNext/actions/workflows/test.yml)
|
||||||
|
[](https://github.com/jxpeng98/obsidian-to-NotionNext/actions/workflows/release.yml)
|
||||||
|
[](https://GitHub.com/jxpeng98/obsidian-to-NotionNext/releases/)
|
||||||
|
[](https://github.com/jxpeng98/obsidian-to-NotionNext/releases/)
|
||||||
|
|
||||||
|
[//]: # ([](https://GitHub.com/jxpeng98/obsidian-to-NotionNext/releases/))
|
||||||
|
|
||||||
|
[//]: # ([中文文档](README-zh.md))
|
||||||
|
|
||||||
|
**Now, support both NotionNext and General databases with customised properties.**
|
||||||
|
|
||||||
|
**现在支持NotionNext和普通Notion数据库,可自定义数据库列表。**
|
||||||
|
|
||||||
|
## Precautions
|
||||||
|
|
||||||
|
### For customised database users
|
||||||
|
|
||||||
|
**⚠️⚠️⚠️: 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版本,你需要重新创建自定义数据库。新版本有一个新的数据库结构,旧的数据库结构无法构建索引。**
|
||||||
|
|
||||||
|
## TODO List
|
||||||
|
|
||||||
|
- [x] ~~Modify the Edit function for the custom properties. 改进自定义属性的编辑功能~~
|
||||||
|
- [ ] 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
|
||||||
|
|
||||||
|
If you want to use this plugin, you need to follow the following steps to set up the plugin. The steps can be divided into two parts: setting up the Notion API and setting up the plugin in Obsidian.
|
||||||
|
|
||||||
|
### 1. Setting up the Notion API
|
||||||
|
|
||||||
|
1). Go to [Notion API](https://www.notion.com/my-integrations) to create a new integration, and **copy the token**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2). Create a database in your Notion workspace.
|
||||||
|
|
||||||
|
Open Notion, click top-left `Create` a new page` -> You can give a title for the page and press "/" Select 'Database - Full page'.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Rename the database properties to `title` and `tags`.
|
||||||
|
**⚠️⚠️`title` is the title column! `tags` is lower case⚠️⚠️**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3). Add the Notion API integration.
|
||||||
|
|
||||||
|
Click the right-top '...' -> connections -> connect to -> find the integration you created and connect it.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
4). Publish the database to the public.
|
||||||
|
Click the 'Share' -> 'Publish' -> 'Publish' -> **copy the database id from the URL**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 2. Setting up the plugin in Obsidian
|
||||||
|
|
||||||
|
1). Go to Obsidian settings -> community plugins -> search 'NotionNext' -> install it.
|
||||||
|

|
||||||
|
|
||||||
|
2). Open the plugin settings and create a new database.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 3. Create a database
|
||||||
|
|
||||||
|
**You can create three types of databases:**
|
||||||
|
|
||||||
|
#### 1️⃣ General database
|
||||||
|
|
||||||
|
General database is the default database, which only has the `title` and `tags` columns. **⚠️⚠️`title` and `tags` are lowercase⚠️⚠️**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The general is as follows:
|
||||||
|
|
||||||
|
- Full Name: the full name that you would like to give to the database.
|
||||||
|
- Abbreviate Name: the abbreviation that you would like to give to the database.
|
||||||
|
- Notion tags sync: whether you want to sync the tags column to Notion.
|
||||||
|
- Customise title property: whether you want to change the `title` to another name (e.g., `name`).
|
||||||
|
- Notion API token: the token you copied from the Notion API page.
|
||||||
|
- Notion Database ID: the database ID you copied from the Notion database URL. (e.g., only copy `7158cd3f70ce4f60afd9da5bdf74fd0c` between `site/` and `?`. `https://jxpeng.notion.site/7158cd3f70ce4f60afd9da5bdf74fd0c?v=380317ac5373sadasdas3c13e618f&pvs=4**`)
|
||||||
|
|
||||||
|
After setting, it should look like this:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Once you click `save`, the new database will be created in the plugin settings and display in the detabase list.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 2️⃣ NotionNext database
|
||||||
|
|
||||||
|
If you use [NotionNext](https://github.com/tangly1024/NotionNext) to set up your website, you can use the NotionNext database. (**All the properties are in lowercase, and you do not need to change the properties**)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Like the general database, you need to fill in the following information:
|
||||||
|
|
||||||
|
- Full Name: the full name that you would like to give to the database.
|
||||||
|
- Abbreviate Name: the abbreviation that you would like to give to the database.
|
||||||
|
- Notion API token: the token you copied from the Notion API page.
|
||||||
|
- Notion Database ID: the database ID you copied from the Notion database URL.
|
||||||
|
|
||||||
|
#### 3️⃣ Custom database
|
||||||
|
|
||||||
|
The custom database is purely customised, and you can customise the properties you want to sync. (**All the properties are in lowercase, and you do not need to change the properties**)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The basic information is identical to the general database and NotionNext database. You need to fill in the following information:
|
||||||
|
|
||||||
|
- Full Name: the full name that you would like to give to the database.
|
||||||
|
- Abbreviate Name: the abbreviation that you would like to give to the database.
|
||||||
|
- Notion API token: the token you copied from the Notion API page.
|
||||||
|
- Notion Database ID: the database ID you copied from the Notion database URL.
|
||||||
|
|
||||||
|
After adding the information, You can click `Add New Property` to add the properties you want to sync. There is no limit to the number of properties you can add.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**⚠️⚠️⚠️**: **`Title column` is the main property in the Notion database, which has the open button to open the underlying page.** You can select the type of all other properties. **⚠️⚠️⚠️**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
According to the previously shown database, you can use `title` as the `Title column`, and `Tags` as the property 1.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Until now, you have set up the plugin in Obsidian. You can start syncing your notes to Notion.**
|
||||||
|
|
||||||
|
### 4. Start syncing
|
||||||
|
|
||||||
|
Create a new note in Obsidian, and fill in the front matter with the properties you want to sync.
|
||||||
|
|
||||||
|
#### 1️⃣ note for general database
|
||||||
|
|
||||||
|
For example, we can create a test note with the following front matter:
|
||||||
|
|
||||||
|
``` markdown
|
||||||
|
---
|
||||||
|
title: test
|
||||||
|
tags: [test, web]
|
||||||
|
---
|
||||||
|
|
||||||
|
This is a test file.
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
After creating the note, you can click the `Share to NotionNext` button on the left sidebar to sync the note to Notion.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 2️⃣ note for NotionNext database
|
||||||
|
|
||||||
|
We use the following front matter for the NotionNext database:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
title: test-notionnext
|
||||||
|
titleicon: 📎
|
||||||
|
date: 2023-07-23
|
||||||
|
coverurl: https://img.jxpeng.dev/2023/08/843e27a210847f05a0f7cfb121fec100.jpg
|
||||||
|
type: Post
|
||||||
|
slug: test
|
||||||
|
status: Draft
|
||||||
|
category: test
|
||||||
|
summary: this is a summary for test post
|
||||||
|
icon: fa-solid fa-camera
|
||||||
|
password: "1234"
|
||||||
|
tags:
|
||||||
|
- test
|
||||||
|
- web
|
||||||
|
NotionID-pengjiaxin: 8ba573de-8fdf-4681-b063-c39d26e7860e
|
||||||
|
---
|
||||||
|
|
||||||
|
this is a test file
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 3️⃣ note for custom database
|
||||||
|
|
||||||
|
##### sync title and tags
|
||||||
|
|
||||||
|
Use the previously created general database and rename `title` to `Name` and `tags` to `Tags` in Notion.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The front matter for the custom database is as follows:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
Name: test-custom
|
||||||
|
Tags:
|
||||||
|
- test
|
||||||
|
- web
|
||||||
|
---
|
||||||
|
|
||||||
|
This is a test file.
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
##### sync more properties
|
||||||
|
|
||||||
|
Add more property to the custom database. For example, we can add a `summary` and `date` property to the custom database.
|
||||||
|
|
||||||
|
1). Click `Edit` in the plugin settings.
|
||||||
|

|
||||||
|
2). Click `Add` and add the `summary` and `date` property.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
3). After adding the properties, you can see the properties in the plugin settings.
|
||||||
|

|
||||||
|
4). Add the `summary` and `date` in Notion
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
5). Create a new note with the `summary` and `date` property.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
Name: test-custom-more
|
||||||
|
Tags:
|
||||||
|
- test
|
||||||
|
- web
|
||||||
|
summary: this is a summary for test custom
|
||||||
|
date: 2022-05-03
|
||||||
|
---
|
||||||
|
|
||||||
|
This is a test file.
|
||||||
|
```
|
||||||
|
|
||||||
|
6). Sync the note to Notion.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Acknowledgment
|
||||||
|
|
||||||
|
Thanks to the [original author](https://github.com/EasyChris/obsidian-to-notion) for developing such a useful plugin that can synchronize Obsidian to Notion. However, the original repository can only sync Name and Tag information. For those like me who use [NotionNext](https://github.com/tangly1024/NotionNext) to set up their website, this presents some limitations. Every time I import, I need to make a lot of modifications.
|
||||||
|
|
||||||
|
Thus, based on the [original author's work](https://github.com/EasyChris/obsidian-to-notion), I've added a feature to match the [NotionNext](https://github.com/tangly1024/NotionNext) template. This way, you can edit directly in Obsidian and publish with a single click after organizing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<details> <summary> Original README.md </summary>
|
||||||
|
|
||||||
|
Many Thanks for the original author's work. I've only made some changes to the original author's work. If you find this plugin useful, please give the [original author](https://github.com/EasyChris/obsidian-to-notion) a star.
|
||||||
|
|
||||||
|
# Obsidian to Notion
|
||||||
|
|
||||||
|
[](https://github.com/Easychris/obsidian-to-notion/actions/workflows/CI.yml)
|
||||||
|
[](https://github.com/Easychris/obsidian-to-notion/actions/workflows/release.yml)
|
||||||
|
[](https://raw.githubusercontent.com/EasyChris/obsidian-to-notion/master/LICENSE)
|
||||||
|
[](https://GitHub.com/Easychris/obsidian-to-notion/releases/)
|
||||||
|
[](https://github.com/Easychris/obsidian-to-notion/releases)
|
||||||
|
|
||||||
|
Share of obsidian to Notion [中文文档](README-zh.md)
|
||||||
|
|
||||||
|
Sharing files from Obsidian to Notion with a single click, and Obsidian will automatically add the Notion share link
|
||||||
|
|
||||||
|
You are welcome to offer it a star if it can benefit you.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
# 使用方式
|
# TODO
|
||||||
## 安装插件
|
|
||||||
|
### [TODO Board](https://github.com/users/EasyChris/projects/3/views/1)
|
||||||
|
|
||||||
|
- [x] support for custom page banner
|
||||||
|
- [x] update the exsit page
|
||||||
|
- [x] support for mult language
|
||||||
|
- [x] support for auto copy the share link to clipboard
|
||||||
|
- [x] support for mobile
|
||||||
|
- [x] support tags thank for [@jannikbuscha](https://github.com/jannikbuscha)
|
||||||
|
- [ ] transfer the bi-link format like [[]] into the format that Notion supports.
|
||||||
|
|
||||||
|
# How to use
|
||||||
|
|
||||||
|
## Install the plugin
|
||||||
|
|
||||||
|
### Marketplace download
|
||||||
|
|
||||||
|
Open obsidian setting -> Add plugin -> Search -> notion
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
### BRAT
|
### BRAT
|
||||||
插件中中心搜索 BRAT
|
|
||||||
添加 `EasyChris/obsidian-to-notion` 到 BRAT 插件安装列表中
|
Enter `BRAT` into the plugin market center to find it.
|
||||||
返回插件中心启用即可
|
Add `EasyChris/obsidian-to-notion` to the list of BRAT plugins that have been installed.
|
||||||
### 手动安装
|
Return to the plugin center and turn it on.
|
||||||
|
|
||||||
|
### Manual installation
|
||||||
|
|
||||||
```
|
```
|
||||||
cd YOUR_OBSIDIAN_FOLDER/.obsidian/plugins/
|
cd YOUR_OBSIDIAN_FOLDER/.obsidian/plugins/
|
||||||
git clone https://github.com/EasyChris/obsidian-to-notion.git
|
git clone https://github.com/EasyChris/obsidian-to-notion.git
|
||||||
```
|
```
|
||||||
### 市场下载
|
|
||||||
因为暂时还未上架,所以请通过BRAT安装
|
|
||||||
|
|
||||||
## 申请 Notion API
|
## Apply Notion API
|
||||||
官方参考文档:[https://developers.notion.com/docs](https://developers.notion.com/docs)
|
|
||||||
### 第 1 步:创建integration。
|
Official reference documentation: [https://developers.notion.com/docs](https://developers.notion.com/docs)
|
||||||
转到 [https://www.notion.com/my-integrations](https://www.notion.com/my-integrations)
|
|
||||||
创建完成之后,复制`secrets toekn`
|
### Step 1: Create integration
|
||||||
|
|
||||||
|
Go to [https://www.notion.com/my-integrations](https://www.notion.com/my-integrations)
|
||||||
|
Once created, copy `secrets toekn`
|
||||||

|

|
||||||
|
|
||||||
### 第2步:与你的集成共享一个数据库
|
#### Note
|
||||||
新建一个的page(权限为公开)
|
|
||||||
在page中新建一个数据库 -> 需要`full page database`
|
database first custom name must be "Name", otherwise sync to notion will be failed
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Step 2: Share a database with your integration
|
||||||
|
|
||||||
|
Create a new page (with public permissions)
|
||||||
|
Create a new database in the page -> you need `full page database`
|
||||||

|

|
||||||
|
|
||||||
将`integration`添加到你的新建的数据库中
|
Add `integration` to your new database
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### 第三步:复制database ID
|
### Step 3: Copy the database ID
|
||||||
|
|
||||||
```
|
```
|
||||||
https://www.notion.so/myworkspace/a8aec43384f447ed84390e8e42c2e089?v=...
|
https://www.notion.so/myworkspace/a8aec43384f447ed84390e8e42c2e089?v=...
|
||||||
|--------- Database ID --------|
|
| --------- Database ID --------|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Open the plugin configuration
|
||||||
|
|
||||||
|
Fill the configuration with the `NOTION_API_KEY` and `DATABASE_ID` you got
|
||||||
## 打开插件配置
|
|
||||||
将得到的 `NOTION_API_KEY` 和 `DATABASE_ID`填入配置当中
|
|
||||||

|

|
||||||
|
|
||||||
## 上传文件内容到notion
|
## Upload file content to notion
|
||||||
点击上传notion的按钮
|
|
||||||
|
Click the uploadCommand notion button
|
||||||

|

|
||||||
上传成功之后会自动生成一个分享链接
|
A share link will be automatically generated after successful uploadCommand
|
||||||

|

|
||||||
|
|
||||||
|
## Banner URL [option]
|
||||||
|
|
||||||
# 请我喝杯咖啡
|
Banner url must be a image url like: <https://i.imgur.com/xxx.jpg>
|
||||||
|
If you don't want to use banner, leave it blank
|
||||||
|
|
||||||
[顿顿饭](https://dun.mianbaoduo.com/@easy)
|
## Convert Tags [option]
|
||||||
|
|
||||||
|
Transfer the Obsidian tags to the Notion table.
|
||||||
|
It requires the column with the name 'Tags'.
|
||||||
|

|
||||||
|
|
||||||
|
Add tags to your notion page
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- open plugin convert tags
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- add tags in the head
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
tags: [tag1,tag2]
|
||||||
|
---
|
||||||
|
|
||||||
|
this is test tags
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- tag4
|
||||||
|
---
|
||||||
|
|
||||||
|
this is test tags
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Thanks for [@jannikbuscha](https://github.com/jannikbuscha) contribution
|
||||||
|
|
||||||
|
## Notion ID [option]
|
||||||
|
|
||||||
|
Notion ID is the your notion site ID that you want to share the file to.
|
||||||
|
if you don't write it, notion will share to the default link like:
|
||||||
|
<https://www.notion.so/myworkspace/a8aec43384f447ed84390>
|
||||||
|
that visit this page need to redirect to your site url
|
||||||
|
if you write the Notion ID, it will share to the page link like:
|
||||||
|
<https://your_user_name.notion.site/myworkspace/a8aec43384f447ed84390>.
|
||||||
|
The visiter don't need to redirect url.
|
||||||
|
|
||||||
|
## Sync image to Notion
|
||||||
|
|
||||||
|
To sync images to your oss or cos bucket, use the [Obsidian Image Auto Upload Plugin](https://github.com/renmu123/obsidian-image-auto-upload-plugin).
|
||||||
|
|
||||||
|
# Development
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/EasyChris/obsidian-to-notion.git
|
||||||
|
yarn install
|
||||||
|
yarn dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Release
|
||||||
|
|
||||||
|
```
|
||||||
|
node update-version.js
|
||||||
|
./release.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
# Thanks
|
||||||
|
[Development Process | Obsidian Plugin Development Documentation](https://luhaifeng666.github.io/obsidian-plugin-docs-zh/zh/getting-started/development-workflow.html)
|
||||||
|
|
||||||
# 感谢
|
|
||||||
[开发流程 | Obsidian 插件开发文档](https://luhaifeng666.github.io/obsidian-plugin-docs-zh/zh/getting-started/development-workflow.html)
|
|
||||||
[GitHub - devbean/obsidian-wordpress: An obsidian plugin for publishing docs to WordPress.](https://github.com/devbean/obsidian-wordpress)
|
[GitHub - devbean/obsidian-wordpress: An obsidian plugin for publishing docs to WordPress.](https://github.com/devbean/obsidian-wordpress)
|
||||||
|
|
||||||
[GitHub - obsidianmd/obsidian-api](https://github.com/obsidianmd/obsidian-api)
|
[GitHub - obsidianmd/obsidian-api](https://github.com/obsidianmd/obsidian-api)
|
||||||
[GitHub - zhaohongxuan/obsidian-weread-plugin: Obsidian Weread Plugin is an plugin to sync Weread(微信读书) hightlights and annotations into your Obsidian Vault.](https://github.com/zhaohongxuan/obsidian-weread-plugin)
|
|
||||||
|
[GitHub - Easychris/obsidian-to-notion: Obsidian Weread Plugin is an plugin to sync Weread(微信读书) hightlights and annotations into your Obsidian Vault.](https://github.dev/zhaohongxuan/obsidian-weread-plugin)
|
||||||
|
|
||||||
|
[GitHub - Quorafind/Obsidian-Memos: A quick capture plugin for Obsidian, all data from your notes.](https://github.com/Quorafind/Obsidian-Memos)
|
||||||
|
|
||||||
|
[https://github.com/jannikbuscha/obsidian-to-notion](https://github.com/jannikbuscha)
|
||||||
|
|
||||||
|
# License
|
||||||
|
GNU GPLv3
|
||||||
|
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
import { requestUrl } from "obsidian";
|
|
||||||
import { Client } from "@notionhq/client";
|
|
||||||
import { markdownToBlocks, } from "@tryfabric/martian";
|
|
||||||
import * as fs from "fs";
|
|
||||||
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 createPage(title:string, childArr: any) {
|
|
||||||
const bodyString = {
|
|
||||||
parent: {
|
|
||||||
database_id: this.app.settings.databaseID
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
Name: {
|
|
||||||
title: [
|
|
||||||
{
|
|
||||||
text: {
|
|
||||||
content: title,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
children: childArr,
|
|
||||||
}
|
|
||||||
const response = 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': '2021-08-16',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(bodyString),
|
|
||||||
})
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
async syncMarkdownToNotion(title:string, markdown: string, fullPath:string): Promise<any> {
|
|
||||||
const yamlObj:any = yamlFrontMatter.loadFront(markdown);
|
|
||||||
const __content = yamlObj.__content
|
|
||||||
const file2md = markdownToBlocks(__content);
|
|
||||||
const res = await this.createPage(title, file2md);
|
|
||||||
if (res.status === 200) {
|
|
||||||
this.updateYamlInfo(markdown, fullPath, res)
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateYamlInfo(yamlContent: string, fullPath: string, res: any) {
|
|
||||||
const yamlObj:any = yamlFrontMatter.loadFront(yamlContent);
|
|
||||||
const {url} = res.json
|
|
||||||
yamlObj.link = url;
|
|
||||||
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;
|
|
||||||
|
|
||||||
//write content fo file
|
|
||||||
fs.writeFileSync(fullPath, content);
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BIN
doc/10.png
Normal file
BIN
doc/10.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
@@ -1,52 +1,61 @@
|
|||||||
import esbuild from "esbuild";
|
import esbuild from "esbuild";
|
||||||
import process from "process";
|
import process from "process";
|
||||||
import builtins from 'builtin-modules'
|
import builtins from "builtin-modules";
|
||||||
|
|
||||||
const banner =
|
const banner = `/*
|
||||||
`/*
|
|
||||||
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
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');
|
const ctx = await esbuild.context({
|
||||||
|
|
||||||
esbuild.build({
|
|
||||||
banner: {
|
banner: {
|
||||||
js: banner,
|
js: banner,
|
||||||
},
|
},
|
||||||
entryPoints: ['main.ts'],
|
entryPoints: ["src/main.ts"],
|
||||||
bundle: true,
|
bundle: true,
|
||||||
external: [
|
external: [
|
||||||
'obsidian',
|
"obsidian",
|
||||||
'electron',
|
"electron",
|
||||||
'@codemirror/autocomplete',
|
"@codemirror/autocomplete",
|
||||||
'@codemirror/closebrackets',
|
"@codemirror/closebrackets",
|
||||||
'@codemirror/collab',
|
"@codemirror/collab",
|
||||||
'@codemirror/commands',
|
"@codemirror/commands",
|
||||||
'@codemirror/comment',
|
"@codemirror/comment",
|
||||||
'@codemirror/fold',
|
"@codemirror/fold",
|
||||||
'@codemirror/gutter',
|
"@codemirror/gutter",
|
||||||
'@codemirror/highlight',
|
"@codemirror/highlight",
|
||||||
'@codemirror/history',
|
"@codemirror/history",
|
||||||
'@codemirror/language',
|
"@codemirror/language",
|
||||||
'@codemirror/lint',
|
"@codemirror/lint",
|
||||||
'@codemirror/matchbrackets',
|
"@codemirror/matchbrackets",
|
||||||
'@codemirror/panel',
|
"@codemirror/panel",
|
||||||
'@codemirror/rangeset',
|
"@codemirror/rangeset",
|
||||||
'@codemirror/rectangular-selection',
|
"@codemirror/rectangular-selection",
|
||||||
'@codemirror/search',
|
"@codemirror/search",
|
||||||
'@codemirror/state',
|
"@codemirror/state",
|
||||||
'@codemirror/stream-parser',
|
"@codemirror/stream-parser",
|
||||||
'@codemirror/text',
|
"@codemirror/text",
|
||||||
'@codemirror/tooltip',
|
"@codemirror/tooltip",
|
||||||
'@codemirror/view',
|
"@codemirror/view",
|
||||||
...builtins],
|
...builtins,
|
||||||
format: 'cjs',
|
],
|
||||||
watch: !prod,
|
platform: "node",
|
||||||
target: 'es2016',
|
format: "cjs",
|
||||||
|
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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
17
icon.ts
17
icon.ts
@@ -1,17 +0,0 @@
|
|||||||
import { addIcon } from 'obsidian';
|
|
||||||
|
|
||||||
const icons: Record<string, string> = {
|
|
||||||
'notion-logo': `
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100px" height="100px" viewBox="0 0 100 100" version="1.1">
|
|
||||||
<g id="surface1">
|
|
||||||
<path style=" stroke:none;fill-rule:evenodd;fill:currentColor;fill-opacity:1;" d="M 18.738281 17.796875 C 21.832031 20.308594 22.996094 20.117188 28.804688 19.730469 L 83.582031 16.441406 C 84.746094 16.441406 83.78125 15.28125 83.390625 15.089844 L 74.292969 8.511719 C 72.550781 7.160156 70.230469 5.609375 65.777344 5.996094 L 12.734375 9.867188 C 10.800781 10.058594 10.414062 11.023438 11.183594 11.800781 Z M 22.027344 30.5625 L 22.027344 88.199219 C 22.027344 91.296875 23.574219 92.457031 27.058594 92.265625 L 87.261719 88.78125 C 90.746094 88.589844 91.136719 86.457031 91.136719 83.941406 L 91.136719 26.691406 C 91.136719 24.179688 90.167969 22.824219 88.035156 23.019531 L 25.125 26.691406 C 22.800781 26.886719 22.027344 28.046875 22.027344 30.5625 Z M 81.460938 33.652344 C 81.84375 35.398438 81.460938 37.136719 79.714844 37.332031 L 76.8125 37.910156 L 76.8125 80.460938 C 74.292969 81.816406 71.972656 82.589844 70.035156 82.589844 C 66.9375 82.589844 66.164062 81.621094 63.839844 78.722656 L 44.867188 48.9375 L 44.867188 77.753906 L 50.871094 79.109375 C 50.871094 79.109375 50.871094 82.589844 46.027344 82.589844 L 32.675781 83.363281 C 32.285156 82.589844 32.675781 80.65625 34.027344 80.269531 L 37.511719 79.304688 L 37.511719 41.199219 L 32.675781 40.8125 C 32.285156 39.070312 33.253906 36.558594 35.964844 36.363281 L 50.292969 35.398438 L 70.035156 65.570312 L 70.035156 38.878906 L 65.003906 38.300781 C 64.617188 36.171875 66.164062 34.625 68.097656 34.433594 Z M 8.28125 4.644531 L 63.453125 0.582031 C 70.230469 0 71.972656 0.390625 76.230469 3.484375 L 93.84375 15.859375 C 96.75 17.988281 97.71875 18.570312 97.71875 20.890625 L 97.71875 88.78125 C 97.71875 93.035156 96.167969 95.550781 90.75 95.9375 L 26.675781 99.804688 C 22.609375 100 20.671875 99.421875 18.542969 96.710938 L 5.574219 79.882812 C 3.25 76.785156 2.28125 74.46875 2.28125 71.757812 L 2.28125 11.410156 C 2.28125 7.933594 3.832031 5.03125 8.28125 4.644531 Z M 8.28125 4.644531 "/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
`
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addIcons = (): void => {
|
|
||||||
Object.keys(icons).forEach((key) => {
|
|
||||||
addIcon(key, icons[key]);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
188
main.ts
188
main.ts
@@ -1,188 +0,0 @@
|
|||||||
import { timingSafeEqual } from "crypto";
|
|
||||||
import {
|
|
||||||
App,
|
|
||||||
EditableFileView,
|
|
||||||
requestUrl,
|
|
||||||
Editor,
|
|
||||||
MarkdownView,
|
|
||||||
Modal,
|
|
||||||
Notice,
|
|
||||||
Plugin,
|
|
||||||
PluginSettingTab,
|
|
||||||
Setting,
|
|
||||||
} from "obsidian";
|
|
||||||
import { join } from "path";
|
|
||||||
import * as fs from "fs";
|
|
||||||
import {addIcons} from 'icon';
|
|
||||||
import { Upload2Notion } from "Upload2Notion";
|
|
||||||
|
|
||||||
// Remember to rename these classes and interfaces!
|
|
||||||
|
|
||||||
interface MyPluginSettings {
|
|
||||||
notionAPI: string;
|
|
||||||
databaseID: string;
|
|
||||||
proxy: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_SETTINGS: MyPluginSettings = {
|
|
||||||
notionAPI: "",
|
|
||||||
databaseID: "",
|
|
||||||
proxy: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class MyPlugin extends Plugin {
|
|
||||||
settings: MyPluginSettings;
|
|
||||||
|
|
||||||
async onload() {
|
|
||||||
await this.loadSettings();
|
|
||||||
addIcons();
|
|
||||||
// This creates an icon in the left ribbon.
|
|
||||||
const ribbonIconEl = this.addRibbonIcon(
|
|
||||||
"notion-logo",
|
|
||||||
"Share to notion",
|
|
||||||
async (evt: MouseEvent) => {
|
|
||||||
// Called when the user clicks the icon.
|
|
||||||
this.upload();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
// Perform additional things with the ribbon
|
|
||||||
ribbonIconEl.addClass("my-plugin-ribbon-class");
|
|
||||||
|
|
||||||
// This adds a status bar item to the bottom of the app. Does not work on mobile apps.
|
|
||||||
const statusBarItemEl = this.addStatusBarItem();
|
|
||||||
// statusBarItemEl.setText("share to notion");
|
|
||||||
|
|
||||||
this.addCommand({
|
|
||||||
id: "share-to-notion",
|
|
||||||
name: "share to notion",
|
|
||||||
editorCallback: async (editor: Editor, view: MarkdownView) => {
|
|
||||||
this.upload()
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// This adds a settings tab so the user can configure various aspects of the plugin
|
|
||||||
this.addSettingTab(new SampleSettingTab(this.app, this));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
onunload() {}
|
|
||||||
|
|
||||||
async upload(){
|
|
||||||
const { notionAPI, databaseID } = this.settings;
|
|
||||||
if (notionAPI === "" || databaseID === "") {
|
|
||||||
new Notice(
|
|
||||||
"Please set up the notion API and database ID in the settings tab."
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { nowFile, fileData,fullPath } =
|
|
||||||
await this.getNowFileMarkdwonContent(this.app);
|
|
||||||
const { basename } = nowFile;
|
|
||||||
if (fileData) {
|
|
||||||
const upload = new Upload2Notion(this);
|
|
||||||
const res = await upload.syncMarkdownToNotion(basename,fileData, fullPath)
|
|
||||||
console.log(res)
|
|
||||||
if(res.status === 200){
|
|
||||||
new Notice(`${basename} 同步成功`)
|
|
||||||
}else {
|
|
||||||
new Notice(`${basename} 同步失败`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getNowFileMarkdwonContent(app: App) {
|
|
||||||
const nowFile = app.workspace.getActiveFile();
|
|
||||||
if (nowFile) {
|
|
||||||
const filePath: string = nowFile.path;
|
|
||||||
const basePath: string = nowFile.vault.adapter.basePath;
|
|
||||||
const fullPath = join(basePath, filePath);
|
|
||||||
console.log("fullpath", fullPath);
|
|
||||||
const fileData = fs.readFileSync(fullPath, "utf8");
|
|
||||||
return {
|
|
||||||
fileData,
|
|
||||||
nowFile,
|
|
||||||
fullPath,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
new Notice("请打开需要同步的文件");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadSettings() {
|
|
||||||
this.settings = Object.assign(
|
|
||||||
{},
|
|
||||||
DEFAULT_SETTINGS,
|
|
||||||
await this.loadData()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveSettings() {
|
|
||||||
await this.saveData(this.settings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SampleModal extends Modal {
|
|
||||||
constructor(app: App) {
|
|
||||||
super(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
onOpen() {
|
|
||||||
const { contentEl } = this;
|
|
||||||
contentEl.setText("Woah!");
|
|
||||||
}
|
|
||||||
|
|
||||||
onClose() {
|
|
||||||
const { contentEl } = this;
|
|
||||||
contentEl.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SampleSettingTab extends PluginSettingTab {
|
|
||||||
plugin: MyPlugin;
|
|
||||||
|
|
||||||
constructor(app: App, plugin: MyPlugin) {
|
|
||||||
super(app, plugin);
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
display(): void {
|
|
||||||
const { containerEl } = this;
|
|
||||||
|
|
||||||
containerEl.empty();
|
|
||||||
|
|
||||||
containerEl.createEl("h2", {
|
|
||||||
text: "Settings for obsidian to notion plugin.",
|
|
||||||
});
|
|
||||||
|
|
||||||
new Setting(containerEl)
|
|
||||||
.setName("Notion API Token")
|
|
||||||
.setDesc("It's a secret")
|
|
||||||
.addText((text) =>
|
|
||||||
text
|
|
||||||
.setPlaceholder("Enter your Notion API Token")
|
|
||||||
.setValue(this.plugin.settings.notionAPI)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
console.log("Secret: " + value);
|
|
||||||
this.plugin.settings.notionAPI = value;
|
|
||||||
await this.plugin.saveSettings();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
new Setting(containerEl)
|
|
||||||
.setName("Database ID")
|
|
||||||
.setDesc("It's a secret")
|
|
||||||
.addText((text) =>
|
|
||||||
text
|
|
||||||
.setPlaceholder("Enter your Database ID")
|
|
||||||
.setValue(this.plugin.settings.databaseID)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
console.log("Secret: " + value);
|
|
||||||
this.plugin.settings.databaseID = value;
|
|
||||||
await this.plugin.saveSettings();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-to-notion",
|
"id": "share-to-notionnext",
|
||||||
"name": "Obsidian shared to Notion",
|
"name": "Share to NotionNext",
|
||||||
"version": "0.3.0",
|
"version": "2.3.5",
|
||||||
"minAppVersion": "0.0.1",
|
"minAppVersion": "0.0.1",
|
||||||
"description": "This is a plugin for Obsidian. This plugin share obsidian md file to notion with notion api",
|
"description": "Shares obsidian md file to notion with notion api for NotionNext web deploy, originally created by EasyChris/obsidian-to-notion.",
|
||||||
"author": "Easychris",
|
"author": "EasyChris, jxpeng98",
|
||||||
"authorUrl": "https://github.com/EasyChris/obsidian-to-notion",
|
"authorUrl": "https://github.com/jxpeng98",
|
||||||
"isDesktopOnly": false
|
"isDesktopOnly": false
|
||||||
}
|
}
|
||||||
26
obsidian_template.md
Normal file
26
obsidian_template.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
# default value has been set.
|
||||||
|
# for any unwanted value, you can delete it or set it to empty.
|
||||||
|
# for example, if you donot want to set password, you can delete password: "1234" or set it to password: ""
|
||||||
|
# 我已经在插件中设置了默认值,如果有不需要的选项,可以直接删除。
|
||||||
|
# 例如你不需要密码选项,你可以将password: "1234"删除,或者将它设置为空。
|
||||||
|
# !!!!!!!!!!!!
|
||||||
|
# 现在阶段一定不要修改表头的名字, please do not change the name of the header in YAML front matter
|
||||||
|
# !!!!!!!!!!!!
|
||||||
|
titleicon: 📎 # default is 📜, 默认是📜
|
||||||
|
date: 2023-07-23 # default is today, 默认是今天。 Format is YYYY-MM-DD, 格式是YYYY-MM-DD
|
||||||
|
coverurl: https://img.jxpeng.dev/2023/08/843e27a210847f05a0f7cfb121fec100.jpg # default is empty, 默认是空
|
||||||
|
type: Post # Post or Page, default is Post, 默认是Post
|
||||||
|
slug: test # slug for url, default is empty, 默认是空
|
||||||
|
status: Draft # Draft, Invisible, Published, default is Draft, 默认是Draft
|
||||||
|
category: test # default is 'Obsidian', 默认是'Obsidian'
|
||||||
|
summary: this is a summary for test post # default is empty, 默认是空
|
||||||
|
icon: fa-solid fa-camera # you can ignore this, default is empty, 默认是空,可直接删除
|
||||||
|
password: "1234" # if you donot want to set password, you can delete this line, default is empty, 默认是空,可直接删除
|
||||||
|
# 现在必须开启tags选项,否则会报错
|
||||||
|
tags:
|
||||||
|
- test # tags for post
|
||||||
|
- web # add more tags if you want
|
||||||
|
---
|
||||||
|
|
||||||
|
Contents Below
|
||||||
33
package.json
33
package.json
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-to-notion",
|
"name": "share-to-notionnext",
|
||||||
"version": "0.3.0",
|
"version": "2.3.5",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "This is a plugin for Obsidian. This plugin share obsidian md file to notion with notion api",
|
"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,20 +13,23 @@
|
|||||||
"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.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.2.0",
|
"@typescript-eslint/eslint-plugin": "^6.16.0",
|
||||||
"@typescript-eslint/parser": "^5.2.0",
|
"@typescript-eslint/parser": "^6.16.0",
|
||||||
"builtin-modules": "^3.2.0",
|
"builtin-modules": "^3.3.0",
|
||||||
"esbuild": "0.13.12",
|
"esbuild": "0.19.5",
|
||||||
"obsidian": "latest",
|
"obsidian": "^1.6.6",
|
||||||
"tslib": "2.3.1",
|
"tslib": "2.6.2",
|
||||||
"typescript": "4.4.4"
|
"typescript": "5.2.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tryfabric/martian": "^1.2.0",
|
"@jxpeng98/martian": "^1.2.7",
|
||||||
"https-proxy-agent": "^5.0.1",
|
"https-proxy-agent": "^7.0.2",
|
||||||
"yaml": "^2.1.0",
|
"node-fetch": "^3.3.2",
|
||||||
|
"process": "^0.11.10",
|
||||||
|
"remark-math": "^6.0.0",
|
||||||
|
"yaml": "^2.3.4",
|
||||||
"yaml-front-matter": "^4.1.1"
|
"yaml-front-matter": "^4.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
6
release.sh
Executable file
6
release.sh
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
node update-version.js $1
|
||||||
|
git add .
|
||||||
|
git commit -m "Update version to $1"
|
||||||
|
git tag -a $1 -m $1
|
||||||
|
git push origin $1
|
||||||
51
src/commands/FuzzySuggester.ts
Normal file
51
src/commands/FuzzySuggester.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { FuzzySuggestModal, FuzzyMatch } from 'obsidian';
|
||||||
|
import MyPlugin from "../main";
|
||||||
|
import { i18nConfig } from "../lang/I18n";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple interface for what should be displayed and stored for suggester
|
||||||
|
*/
|
||||||
|
export interface DatabaseList {
|
||||||
|
name: string, // specific database name
|
||||||
|
match: any //
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class FuzzySuggester extends FuzzySuggestModal<DatabaseList> {
|
||||||
|
private plugin: MyPlugin;
|
||||||
|
private data: DatabaseList[];
|
||||||
|
private callback: any;
|
||||||
|
|
||||||
|
constructor(plugin: MyPlugin) {
|
||||||
|
super(plugin.app);
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.setPlaceholder(i18nConfig.PlaceHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSuggesterData(suggesterData: Array<DatabaseList>): void { this.data = suggesterData }
|
||||||
|
|
||||||
|
async display(callBack: (item: DatabaseList, evt: MouseEvent | KeyboardEvent) => void): Promise<any> {
|
||||||
|
this.callback = callBack;
|
||||||
|
this.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the data
|
||||||
|
getItems(): DatabaseList[] {
|
||||||
|
return this.data
|
||||||
|
}
|
||||||
|
|
||||||
|
getItemText(item: DatabaseList): string {
|
||||||
|
return item.name
|
||||||
|
}
|
||||||
|
|
||||||
|
onChooseItem(item: DatabaseList, evt: MouseEvent | KeyboardEvent): void { }
|
||||||
|
|
||||||
|
onChooseSuggestion(item: FuzzyMatch<DatabaseList>, evt: MouseEvent | KeyboardEvent): void {
|
||||||
|
this.callback(item.item, evt)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSuggestion(item: FuzzyMatch<DatabaseList>, el: HTMLElement): void {
|
||||||
|
el.createEl('div', { text: item.item.name })
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
107
src/commands/NotionCommands.ts
Normal file
107
src/commands/NotionCommands.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import { i18nConfig } from "src/lang/I18n";
|
||||||
|
import { Editor, MarkdownView, setTooltip } from "obsidian";
|
||||||
|
import { FuzzySuggester, DatabaseList } from "./FuzzySuggester";
|
||||||
|
import { uploadCommandCustom, uploadCommandGeneral, uploadCommandNext } from "../upload/uploadCommand";
|
||||||
|
import ObsidianSyncNotionPlugin from "src/main";
|
||||||
|
import { DatabaseDetails } from "../ui/settingTabs";
|
||||||
|
|
||||||
|
|
||||||
|
interface Command {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
editorCallback: (editor: Editor, view: MarkdownView) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// create the commands list
|
||||||
|
export default class RibbonCommands {
|
||||||
|
plugin: ObsidianSyncNotionPlugin;
|
||||||
|
|
||||||
|
Ncommand: Command[] = [];
|
||||||
|
|
||||||
|
constructor(plugin: ObsidianSyncNotionPlugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
|
||||||
|
// iterate through the database detail
|
||||||
|
|
||||||
|
for (let key in this.plugin.settings.databaseDetails) {
|
||||||
|
let dbDetails = this.plugin.settings.databaseDetails[key];
|
||||||
|
this.addCommandForDatabase(dbDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register all the commands
|
||||||
|
this.Ncommand.forEach(command => {
|
||||||
|
this.plugin.addCommand(
|
||||||
|
{
|
||||||
|
id: command.id,
|
||||||
|
name: command.name,
|
||||||
|
editorCallback: command.editorCallback,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async ribbonDisplay() {
|
||||||
|
const NcommandList: DatabaseList[] = [];
|
||||||
|
|
||||||
|
this.Ncommand.map(command => NcommandList.push(
|
||||||
|
{
|
||||||
|
name: command.name,
|
||||||
|
match: command.editorCallback
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const fusg = new FuzzySuggester(this.plugin);
|
||||||
|
|
||||||
|
fusg.setSuggesterData(NcommandList);
|
||||||
|
await fusg.display(async (results) => { await results.match() })
|
||||||
|
};
|
||||||
|
|
||||||
|
// if the setting has been changed, try to rebuild the command list
|
||||||
|
async updateCommand() {
|
||||||
|
|
||||||
|
this.Ncommand = [];
|
||||||
|
|
||||||
|
for (let key in this.plugin.settings.databaseDetails) {
|
||||||
|
let dbDetails = this.plugin.settings.databaseDetails[key];
|
||||||
|
this.addCommandForDatabase(dbDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Ncommand.forEach(command => {
|
||||||
|
this.plugin.addCommand(
|
||||||
|
{
|
||||||
|
id: command.id,
|
||||||
|
name: command.name,
|
||||||
|
editorCallback: command.editorCallback,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private addCommandForDatabase(dbDetails: DatabaseDetails) {
|
||||||
|
// Example logic - adjust based on your specific requirements
|
||||||
|
let commandId = `share-to-${dbDetails.abName}`;
|
||||||
|
let commandName = `Share to ${dbDetails.fullName} (${dbDetails.abName})`; // or use a translated name
|
||||||
|
|
||||||
|
let editorCallback: (editor: Editor, view: MarkdownView) => Promise<void>;
|
||||||
|
if (dbDetails.format === 'next') {
|
||||||
|
editorCallback = async (editor, view) => {
|
||||||
|
await uploadCommandNext(this.plugin, this.plugin.settings, dbDetails, this.plugin.app);
|
||||||
|
};
|
||||||
|
} else if (dbDetails.format === 'general') {
|
||||||
|
editorCallback = async (editor, view) => {
|
||||||
|
await uploadCommandGeneral(this.plugin, this.plugin.settings, dbDetails, this.plugin.app);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (dbDetails.format === 'custom') {
|
||||||
|
editorCallback = async (editor, view) => {
|
||||||
|
await uploadCommandCustom(this.plugin, this.plugin.settings, dbDetails, this.plugin.app);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Ncommand.push({ id: commandId, name: commandName, editorCallback });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
17
src/lang/I18n.ts
Normal file
17
src/lang/I18n.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import {en} from "./locale/en";
|
||||||
|
import {zh} from "./locale/zh";
|
||||||
|
import {ja} from "./locale/ja";
|
||||||
|
|
||||||
|
export const I18n: {[key: string]:any} = {
|
||||||
|
en,
|
||||||
|
zh,
|
||||||
|
ja,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const I18nConfig = (lang: string): any => {
|
||||||
|
return I18n[lang];
|
||||||
|
};
|
||||||
|
|
||||||
|
const storedLanguage = window.localStorage.getItem("language");
|
||||||
|
|
||||||
|
export const i18nConfig = I18n[storedLanguage || window.navigator.language.split('-')[0] || "en"];
|
||||||
79
src/lang/locale/en.ts
Normal file
79
src/lang/locale/en.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
export const en = {
|
||||||
|
databaseFormat: "Database Format",
|
||||||
|
databaseFormatDesc: "Select the database format you want to sync to NotionNext or General",
|
||||||
|
databaseNext: "NotionNext",
|
||||||
|
databaseGeneral: "General",
|
||||||
|
databaseCustom: "Custom",
|
||||||
|
databaseFullName: "Database Full Name",
|
||||||
|
databaseFullNameDesc: "Please give a full name for your database",
|
||||||
|
databaseFullNameText: "Enter your database full name",
|
||||||
|
databaseAbbreviateName: "Database Abbreviate Name",
|
||||||
|
databaseAbbreviateNameDesc: "Please give a nick name for your database",
|
||||||
|
databaseAbbreviateNameText: "Enter your database nick name",
|
||||||
|
ribbonIcon: "Share to NotionNext",
|
||||||
|
GeneralSetting: "General information Settings",
|
||||||
|
CommandID: "share-to-notionnext",
|
||||||
|
CommandName: "Share to NotionNext Database",
|
||||||
|
CommandIDGeneral: "share-to-notion",
|
||||||
|
CommandNameGeneral: "Share to Notion General Database",
|
||||||
|
NotionNextButton: "NotionNext Sync",
|
||||||
|
NotionNextButtonDesc: "Open this option, Sync to NotionNext command will be displayed in the command palette (default: ON)",
|
||||||
|
NotionNextSettingHeader: "NotionNext Database Settings",
|
||||||
|
NotionAPI: "Notion API Token",
|
||||||
|
NotionAPIDesc: "Generate from https://www.notion.so/my-integrations",
|
||||||
|
NotionAPIText: "Enter your Notion API Token",
|
||||||
|
DatabaseID: "Database ID",
|
||||||
|
DatabaseIDDesc: "Collect from the top-right Share --> Publish",
|
||||||
|
DatabaseIDText: "Enter your Database ID",
|
||||||
|
BannerUrl: "Banner url (optional)",
|
||||||
|
BannerUrlDesc:
|
||||||
|
"Default is empty, if you want to show a banner, please enter the url (like: https://abc.com/b.png)",
|
||||||
|
BannerUrlText: "Enter your banner url",
|
||||||
|
NotionUser: "Notion ID (username, optional)",
|
||||||
|
NotionUserDesc:
|
||||||
|
"Collect from share link likes:https://username.notion.site. Your notion id is [username]",
|
||||||
|
NotionUserText: "Enter your notion ID",
|
||||||
|
NotionLinkDisplay: "Notion Link Display",
|
||||||
|
NotionLinkDisplayDesc: "Default is ON, if you want to hide the link in the front matter, please turn it off",
|
||||||
|
NotionGeneralSettingHeader: "General Notion Database Settings",
|
||||||
|
NotionGeneralButton: "Notion General Sync",
|
||||||
|
NotionGeneralButtonDesc: "Open this option, Sync to Notion General Database command will be displayed in the command palette (default: ON)",
|
||||||
|
NotionTagButton: "Notion Tags Sync",
|
||||||
|
NotionTagButtonDesc: "Sync Tags to Notion General Database (default: ON)",
|
||||||
|
NotionCustomTitle: "Customise title property",
|
||||||
|
NotionCustomTitleDesc: "Modify the column name of the Notion database (default: OFF)",
|
||||||
|
NotionCustomTitleName: "Preferred title name",
|
||||||
|
NotionCustomTitleNameDesc: "Enter the preferred title name for the first column of the Notion database (default: title)",
|
||||||
|
NotionCustomTitleText: "Enter the name",
|
||||||
|
NotionCustomValues: "Customise values property",
|
||||||
|
NotionCustomValuesDesc: "Modify the column name of the Notion database,one per line",
|
||||||
|
NotionCustomValuesText: "Enter all properties that you want to sync",
|
||||||
|
NotYetFinish: "Not finished. This function will be available in the next version",
|
||||||
|
PlaceHolder: "Enter database Name",
|
||||||
|
"notion-logo": "Share to NotionNext",
|
||||||
|
"sync-preffix": "Sync to ",
|
||||||
|
"sync-success": "success",
|
||||||
|
"sync-fail": "failed",
|
||||||
|
"open-notion": "Please open the file that needs to be synchronized",
|
||||||
|
"config-secrets-notion-api":
|
||||||
|
"Please set up the notion API in the settings tab.",
|
||||||
|
"config-secrets-database-id":
|
||||||
|
"Please set up the database id in the settings tab.",
|
||||||
|
"set-tags-fail":
|
||||||
|
"Set tags fail,please check the frontmatter of the file or close the tag switch in the settings tab.",
|
||||||
|
NNonMissing:
|
||||||
|
"The 'NNon' property is missing in the settings. Please set it up.",
|
||||||
|
"set-api-id":
|
||||||
|
"Please set up the notion API and database ID in the settings tab.",
|
||||||
|
NotionCustomSettingHeader: "Notion Custom Database Settings",
|
||||||
|
NotionCustomButton: "Notion Customised command switch",
|
||||||
|
NotionCustomButtonDesc: "Open this option, Sync to Notion Customised Database command will be displayed in the command palette",
|
||||||
|
CustomPropertyName: "Property Name",
|
||||||
|
CustomPropertyFirstColumn: "Title Column",
|
||||||
|
CustomPropertyFirstColumnDesc: "The title of the page, must be the first property",
|
||||||
|
CustomProperty: "Property",
|
||||||
|
AddCustomProperty: "Add Custom Property",
|
||||||
|
AddNewProperty: "Add New Property",
|
||||||
|
AddNewPropertyDesc: "Add new property match with your notion database",
|
||||||
|
CopyErrorMessage: "Auto copy failed, please copy it manually",
|
||||||
|
}
|
||||||
71
src/lang/locale/ja.ts
Normal file
71
src/lang/locale/ja.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
export const ja = {
|
||||||
|
databaseFormat: "データベース形式",
|
||||||
|
databaseFormatDesc: "同期したいデータベース形式を選択してください",
|
||||||
|
databaseNext: "NotionNext",
|
||||||
|
databaseGeneral: "一般的なNotion",
|
||||||
|
databaseCustom: "カスタム",
|
||||||
|
databaseFullName: "データベースの全称",
|
||||||
|
databaseFullNameDesc: "データベースの全称を入力してください",
|
||||||
|
databaseFullNameText: "データベースの全称を入力",
|
||||||
|
databaseAbbreviateName: "データベースの略称",
|
||||||
|
databaseAbbreviateNameDesc: "データベースの略称を入力してください",
|
||||||
|
databaseAbbreviateNameText: "データベースの略称を入力",
|
||||||
|
ribbonIcon: "NotionNextで共有",
|
||||||
|
GeneralSetting: "一般設定",
|
||||||
|
CommandID: "share-to-notionnext",
|
||||||
|
CommandName: "NotionNextデータベースに共有",
|
||||||
|
CommandIDGeneral: "share-to-notion",
|
||||||
|
CommandNameGeneral: "一般的なNotionデータベースに共有",
|
||||||
|
NotionNextButton: "NotionNext同期",
|
||||||
|
NotionNextButtonDesc: "このオプションを開くと、NotionNext同期コマンドがコマンドパレットに表示されます(デフォルト:ON)",
|
||||||
|
NotionNextSettingHeader: "NotionNextデータベース設定",
|
||||||
|
NotionAPI: "Notion API トークン",
|
||||||
|
NotionAPIDesc: "https://www.notion.so/my-integrations から生成してください",
|
||||||
|
NotionAPIText: "Notion API トークンを入力",
|
||||||
|
DatabaseID: "データベースID",
|
||||||
|
DatabaseIDDesc: "右上の共有 --> 公開から取得してください",
|
||||||
|
DatabaseIDText: "データベースIDを入力",
|
||||||
|
BannerUrl: "バナーのURL(任意)",
|
||||||
|
BannerUrlDesc: "デフォルトは空白です。バナーを表示したい場合は、URLを入力してください(例:https://abc.com/b.png)",
|
||||||
|
BannerUrlText: "バナーのURLを入力",
|
||||||
|
NotionUser: "Notion ID(ユーザー名、任意)",
|
||||||
|
NotionUserDesc: "共有リンクから取得(例:https://username.notion.site)。Notion IDは[username]です",
|
||||||
|
NotionUserText: "Notion IDを入力",
|
||||||
|
NotionLinkDisplay: "Notionリンク表示",
|
||||||
|
NotionLinkDisplayDesc: "デフォルトはONです。front matterにリンクを非表示にしたい場合は、オフにしてください",
|
||||||
|
NotionGeneralSettingHeader: "一般的なNotionデータベース設定",
|
||||||
|
NotionGeneralButton: "一般的なNotion同期",
|
||||||
|
NotionGeneralButtonDesc: "このオプションを開くと、一般的なNotionデータベース同期コマンドがコマンドパレットに表示されます(デフォルト:ON)",
|
||||||
|
NotionTagButton: "Notionタグ同期",
|
||||||
|
NotionTagButtonDesc: "タグを一般的なNotionデータベースに同期(デフォルト:ON)",
|
||||||
|
NotionCustomTitle: "タイトルのカスタマイズ",
|
||||||
|
NotionCustomTitleDesc: "Notionデータベースの列名を変更(デフォルト:OFF)",
|
||||||
|
NotionCustomTitleName: "希望のタイトル名",
|
||||||
|
NotionCustomTitleNameDesc: "Notionデータベースの最初の列のための希望のタイトル名を入力(デフォルト:title)",
|
||||||
|
NotionCustomTitleText: "名前を入力",
|
||||||
|
NotionCustomValues: "値のカスタマイズ",
|
||||||
|
NotionCustomValuesDesc: "Notionデータベースの列名を変更、1行に1つ",
|
||||||
|
NotionCustomValuesText: "同期したいすべてのプロパティを入力",
|
||||||
|
NotYetFinish: "未完了。この機能は次のバージョンで利用可能になります",
|
||||||
|
PlaceHolder: "データベース名を入力",
|
||||||
|
"notion-logo": "NotionNextで共有",
|
||||||
|
"sync-success": "NotionNextへの同期に成功:\n",
|
||||||
|
"sync-fail": "NotionNextへの同期に失敗:\n",
|
||||||
|
"open-notion": "同期が必要なファイルを開いてください",
|
||||||
|
"config-secrets-notion-api": "設定タブでNotion APIを設定してください",
|
||||||
|
"config-secrets-database-id": "設定タブでデータベースIDを設定してください",
|
||||||
|
"set-tags-fail": "タグの設定に失敗。ファイルのfrontmatterを確認するか、設定タブでタグのスイッチをオフにしてください",
|
||||||
|
NNonMissing: "設定に 'NNon' プロパティがありません。設定してください",
|
||||||
|
"set-api-id": "設定タブでNotion APIおよびデータベースIDを設定してください",
|
||||||
|
NotionCustomSettingHeader: "Notionカスタムデータベース設定",
|
||||||
|
NotionCustomButton: "Notionカスタマイズコマンドの切り替え",
|
||||||
|
NotionCustomButtonDesc: "このオプションを開くと、Notionカスタムデータベース同期コマンドがコマンドパレットに表示されます",
|
||||||
|
CustomPropertyName: "カスタムプロパティ名",
|
||||||
|
CustomPropertyFirstColumn: "最初の列のカスタムプロパティ名",
|
||||||
|
CustomPropertyFirstColumnDesc: "最初の列のカスタムプロパティ名を入力してください",
|
||||||
|
CustomProperty: "カスタムプロパティ",
|
||||||
|
AddCustomProperty: "カスタムプロパティを追加",
|
||||||
|
AddNewProperty: "新しいプロパティを追加",
|
||||||
|
AddNewPropertyDesc: "新しいプロパティを追加してください",
|
||||||
|
CopyErrorMessage: "自動コピーに失敗しました",
|
||||||
|
};
|
||||||
74
src/lang/locale/zh.ts
Normal file
74
src/lang/locale/zh.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
export const zh = {
|
||||||
|
databaseFormat: "数据库格式",
|
||||||
|
databaseFormatDesc: "选择你想要同步的数据库格式Next 或者 普通",
|
||||||
|
databaseNext: "NotionNext",
|
||||||
|
databaseGeneral: "普通",
|
||||||
|
databaseCustom: "自定义",
|
||||||
|
databaseFullName: "数据库全称",
|
||||||
|
databaseFullNameDesc: "给你的数据库起一个全称",
|
||||||
|
databaseFullNameText: "输入你的数据库全称",
|
||||||
|
databaseAbbreviateName: "数据库简称",
|
||||||
|
databaseAbbreviateNameDesc: "给你的数据库起一个简称",
|
||||||
|
databaseAbbreviateNameText: "输入你的数据库简称",
|
||||||
|
ribbonIcon: "分享到 NotionNext",
|
||||||
|
GeneralSetting: "通用设置",
|
||||||
|
CommandID: "share-to-notionnext",
|
||||||
|
CommandName: "分享到 NotionNext",
|
||||||
|
CommandIDGeneral: "share-to-notion",
|
||||||
|
CommandNameGeneral: "分享到 Notion 普通数据库",
|
||||||
|
NotionNextButton: "NotionNext 同步",
|
||||||
|
NotionNextButtonDesc: "打开此选项,NotionNext 同步将显示在命令面板中(默认:开)",
|
||||||
|
NotionNextSettingHeader: "NotionNext 数据库参数设置",
|
||||||
|
NotionAPI: "Notion API 令牌",
|
||||||
|
NotionAPIDesc: "从 https://www.notion.so/my-integrations 生成",
|
||||||
|
NotionAPIText: "输入你的 Notion API 令牌",
|
||||||
|
DatabaseID: "数据库 ID",
|
||||||
|
DatabaseIDDesc: "从右上角的分享 --> 发布中获取",
|
||||||
|
DatabaseIDText: "输入你的数据库 ID",
|
||||||
|
BannerUrl: "封面图片地址(可选)",
|
||||||
|
BannerUrlDesc:
|
||||||
|
"默认为空,如果你想显示封面图片,请输入图片地址(例如:https://abc.com/b.png)",
|
||||||
|
BannerUrlText: "输入你的封面图片地址",
|
||||||
|
NotionUser: "Notion ID(用户名,可选)",
|
||||||
|
NotionUserDesc:
|
||||||
|
"数据库分享链接类似:https://username.notion.site/。你的 Notion ID 是 [username]",
|
||||||
|
NotionUserText: "输入你的 Notion ID",
|
||||||
|
NotionLinkDisplay: "Notion 链接显示",
|
||||||
|
NotionLinkDisplayDesc: "默认开启,如果你不想在front matter中显示链接,请关闭",
|
||||||
|
NotionGeneralSettingHeader: "普通 Notion 数据库设置",
|
||||||
|
NotionGeneralButton: "普通数据库同步",
|
||||||
|
NotionGeneralButtonDesc: "打开此选项,同步到普通数据库命令将显示在命令面板中(默认:开)",
|
||||||
|
NotionTagButton: "标签同步开关",
|
||||||
|
NotionTagButtonDesc: "将标签同步到普通数据库(默认:开)",
|
||||||
|
NotionCustomTitle: "修改 Notion 数据库表头开关",
|
||||||
|
NotionCustomTitleDesc: "自定义Notion 数据库第一列表头名(默认:关)",
|
||||||
|
NotionCustomTitleName: "想要修改的表头名",
|
||||||
|
NotionCustomTitleNameDesc: "输入你想要修改的notion数据库的表头名(默认:title)",
|
||||||
|
NotionCustomTitleText: "输入表头名",
|
||||||
|
NotionCustomValues: "自定义Notion 数据库表头",
|
||||||
|
NotionCustomValuesDesc: "自定义Notion 数据库表头,每行一个",
|
||||||
|
NotionCustomValuesText: "输入你想要同步的所有属性",
|
||||||
|
NotYetFinish: "未完成。此功能将在之后版本中提供",
|
||||||
|
PlaceHolder: "输入数据库名称",
|
||||||
|
"notion-logo": "分享到NotionNext",
|
||||||
|
"sync-success": "同步到NotionNext成功:\n",
|
||||||
|
"sync-fail": "同步到NotionNext失败: \n",
|
||||||
|
"open-file": "请打开需要同步的文件",
|
||||||
|
"config-secrets-notion-api": "请在插件设置中添加notion API",
|
||||||
|
"config-secrets-database-id": "请在插件设置中添加database id",
|
||||||
|
"set-tags-fail":
|
||||||
|
"设置标签失败,请检查文件的frontmatter,或者在插件设置中关闭设置tags开关",
|
||||||
|
NNonMissing: "未设置'NNon'属性,请在插件设置中选择NotionNext数据库。",
|
||||||
|
"set-api-id": "请在插件设置中设置notion API和database ID",
|
||||||
|
NotionCustomSettingHeader: "Notion 自定义数据库设置",
|
||||||
|
NotionCustomButton: "Notion 自定义数据库同步命令开关",
|
||||||
|
NotionCustomButtonDesc: "打开此选项,同步到自定义数据库命令将显示在命令面板中",
|
||||||
|
CustomPropertyName: "自定义属性名",
|
||||||
|
CustomPropertyFirstColumn: "第一列属性名",
|
||||||
|
CustomPropertyFirstColumnDesc: "第一列必须为标题属性名",
|
||||||
|
CustomProperty: "自定义属性",
|
||||||
|
AddCustomProperty: "添加自定义属性",
|
||||||
|
AddNewProperty: "添加新属性",
|
||||||
|
AddNewPropertyDesc: "添加一个和Notion数据库匹配的新属性",
|
||||||
|
CopyErrorMessage: "复制链接失败,请手动复制",
|
||||||
|
}
|
||||||
89
src/main.ts
Normal file
89
src/main.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
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";
|
||||||
|
|
||||||
|
// Remember to rename these classes and interfaces!
|
||||||
|
|
||||||
|
|
||||||
|
export default class ObsidianSyncNotionPlugin extends Plugin {
|
||||||
|
settings: PluginSettings;
|
||||||
|
commands: ribbonCommands;
|
||||||
|
app: App;
|
||||||
|
|
||||||
|
async onload() {
|
||||||
|
await this.loadSettings();
|
||||||
|
this.commands = new ribbonCommands(this);
|
||||||
|
|
||||||
|
addIcons();
|
||||||
|
// This creates an icon in the left ribbon.
|
||||||
|
const ribbonIconEl = this.addRibbonIcon(
|
||||||
|
"notion-logo",
|
||||||
|
i18nConfig.ribbonIcon,
|
||||||
|
async (evt: MouseEvent) => {
|
||||||
|
// Called when the user clicks the icon.
|
||||||
|
// await this.uploadCommand();
|
||||||
|
await this.commands.ribbonDisplay();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// This adds a status bar item to the bottom of the app. Does not work on mobile apps.
|
||||||
|
|
||||||
|
// const statusBarItemEl = this.addStatusBarItem();
|
||||||
|
// // statusBarItemEl.setText("share to notion");
|
||||||
|
|
||||||
|
// This adds a settings tab so the user can configure various aspects of the plugin
|
||||||
|
this.addSettingTab(new ObsidianSettingTab(this.app, this));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onunload() {
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadSettings() {
|
||||||
|
this.settings = Object.assign(
|
||||||
|
{},
|
||||||
|
DEFAULT_SETTINGS,
|
||||||
|
await this.loadData()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveSettings() {
|
||||||
|
await this.saveData(this.settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
async addDatabaseDetails(dbDetails: DatabaseDetails) {
|
||||||
|
this.settings.databaseDetails = {
|
||||||
|
...this.settings.databaseDetails,
|
||||||
|
[dbDetails.abName]: dbDetails,
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteDatabaseDetails(dbDetails: DatabaseDetails) {
|
||||||
|
delete this.settings.databaseDetails[dbDetails.abName];
|
||||||
|
|
||||||
|
await this.saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateDatabaseDetails(dbDetails: DatabaseDetails) {
|
||||||
|
// delete the old database details
|
||||||
|
delete this.settings.databaseDetails[dbDetails.abName];
|
||||||
|
|
||||||
|
this.settings.databaseDetails = {
|
||||||
|
...this.settings.databaseDetails,
|
||||||
|
[dbDetails.abName]: dbDetails,
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
64
src/ui/DeleteModal.ts
Normal file
64
src/ui/DeleteModal.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { App, ButtonComponent, Modal, Setting } from "obsidian";
|
||||||
|
import { DatabaseDetails, ObsidianSettingTab } from "./settingTabs";
|
||||||
|
import ObsidianSyncNotionPlugin from "../main";
|
||||||
|
|
||||||
|
export class DeleteModal extends Modal {
|
||||||
|
data: Record<string, any> = {
|
||||||
|
deleted: false,
|
||||||
|
}
|
||||||
|
plugin: ObsidianSyncNotionPlugin;
|
||||||
|
settingTab: ObsidianSettingTab;
|
||||||
|
dbDetails: DatabaseDetails
|
||||||
|
|
||||||
|
constructor(app: App, plugin: ObsidianSyncNotionPlugin, settingTab: ObsidianSettingTab, dbDetails: DatabaseDetails) {
|
||||||
|
super(app);
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.settingTab = settingTab;
|
||||||
|
this.dbDetails = dbDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
display() {
|
||||||
|
this.containerEl.addClass("delete-modal");
|
||||||
|
this.titleEl.setText('Delete Database');
|
||||||
|
|
||||||
|
let { contentEl } = this;
|
||||||
|
contentEl.empty();
|
||||||
|
|
||||||
|
const deleteDiv = contentEl.createDiv('delete-div');
|
||||||
|
deleteDiv.createEl('h4', { text: 'Are you sure you want to delete the following database?' });
|
||||||
|
deleteDiv.createEl('h2', { text: this.dbDetails.fullName + ' (' + this.dbDetails.abName + ', ' + this.dbDetails.format + ')' });
|
||||||
|
|
||||||
|
|
||||||
|
// add delete button
|
||||||
|
let footerEl = contentEl.createDiv('save-button');
|
||||||
|
let deleteButton = new Setting(footerEl)
|
||||||
|
|
||||||
|
deleteButton
|
||||||
|
.addButton((button: ButtonComponent): ButtonComponent => {
|
||||||
|
return button
|
||||||
|
.setTooltip("Delete")
|
||||||
|
.setIcon("trash")
|
||||||
|
.onClick(async () => {
|
||||||
|
this.data.deleted = true;
|
||||||
|
this.close();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
deleteButton.addExtraButton((button) => {
|
||||||
|
return button
|
||||||
|
.setTooltip('Cancel')
|
||||||
|
.setIcon('cross')
|
||||||
|
.onClick(() => {
|
||||||
|
this.data.deleted = false;
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onOpen() {
|
||||||
|
this.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
349
src/ui/EditModal.ts
Normal file
349
src/ui/EditModal.ts
Normal file
@@ -0,0 +1,349 @@
|
|||||||
|
import { App, ButtonComponent, Setting } from "obsidian";
|
||||||
|
import { customProperty, SettingModal } from "./settingModal";
|
||||||
|
import ObsidianSyncNotionPlugin from "../main";
|
||||||
|
import { DatabaseDetails, ObsidianSettingTab } from "./settingTabs";
|
||||||
|
import { i18nConfig } from "../lang/I18n";
|
||||||
|
|
||||||
|
export class EditModal extends SettingModal {
|
||||||
|
propertyLines: Setting[] = []; // Store all property line settings
|
||||||
|
[key: string]: any; // Index signature
|
||||||
|
dataTemp: Record<string, any> = {
|
||||||
|
databaseFormatTemp: '',
|
||||||
|
// databaseFormatTempInd: false,
|
||||||
|
databaseFullNameTemp: '',
|
||||||
|
// databaseFullNameTempInd: false,
|
||||||
|
databaseAbbreviateNameTemp: '',
|
||||||
|
// databaseAbbreviateNameTempInd: false,
|
||||||
|
notionAPITemp: '',
|
||||||
|
// notionAPITempInd: false,
|
||||||
|
databaseIDTemp: '',
|
||||||
|
// databaseIDTempInd: false,
|
||||||
|
tagButtonTemp: false,
|
||||||
|
// tagButtonTempInd: false,
|
||||||
|
customTitleButtonTemp: false,
|
||||||
|
// customTitleButtonTempInd: false,
|
||||||
|
customTitleNameTemp: '',
|
||||||
|
customPropertiesTemp: [],
|
||||||
|
// customTitleNameTempInd: false,
|
||||||
|
// customValues: '',
|
||||||
|
savedTemp: false,
|
||||||
|
savedTempInd: false,
|
||||||
|
};
|
||||||
|
dataPrev: Record<string, any> = {
|
||||||
|
databaseFormatPrev: '',
|
||||||
|
// databaseFormatPrevInd: false,
|
||||||
|
databaseFullNamePrev: '',
|
||||||
|
// databaseFullNamePrevInd: false,
|
||||||
|
databaseAbbreviateNamePrev: '',
|
||||||
|
// databaseAbbreviateNamePrevInd: false,
|
||||||
|
notionAPIPrev: '',
|
||||||
|
// notionAPIPrevInd: false,
|
||||||
|
databaseIDPrev: '',
|
||||||
|
// databaseIDPrevInd: false,
|
||||||
|
tagButtonPrev: false,
|
||||||
|
// tagButtonPrevInd: false,
|
||||||
|
customTitleButtonPrev: false,
|
||||||
|
// customTitleButtonPrevInd: false,
|
||||||
|
customTitleNamePrev: '',
|
||||||
|
customPropertiesPrev: [],
|
||||||
|
// customTitleNamePrevInd: false,
|
||||||
|
// customValues: '',
|
||||||
|
savedPrev: false,
|
||||||
|
savedPrevInd: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
plugin: ObsidianSyncNotionPlugin;
|
||||||
|
settingTab: ObsidianSettingTab;
|
||||||
|
dbDetails: DatabaseDetails;
|
||||||
|
|
||||||
|
constructor(app: App, plugin: ObsidianSyncNotionPlugin, settingTab: ObsidianSettingTab, dbDetails: DatabaseDetails) {
|
||||||
|
super(app, plugin, settingTab);
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.settingTab = settingTab;
|
||||||
|
if (dbDetails) {
|
||||||
|
// Temp details
|
||||||
|
this.dataTemp.databaseFormatTemp = dbDetails.format;
|
||||||
|
this.dataTemp.databaseFullNameTemp = dbDetails.fullName;
|
||||||
|
this.dataTemp.databaseAbbreviateNameTemp = dbDetails.abName;
|
||||||
|
this.dataTemp.notionAPITemp = dbDetails.notionAPI;
|
||||||
|
this.dataTemp.databaseIDTemp = dbDetails.databaseID;
|
||||||
|
this.dataTemp.tagButtonTemp = dbDetails.tagButton;
|
||||||
|
this.dataTemp.customTitleButtonTemp = dbDetails.customTitleButton;
|
||||||
|
this.dataTemp.customTitleNameTemp = dbDetails.customTitleName;
|
||||||
|
this.dataTemp.customPropertiesTemp = dbDetails.customProperties.map(prop => ({ ...prop })); // Ensure deep copy
|
||||||
|
// this.dataTemp.customValues = dbDetails.customValues;
|
||||||
|
this.dataTemp.savedTemp = dbDetails.saved;
|
||||||
|
|
||||||
|
// Prev details
|
||||||
|
this.dataPrev.databaseFormatPrev = dbDetails.format;
|
||||||
|
this.dataPrev.databaseFullNamePrev = dbDetails.fullName;
|
||||||
|
this.dataPrev.databaseAbbreviateNamePrev = dbDetails.abName;
|
||||||
|
this.dataPrev.notionAPIPrev = dbDetails.notionAPI;
|
||||||
|
this.dataPrev.databaseIDPrev = dbDetails.databaseID;
|
||||||
|
this.dataPrev.tagButtonPrev = dbDetails.tagButton;
|
||||||
|
this.dataPrev.customTitleButtonPrev = dbDetails.customTitleButton;
|
||||||
|
this.dataPrev.customTitleNamePrev = dbDetails.customTitleName;
|
||||||
|
this.dataPrev.customPropertiesPrev = dbDetails.customProperties.map(prop => ({ ...prop })); // Ensure deep copy
|
||||||
|
// this.dataTemp.customValues = dbDetails.customValues;
|
||||||
|
this.dataPrev.savedPrev = dbDetails.saved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
display(): void {
|
||||||
|
this.containerEl.addClass("edit-modal");
|
||||||
|
this.titleEl.setText('Edit Database');
|
||||||
|
|
||||||
|
let { contentEl } = this;
|
||||||
|
contentEl.empty();
|
||||||
|
|
||||||
|
const editDiv = contentEl.createDiv('edit-div');
|
||||||
|
const nextTabs = contentEl.createDiv('next-tabs');
|
||||||
|
|
||||||
|
|
||||||
|
new Setting(editDiv)
|
||||||
|
.setName(i18nConfig.databaseFormat)
|
||||||
|
.setDesc(i18nConfig.databaseFormatDesc)
|
||||||
|
.addDropdown((component) => {
|
||||||
|
component
|
||||||
|
.addOption('none', '')
|
||||||
|
.addOption('general', i18nConfig.databaseGeneral)
|
||||||
|
.addOption('next', i18nConfig.databaseNext)
|
||||||
|
.addOption('custom', i18nConfig.databaseCustom)
|
||||||
|
.setValue(this.dataTemp.databaseFormatTemp)
|
||||||
|
.onChange(async (value) => {
|
||||||
|
this.dataTemp.databaseFormatTemp = value;
|
||||||
|
nextTabs.empty();
|
||||||
|
this.updateContentBasedOnSelection(value, nextTabs);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize content based on the current dropdown value
|
||||||
|
this.updateContentBasedOnSelection(this.dataTemp.databaseFormatTemp, nextTabs);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// add save button
|
||||||
|
let footerEl = contentEl.createDiv('save-button');
|
||||||
|
let saveButton = new Setting(footerEl)
|
||||||
|
saveButton.addButton((button: ButtonComponent) => {
|
||||||
|
return button
|
||||||
|
.setTooltip('Save')
|
||||||
|
.setIcon('checkmark')
|
||||||
|
.onClick(async () => {
|
||||||
|
this.dataTemp.savedTempInd = true;
|
||||||
|
this.dataTemp.savedTemp = true;
|
||||||
|
// console.log(this.dataTemp);
|
||||||
|
// console.log(this.dataPrev);
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
saveButton.addExtraButton((button) => {
|
||||||
|
return button
|
||||||
|
.setTooltip('Cancel')
|
||||||
|
.setIcon('cross')
|
||||||
|
.onClick(() => {
|
||||||
|
// console.log(this.dataTemp);
|
||||||
|
// console.log(this.dataPrev);
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpen(): void {
|
||||||
|
this.display()
|
||||||
|
}
|
||||||
|
|
||||||
|
updateContentBasedOnSelection(value: string, nextTabs: HTMLElement): void {
|
||||||
|
// Clear existing content
|
||||||
|
nextTabs.empty();
|
||||||
|
|
||||||
|
// Generate content based on the selected value
|
||||||
|
if (value === 'general') {
|
||||||
|
nextTabs.createEl('h3', { text: i18nConfig.NotionGeneralSettingHeader });
|
||||||
|
|
||||||
|
// add full name
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.databaseFullName, i18nConfig.databaseFullNameDesc, 'text', i18nConfig.databaseFullNameText, this.dataTemp.databaseFullNameTemp, 'dataTemp', 'databaseFullNameTemp')
|
||||||
|
|
||||||
|
// add abbreviate name
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.databaseAbbreviateName, i18nConfig.databaseAbbreviateNameDesc, 'text', i18nConfig.databaseAbbreviateNameText, this.dataTemp.databaseAbbreviateNameTemp, 'dataTemp', 'databaseAbbreviateNameTemp')
|
||||||
|
|
||||||
|
// tag button
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.NotionTagButton, i18nConfig.NotionTagButtonDesc, 'toggle', i18nConfig.NotionCustomTitleText, this.dataTemp.tagButtonTemp, 'dataTemp', 'tagButtonTemp')
|
||||||
|
|
||||||
|
// add custom title button
|
||||||
|
|
||||||
|
new Setting(nextTabs)
|
||||||
|
.setName(i18nConfig.NotionCustomTitle)
|
||||||
|
.setDesc(i18nConfig.NotionCustomTitleDesc)
|
||||||
|
.addToggle((toggle) =>
|
||||||
|
toggle
|
||||||
|
.setValue(this.dataTemp.customTitleButtonTemp)
|
||||||
|
.onChange(async (value) => {
|
||||||
|
this.dataTemp.customTitleButtonTemp = value;
|
||||||
|
this.updateSettingEl(CustomNameEl, value)
|
||||||
|
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// add custom title name
|
||||||
|
const CustomNameEl = this.createStyleDiv('custom-name', (this.dataTemp.customTitleButtonTemp), nextTabs);
|
||||||
|
this.createSettingEl(CustomNameEl, i18nConfig.NotionCustomTitleName, i18nConfig.NotionCustomTitleNameDesc, 'text', i18nConfig.NotionCustomTitleText, this.dataTemp.customTitleNameTemp, 'dataTemp', 'customTitleNameTemp')
|
||||||
|
|
||||||
|
|
||||||
|
// add api key
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.dataTemp.notionAPITemp, 'dataTemp', 'notionAPITemp')
|
||||||
|
|
||||||
|
// add database id
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.DatabaseID, i18nConfig.DatabaseIDDesc, 'password', i18nConfig.DatabaseIDText, this.dataTemp.databaseIDTemp, 'dataTemp', 'databaseIDTemp')
|
||||||
|
|
||||||
|
|
||||||
|
} else if (value === 'next') {
|
||||||
|
|
||||||
|
nextTabs.createEl('h3', { text: i18nConfig.NotionNextSettingHeader });
|
||||||
|
|
||||||
|
// add full name
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.databaseFullName, i18nConfig.databaseFullNameDesc, 'text', i18nConfig.databaseFullNameText, this.dataTemp.databaseFullNameTemp, 'dataTemp', 'databaseFullNameTemp')
|
||||||
|
|
||||||
|
// add abbreviate name
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.databaseAbbreviateName, i18nConfig.databaseAbbreviateNameDesc, 'text', i18nConfig.databaseAbbreviateNameText, this.dataTemp.databaseAbbreviateNameTemp, 'dataTemp', 'databaseAbbreviateNameTemp')
|
||||||
|
|
||||||
|
// add api key
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.dataTemp.notionAPITemp, 'dataTemp', 'notionAPITemp')
|
||||||
|
|
||||||
|
// add database id
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.DatabaseID, i18nConfig.DatabaseIDDesc, 'password', i18nConfig.DatabaseIDText, this.dataTemp.databaseIDTemp, 'dataTemp', 'databaseIDTemp')
|
||||||
|
|
||||||
|
} else if (value === 'custom') {
|
||||||
|
|
||||||
|
nextTabs.createEl('h3', { text: i18nConfig.NotionCustomSettingHeader });
|
||||||
|
|
||||||
|
// add full name
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.databaseFullName, i18nConfig.databaseFullNameDesc, 'text', i18nConfig.databaseFullNameText, this.dataTemp.databaseFullNameTemp, 'dataTemp', 'databaseFullNameTemp')
|
||||||
|
|
||||||
|
// add abbreviate name
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.databaseAbbreviateName, i18nConfig.databaseAbbreviateNameDesc, 'text', i18nConfig.databaseAbbreviateNameText, this.dataTemp.databaseAbbreviateNameTemp, 'dataTemp', 'databaseAbbreviateNameTemp')
|
||||||
|
|
||||||
|
// add api key
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.dataTemp.notionAPITemp, 'dataTemp', 'notionAPITemp')
|
||||||
|
|
||||||
|
// add database id
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.DatabaseID, i18nConfig.DatabaseIDDesc, 'password', i18nConfig.DatabaseIDText, this.dataTemp.databaseIDTemp, 'dataTemp', 'databaseIDTemp')
|
||||||
|
|
||||||
|
// add custom properties
|
||||||
|
this.initializePropertyLines(nextTabs, this.dataTemp.customPropertiesTemp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initializePropertyLines(containerEl: HTMLElement, properties: customProperty[]): void {
|
||||||
|
if (!containerEl) {
|
||||||
|
console.error('Failed to initialize property lines: containerEl is null');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName("Add New Property")
|
||||||
|
.setDesc("Click to add a new property")
|
||||||
|
.addButton(button => {
|
||||||
|
return button
|
||||||
|
.setButtonText('Add')
|
||||||
|
.setTooltip('Add one more property')
|
||||||
|
.onClick(() => {
|
||||||
|
this.createPropertyLine(containerEl, properties);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
properties.forEach(property => {
|
||||||
|
this.updatePropertyLine(containerEl, property, properties);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
updatePropertyLine(containerEl: HTMLElement, property: customProperty, properties: customProperty[]) {
|
||||||
|
let isExistingProperty = property !== null;
|
||||||
|
const propertyIndex = isExistingProperty ? property.index : properties.length;
|
||||||
|
|
||||||
|
const propertyLine = new Setting(containerEl)
|
||||||
|
.setName(propertyIndex === 0 ? i18nConfig.CustomPropertyFirstColumn : `${i18nConfig.CustomProperty} ${propertyIndex}`)
|
||||||
|
.setDesc(propertyIndex === 0 ? i18nConfig.CustomPropertyFirstColumnDesc : "");
|
||||||
|
|
||||||
|
propertyLine.addText(text => {
|
||||||
|
text.setPlaceholder(i18nConfig.CustomPropertyName)
|
||||||
|
.setValue(isExistingProperty ? property.customName : "")
|
||||||
|
.onChange(value => {
|
||||||
|
const actualIndex = properties.findIndex(p => p.index === propertyIndex);
|
||||||
|
if (actualIndex !== -1) {
|
||||||
|
properties[actualIndex].customName = value;
|
||||||
|
} else {
|
||||||
|
properties.push({ customName: value, customType: '', index: propertyIndex });
|
||||||
|
isExistingProperty = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
propertyLine.addDropdown((dropdown) => {
|
||||||
|
const options: Record<string, string> = {
|
||||||
|
'rich_text': 'Text',
|
||||||
|
'number': 'Number',
|
||||||
|
'select': 'Select',
|
||||||
|
'multi_select': 'Multi-Select',
|
||||||
|
'date': 'Date',
|
||||||
|
'files': 'Files & Media',
|
||||||
|
'checkbox': 'Checkbox',
|
||||||
|
'url': 'URL',
|
||||||
|
'email': 'Email',
|
||||||
|
'phone_number': 'Phone Number'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (propertyIndex === 0) {
|
||||||
|
dropdown.addOption('title', 'Title');
|
||||||
|
} else {
|
||||||
|
Object.keys(options).forEach(key => {
|
||||||
|
dropdown.addOption(key, options[key]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dropdown.setValue(isExistingProperty ? property.customType : "")
|
||||||
|
.onChange(value => {
|
||||||
|
const actualIndex = properties.findIndex(p => p.index === propertyIndex);
|
||||||
|
if (actualIndex !== -1) {
|
||||||
|
properties[actualIndex].customType = value;
|
||||||
|
} else if (!isExistingProperty) {
|
||||||
|
properties.push({ customName: '', customType: value, index: propertyIndex });
|
||||||
|
isExistingProperty = true; // Update the flag to prevent re-adding
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (propertyIndex > 0) {
|
||||||
|
propertyLine.addButton(button => {
|
||||||
|
return button
|
||||||
|
.setTooltip("Delete")
|
||||||
|
.setIcon("trash")
|
||||||
|
.onClick(() => {
|
||||||
|
console.log('Deleting property', properties[propertyIndex]);
|
||||||
|
this.deleteProperty(propertyIndex, properties);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.propertyLines.push(propertyLine);
|
||||||
|
this.updatePropertyLines();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
createStyleDiv(className: string, commandValue: boolean = false, parentEl: HTMLElement): HTMLDivElement {
|
||||||
|
return super.createStyleDiv(className, commandValue, parentEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSettingEl(element: HTMLElement, commandValue: boolean) {
|
||||||
|
super.updateSettingEl(element, commandValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
createSettingEl(contentEl: HTMLElement, name: string, desc: string, type: string, placeholder: string, holderValue: any, dataRecord: string, settingsKey: string): Setting {
|
||||||
|
return super.createSettingEl(contentEl, name, desc, type, placeholder, holderValue, dataRecord, settingsKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
178
src/ui/PreviewModal.ts
Normal file
178
src/ui/PreviewModal.ts
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
import { App, ExtraButtonComponent, Modal, Notice, Setting } from "obsidian";
|
||||||
|
import ObsidianSyncNotionPlugin from "../main";
|
||||||
|
import { DatabaseDetails, ObsidianSettingTab } from "./settingTabs";
|
||||||
|
import { customProperty } from "./settingModal";
|
||||||
|
import { i18nConfig } from "../lang/I18n";
|
||||||
|
|
||||||
|
export class PreviewModal extends Modal {
|
||||||
|
plugin: ObsidianSyncNotionPlugin;
|
||||||
|
settingTab: ObsidianSettingTab;
|
||||||
|
dbDetails: DatabaseDetails;
|
||||||
|
|
||||||
|
constructor(app: App, plugin: ObsidianSyncNotionPlugin, settingTab: ObsidianSettingTab, dbDetails: DatabaseDetails) {
|
||||||
|
super(app);
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.settingTab = settingTab;
|
||||||
|
this.dbDetails = dbDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
display(): void {
|
||||||
|
this.containerEl.addClass('preview-modal')
|
||||||
|
this.titleEl.setText('Preview')
|
||||||
|
|
||||||
|
let { contentEl } = this;
|
||||||
|
|
||||||
|
const previewEl = contentEl.createDiv('preview-content')
|
||||||
|
|
||||||
|
const dbFormatEl = new Setting(previewEl)
|
||||||
|
dbFormatEl
|
||||||
|
.setName('Database Format')
|
||||||
|
.addText(text => text
|
||||||
|
.setValue(this.dbDetails.format)
|
||||||
|
.setDisabled(true));
|
||||||
|
|
||||||
|
const dbFullEl = new Setting(previewEl)
|
||||||
|
dbFullEl
|
||||||
|
.setName('Database Full Name')
|
||||||
|
.addText(text => text
|
||||||
|
.setValue(this.dbDetails.fullName)
|
||||||
|
.setDisabled(true));
|
||||||
|
|
||||||
|
const dbAbbrEl = new Setting(previewEl)
|
||||||
|
dbAbbrEl
|
||||||
|
.setName('Database Abbreviate Name')
|
||||||
|
.addText(text => text
|
||||||
|
.setValue(this.dbDetails.abName)
|
||||||
|
.setDisabled(true));
|
||||||
|
// .controlEl.createEl('p', { text: this.dbDetails.abName })
|
||||||
|
|
||||||
|
// Setting for toggle and copy buttons
|
||||||
|
new Setting(previewEl)
|
||||||
|
.setName('Notion API Key')
|
||||||
|
.addExtraButton((button: ExtraButtonComponent) => {
|
||||||
|
let isApiKeyVisible = false;
|
||||||
|
|
||||||
|
return button
|
||||||
|
.setTooltip('Toggle API Key Visibility')
|
||||||
|
.setIcon('eye')
|
||||||
|
.onClick(() => {
|
||||||
|
isApiKeyVisible = !isApiKeyVisible;
|
||||||
|
button.setIcon(isApiKeyVisible ? 'eye-off' : 'eye');
|
||||||
|
apiKeySetting.settingEl.style.display = isApiKeyVisible ? '' : 'none'; // Toggle visibility
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// Setting for displaying the API key
|
||||||
|
const apiKeySetting = new Setting(previewEl)
|
||||||
|
|
||||||
|
apiKeySetting.infoEl.createEl('p', { text: this.dbDetails.notionAPI })
|
||||||
|
|
||||||
|
|
||||||
|
apiKeySetting
|
||||||
|
.addExtraButton((button: ExtraButtonComponent) => {
|
||||||
|
return button
|
||||||
|
.setTooltip('Copy API Key')
|
||||||
|
.setIcon('clipboard')
|
||||||
|
.onClick(() => {
|
||||||
|
navigator.clipboard.writeText(this.dbDetails.notionAPI)
|
||||||
|
new Notice('API Key copied to clipboard');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
apiKeySetting.settingEl.style.display = 'none'; // Hide initially
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
new Setting(previewEl)
|
||||||
|
.setName('Database ID')
|
||||||
|
.addExtraButton((button: ExtraButtonComponent) => {
|
||||||
|
let isDbIdVisible = false;
|
||||||
|
|
||||||
|
return button
|
||||||
|
.setTooltip('Toggle Database ID Visibility')
|
||||||
|
.setIcon('eye')
|
||||||
|
.onClick(() => {
|
||||||
|
|
||||||
|
isDbIdVisible = !isDbIdVisible;
|
||||||
|
button.setIcon(isDbIdVisible ? 'eye-off' : 'eye');
|
||||||
|
dbIdSetting.settingEl.style.display = isDbIdVisible ? '' : 'none'; // Toggle visibility
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
const dbIdSetting = new Setting(previewEl)
|
||||||
|
|
||||||
|
dbIdSetting.infoEl.createEl('p', { text: this.dbDetails.databaseID })
|
||||||
|
|
||||||
|
dbIdSetting
|
||||||
|
.addExtraButton((button: ExtraButtonComponent) => {
|
||||||
|
return button
|
||||||
|
.setTooltip('Copy Database ID')
|
||||||
|
.setIcon('clipboard')
|
||||||
|
.onClick(() => {
|
||||||
|
navigator.clipboard.writeText(this.dbDetails.databaseID)
|
||||||
|
new Notice('Database ID copied to clipboard');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
dbIdSetting.settingEl.style.display = 'none'; // Hide initially
|
||||||
|
|
||||||
|
// Preview the custom properties
|
||||||
|
|
||||||
|
if (this.dbDetails.format === 'custom') {
|
||||||
|
|
||||||
|
const customPrv = previewEl.createDiv("custom-tabs");
|
||||||
|
|
||||||
|
this.previewPropertyLine(previewEl, this.dbDetails.customProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onOpen() {
|
||||||
|
this.display()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
previewPropertyLine(containerEl: HTMLElement, properties: customProperty[]): void {
|
||||||
|
|
||||||
|
properties.forEach((property, index) => {
|
||||||
|
const propertyLine = new Setting(containerEl)
|
||||||
|
.setName(index === 0 ? i18nConfig.CustomPropertyFirstColumn : `${i18nConfig.CustomProperty} ${index}`)
|
||||||
|
.setDesc(index === 0 ? i18nConfig.CustomPropertyFirstColumnDesc : "");
|
||||||
|
|
||||||
|
propertyLine.addText(text => {
|
||||||
|
text.setPlaceholder(i18nConfig.CustomPropertyName)
|
||||||
|
.setValue(property.customName)
|
||||||
|
.setDisabled(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
propertyLine.addDropdown((dropdown) => {
|
||||||
|
const options: Record<string, string> = {
|
||||||
|
'title': 'Title',
|
||||||
|
'rich_text': 'Text',
|
||||||
|
'number': 'Number',
|
||||||
|
'select': 'Select',
|
||||||
|
'multi_select': 'Multi-Select',
|
||||||
|
'date': 'Date',
|
||||||
|
'files': 'Files & Media',
|
||||||
|
'checkbox': 'Checkbox',
|
||||||
|
'url': 'URL',
|
||||||
|
'email': 'Email',
|
||||||
|
'phone_number': 'Phone Number',
|
||||||
|
// Additional options can be added here
|
||||||
|
};
|
||||||
|
|
||||||
|
// Populate dropdown with options
|
||||||
|
Object.keys(options).forEach(key => {
|
||||||
|
dropdown.addOption(key, options[key]);
|
||||||
|
});
|
||||||
|
|
||||||
|
dropdown.setValue(property.customType)
|
||||||
|
.setDisabled(true); // Disable dropdown to prevent changes
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
14
src/ui/icon.ts
Normal file
14
src/ui/icon.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { addIcon } from 'obsidian';
|
||||||
|
|
||||||
|
const icons: Record<string, string> = {
|
||||||
|
'notion-logo': `
|
||||||
|
<!-- License: Apache. Made by lawnchairlauncher: https://github.com/lawnchairlauncher/lawnicons -->
|
||||||
|
<svg width="110px" height="110px" viewBox="0 0 192 192" xmlns="http://www.w3.org/2000/svg" fill="none"><path fill="#817f7a" fill-rule="evenodd" d="m138.462 21.522 27.784 19.588.044.033.275.201c1.713 1.256 3.349 2.455 4.452 3.83 1.411 1.76 1.884 3.644 1.884 5.877v104.706c0 3.587-.635 7.178-3.058 9.934-2.451 2.789-6.145 4.067-10.732 4.394l-.018.001-98.629 5.971-.021.001c-3.242.154-6.094.035-8.669-.907-2.688-.984-4.719-2.727-6.604-5.129l-.01-.012-19.979-25.979-.012-.017c-3.81-5.086-5.723-9.348-5.723-14.509V34.509c0-3.12.688-6.394 2.745-9.033 2.124-2.727 5.356-4.328 9.503-4.686l.058-.005 84.854-4.344c5.192-.445 8.938-.576 12.286.185 3.459.787 6.208 2.452 9.57 4.896ZM56.43 157.336h.002v3.3c0 1.904.47 2.337.613 2.452.296.235 1.203.652 3.642.518l97.449-5.371c1.928-.106 2.256-.649 2.348-.801l.005-.008c.29-.476.486-1.407.486-3.357V60.001c0-1.635-.334-2.218-.421-2.327l-.005-.007-.002-.003-.006-.002a.117.117 0 0 1-.012-.004c-.053-.019-.263-.078-.724-.037l-.057.005-101.622 5.668c-.624.056-.973.163-1.152.242-.142.062-.173.104-.181.116l-.002.002c-.066.085-.36.586-.36 2.321v91.361Zm9.085-106.705 87.074-4.506-21.028-15.375-.039-.031c-1.259-.98-2.507-1.854-4.12-2.46-1.588-.597-3.695-.993-6.669-.734l-.05.005-87.009 4.898h-.01a6.453 6.453 0 0 0-.893.116L49.934 48.56c2.037 1.646 3.109 2.146 4.337 2.367 1.538.277 3.52.167 7.722-.115l3.522-.237v.056Zm-34.231-3.586v83.893c0 .538.175 1.061.498 1.49l13.174 17.464V61.224a2.47 2.47 0 0 0-.877-1.889l-.08-.068-12.715-12.222Zm109.871 35.062c.451 2.04 0 4.082-2.041 4.315l-3.393.673v49.881c-2.947 1.586-5.66 2.492-7.927 2.492-3.622 0-4.528-1.134-7.239-4.53l-.003-.003L98.36 100.02v33.78l7.02 1.59s0 4.082-5.664 4.082l-15.615.906c-.455-.91 0-3.176 1.582-3.627l4.078-1.131V90.955l-5.66-.459c-.454-2.04.677-4.987 3.85-5.216l16.754-1.128 23.09 35.367V88.231l-5.885-.677c-.455-2.499 1.356-4.315 3.618-4.536l15.627-.91v-.001Z" clip-rule="evenodd"/></svg>
|
||||||
|
`
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addIcons = (): void => {
|
||||||
|
Object.keys(icons).forEach((key) => {
|
||||||
|
addIcon(key, icons[key]);
|
||||||
|
});
|
||||||
|
};
|
||||||
377
src/ui/settingModal.ts
Normal file
377
src/ui/settingModal.ts
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
import {
|
||||||
|
Modal,
|
||||||
|
Setting,
|
||||||
|
ButtonComponent, App
|
||||||
|
} from 'obsidian';
|
||||||
|
|
||||||
|
import { i18nConfig } from "../lang/I18n";
|
||||||
|
import ObsidianSyncNotionPlugin from "../main";
|
||||||
|
import { DatabaseDetails, ObsidianSettingTab } from "./settingTabs";
|
||||||
|
|
||||||
|
export interface customProperty {
|
||||||
|
customName: string;
|
||||||
|
customType: string;
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SettingModal extends Modal {
|
||||||
|
propertyLines: Setting[] = []; // Store all property line settings
|
||||||
|
properties: customProperty[] = []; // Array to store property values and types
|
||||||
|
[key: string]: any; // Index signature
|
||||||
|
data: Record<string, any> = {
|
||||||
|
databaseFormat: 'none',
|
||||||
|
databaseFullName: '',
|
||||||
|
databaseAbbreviateName: '',
|
||||||
|
notionAPI: '',
|
||||||
|
databaseID: '',
|
||||||
|
tagButton: true,
|
||||||
|
customTitleButton: false,
|
||||||
|
customTitleName: '',
|
||||||
|
customProperties: [],
|
||||||
|
// customValues: '',
|
||||||
|
saved: false,
|
||||||
|
};
|
||||||
|
plugin: ObsidianSyncNotionPlugin;
|
||||||
|
settingTab: ObsidianSettingTab;
|
||||||
|
|
||||||
|
constructor(app: App, plugin: ObsidianSyncNotionPlugin, settingTab: ObsidianSettingTab, dbDetails?: DatabaseDetails) {
|
||||||
|
super(app);
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.settingTab = settingTab;
|
||||||
|
this.properties = [];
|
||||||
|
if (dbDetails) {
|
||||||
|
this.data.databaseFormat = dbDetails.format;
|
||||||
|
this.data.databaseFullName = dbDetails.fullName;
|
||||||
|
this.data.databaseAbbreviateName = dbDetails.abName;
|
||||||
|
this.data.notionAPI = dbDetails.notionAPI;
|
||||||
|
this.data.databaseID = dbDetails.databaseID;
|
||||||
|
this.data.tagButton = dbDetails.tagButton;
|
||||||
|
this.data.customTitleButton = dbDetails.customTitleButton;
|
||||||
|
this.data.customTitleName = dbDetails.customTitleName;
|
||||||
|
this.data.customProperties = dbDetails.customProperties;
|
||||||
|
// this.data.customValues = dbDetails.customValues;
|
||||||
|
this.data.saved = dbDetails.saved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
display(): void {
|
||||||
|
this.containerEl.addClass("settings-modal");
|
||||||
|
this.titleEl.setText('Add new database');
|
||||||
|
|
||||||
|
// create the dropdown button to select the database format
|
||||||
|
let { contentEl } = this;
|
||||||
|
contentEl.empty();
|
||||||
|
|
||||||
|
const settingDiv = contentEl.createDiv('setting-div');
|
||||||
|
const nextTabs = contentEl.createDiv('next-tabs');
|
||||||
|
|
||||||
|
|
||||||
|
new Setting(settingDiv)
|
||||||
|
.setName(i18nConfig.databaseFormat)
|
||||||
|
.setDesc(i18nConfig.databaseFormatDesc)
|
||||||
|
.addDropdown((component) => {
|
||||||
|
component
|
||||||
|
.addOption('none', '')
|
||||||
|
.addOption('general', i18nConfig.databaseGeneral)
|
||||||
|
.addOption('next', i18nConfig.databaseNext)
|
||||||
|
.addOption('custom', i18nConfig.databaseCustom)
|
||||||
|
.setValue(this.data.databaseFormat)
|
||||||
|
.onChange(async (value) => {
|
||||||
|
this.data.databaseFormat = value;
|
||||||
|
nextTabs.empty();
|
||||||
|
this.updateContentBasedOnSelection(value, nextTabs);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize content based on the current dropdown value
|
||||||
|
(this.data.saved) ? this.updateContentBasedOnSelection(this.data.databaseFormat, nextTabs) : this.updateContentBasedOnSelection(this.plugin.settings.databaseFormat, nextTabs);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// add save button
|
||||||
|
let footerEl = contentEl.createDiv('save-button');
|
||||||
|
let saveButton = new Setting(footerEl)
|
||||||
|
saveButton.addButton((button: ButtonComponent) => {
|
||||||
|
return button
|
||||||
|
.setTooltip('Save')
|
||||||
|
.setIcon('checkmark')
|
||||||
|
.onClick(async () => {
|
||||||
|
this.data.saved = true;
|
||||||
|
this.data.customProperties = this.properties;
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
saveButton.addExtraButton((button) => {
|
||||||
|
return button
|
||||||
|
.setTooltip('Cancel')
|
||||||
|
.setIcon('cross')
|
||||||
|
.onClick(() => {
|
||||||
|
this.data.saved = false;
|
||||||
|
// this.data.customProperties = this.properties;
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateContentBasedOnSelection(value: string, nextTabs: HTMLElement): void {
|
||||||
|
// Clear existing content
|
||||||
|
nextTabs.empty();
|
||||||
|
|
||||||
|
// Generate content based on the selected value
|
||||||
|
if (value === 'general') {
|
||||||
|
nextTabs.createEl('h3', { text: i18nConfig.NotionGeneralSettingHeader });
|
||||||
|
|
||||||
|
// add full name
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.databaseFullName, i18nConfig.databaseFullNameDesc, 'text', i18nConfig.databaseFullNameText, this.data.databaseFullName, 'data', 'databaseFullName')
|
||||||
|
|
||||||
|
// add abbreviate name
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.databaseAbbreviateName, i18nConfig.databaseAbbreviateNameDesc, 'text', i18nConfig.databaseAbbreviateNameText, this.data.databaseAbbreviateName, 'data', 'databaseAbbreviateName')
|
||||||
|
|
||||||
|
// tag button
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.NotionTagButton, i18nConfig.NotionTagButtonDesc, 'toggle', i18nConfig.NotionCustomTitleText, this.data.tagButton, 'data', 'tagButton')
|
||||||
|
|
||||||
|
// add custom title button
|
||||||
|
|
||||||
|
new Setting(nextTabs)
|
||||||
|
.setName(i18nConfig.NotionCustomTitle)
|
||||||
|
.setDesc(i18nConfig.NotionCustomTitleDesc)
|
||||||
|
.addToggle((toggle) =>
|
||||||
|
toggle
|
||||||
|
.setValue(this.data.customTitleButton)
|
||||||
|
.onChange(async (value) => {
|
||||||
|
this.data.customTitleButton = value;
|
||||||
|
this.updateSettingEl(CustomNameEl, value)
|
||||||
|
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// add custom title name
|
||||||
|
const CustomNameEl = this.createStyleDiv('custom-name', (this.data.customTitleButton), nextTabs);
|
||||||
|
this.createSettingEl(CustomNameEl, i18nConfig.NotionCustomTitleName, i18nConfig.NotionCustomTitleNameDesc, 'text', i18nConfig.NotionCustomTitleText, this.data.customTitleName, 'data', 'customTitleName')
|
||||||
|
|
||||||
|
|
||||||
|
// add api key
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.data.notionAPI, 'data', 'notionAPI')
|
||||||
|
|
||||||
|
// add database id
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.DatabaseID, i18nConfig.DatabaseIDDesc, 'password', i18nConfig.DatabaseIDText, this.data.databaseID, 'data', 'databaseID')
|
||||||
|
|
||||||
|
|
||||||
|
} else if (value === 'next') {
|
||||||
|
|
||||||
|
nextTabs.createEl('h3', { text: i18nConfig.NotionNextSettingHeader });
|
||||||
|
|
||||||
|
// add full name
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.databaseFullName, i18nConfig.databaseFullNameDesc, 'text', i18nConfig.databaseFullNameText, this.data.databaseFullName, 'data', 'databaseFullName')
|
||||||
|
|
||||||
|
// add abbreviate name
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.databaseAbbreviateName, i18nConfig.databaseAbbreviateNameDesc, 'text', i18nConfig.databaseAbbreviateNameText, this.data.databaseAbbreviateName, 'data', 'databaseAbbreviateName')
|
||||||
|
|
||||||
|
// add api key
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.data.notionAPI, 'data', 'notionAPI')
|
||||||
|
|
||||||
|
|
||||||
|
// add database id
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.DatabaseID, i18nConfig.DatabaseIDDesc, 'password', i18nConfig.DatabaseIDText, this.data.databaseID, 'data', 'databaseID')
|
||||||
|
|
||||||
|
} else if (value === 'custom') {
|
||||||
|
|
||||||
|
nextTabs.createEl('h3', { text: i18nConfig.NotionCustomSettingHeader });
|
||||||
|
|
||||||
|
// add full name
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.databaseFullName, i18nConfig.databaseFullNameDesc, 'text', i18nConfig.databaseFullNameText, this.data.databaseFullName, 'data', 'databaseFullName')
|
||||||
|
|
||||||
|
// add abbreviate name
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.databaseAbbreviateName, i18nConfig.databaseAbbreviateNameDesc, 'text', i18nConfig.databaseAbbreviateNameText, this.data.databaseAbbreviateName, 'data', 'databaseAbbreviateName')
|
||||||
|
|
||||||
|
// add api key
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.data.notionAPI, 'data', 'notionAPI')
|
||||||
|
|
||||||
|
// add database id
|
||||||
|
this.createSettingEl(nextTabs, i18nConfig.DatabaseID, i18nConfig.DatabaseIDDesc, 'password', i18nConfig.DatabaseIDText, this.data.databaseID, 'data', 'databaseID')
|
||||||
|
|
||||||
|
// add new property button
|
||||||
|
new Setting(nextTabs)
|
||||||
|
.setName(i18nConfig.NotionCustomValues)
|
||||||
|
.setDesc(i18nConfig.NotionCustomValuesDesc)
|
||||||
|
.addButton((button: ButtonComponent) => {
|
||||||
|
return button
|
||||||
|
.setTooltip('Add one more property')
|
||||||
|
.setButtonText('Add New Property')
|
||||||
|
.onClick(async () => {
|
||||||
|
const customTabs = nextTabs.createDiv("custom-tabs");
|
||||||
|
this.createPropertyLine(customTabs, this.properties);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onOpen() {
|
||||||
|
// add console log to check if the modal is opened
|
||||||
|
this.display()
|
||||||
|
}
|
||||||
|
|
||||||
|
createPropertyLine(containerEl: HTMLElement, properties: customProperty[]): void {
|
||||||
|
const propertyIndex = properties.length;
|
||||||
|
|
||||||
|
properties.push({ customName: "", customType: "", index: propertyIndex });
|
||||||
|
|
||||||
|
const propertyLine = new Setting(containerEl)
|
||||||
|
.setName(propertyIndex === 0 ? i18nConfig.CustomPropertyFirstColumn : `${i18nConfig.CustomProperty} ${propertyIndex}`)
|
||||||
|
.setDesc(propertyIndex === 0 ? i18nConfig.CustomPropertyFirstColumnDesc : "");
|
||||||
|
|
||||||
|
propertyLine.addText(text => {
|
||||||
|
text.setPlaceholder(i18nConfig.CustomPropertyName)
|
||||||
|
.setValue("")
|
||||||
|
.onChange(value => {
|
||||||
|
let actualIndex = properties.findIndex(p => p.index === propertyIndex);
|
||||||
|
if (actualIndex !== -1) {
|
||||||
|
properties[actualIndex].customName = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
propertyLine.addDropdown((dropdown) => {
|
||||||
|
const options: Record<string, string> = {
|
||||||
|
'rich_text': 'Text',
|
||||||
|
'number': 'Number',
|
||||||
|
'select': 'Select',
|
||||||
|
'multi_select': 'Multi-Select',
|
||||||
|
'date': 'Date',
|
||||||
|
'files': 'Files & Media',
|
||||||
|
'checkbox': 'Checkbox',
|
||||||
|
'url': 'URL',
|
||||||
|
'email': 'Email',
|
||||||
|
'phone_number': 'Phone Number',
|
||||||
|
// 'formula': 'Formula',
|
||||||
|
// 'relation': 'Relation',
|
||||||
|
// 'rollup': 'Rollup',
|
||||||
|
// 'created_time': 'Created time',
|
||||||
|
// 'created_by': 'Created by',
|
||||||
|
// 'last_edited_time': 'Last Edited Time',
|
||||||
|
// 'last_edited_by': 'Last Edited By',
|
||||||
|
};
|
||||||
|
|
||||||
|
const currentProperty = properties[propertyIndex];
|
||||||
|
|
||||||
|
if (propertyIndex === 0) {
|
||||||
|
dropdown.addOption("title", "Title");
|
||||||
|
} else {
|
||||||
|
Object.keys(options).forEach(key => {
|
||||||
|
dropdown.addOption(key, options[key]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
dropdown.setValue("")
|
||||||
|
.onChange(value => {
|
||||||
|
if (currentProperty) {
|
||||||
|
currentProperty.customType = value;
|
||||||
|
// Retrieve the index of currentProperty from the properties array
|
||||||
|
const updatedIndex = properties.findIndex(p => p === currentProperty);
|
||||||
|
console.log(`Updated value at index ${updatedIndex}: ${value}`);
|
||||||
|
} else {
|
||||||
|
console.log("Property not found, may have been deleted.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (propertyIndex > 0) {
|
||||||
|
propertyLine.addButton(button => {
|
||||||
|
return button
|
||||||
|
.setTooltip("Delete")
|
||||||
|
.setIcon("trash")
|
||||||
|
.onClick(() => {
|
||||||
|
this.deleteProperty(propertyIndex, properties);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.propertyLines.push(propertyLine);
|
||||||
|
this.updatePropertyLines(); // Ensure property lines are updated after creation
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteProperty(propertyIndex: number, properties: customProperty[]): void {
|
||||||
|
let actualIndex = properties.findIndex(p => p.index === propertyIndex);
|
||||||
|
if (actualIndex !== -1) {
|
||||||
|
properties.splice(actualIndex, 1);
|
||||||
|
if (this.propertyLines[actualIndex]) {
|
||||||
|
this.propertyLines[actualIndex].settingEl.remove();
|
||||||
|
this.propertyLines.splice(actualIndex, 1);
|
||||||
|
}
|
||||||
|
// Update indices in the properties array
|
||||||
|
properties.forEach((prop, idx) => {
|
||||||
|
prop.index = idx;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.updatePropertyLines();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePropertyLines() {
|
||||||
|
this.propertyLines.forEach((line, idx) => {
|
||||||
|
line.setName(idx === 0 ? i18nConfig.CustomPropertyFirstColumn : `${i18nConfig.CustomProperty} ${idx}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a function to create a div with a style for pop over elements
|
||||||
|
public createStyleDiv(className: string, commandValue: boolean = false, parentEl: HTMLElement) {
|
||||||
|
return parentEl.createDiv(className, (div) => {
|
||||||
|
this.updateSettingEl(div, commandValue);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the setting display style in the setting tab
|
||||||
|
public updateSettingEl(element: HTMLElement, commandValue: boolean) {
|
||||||
|
element.style.borderTop = commandValue ? "1px solid var(--background-modifier-border)" : "none";
|
||||||
|
element.style.paddingTop = commandValue ? "0.75em" : "0";
|
||||||
|
element.style.display = commandValue ? "block" : "none";
|
||||||
|
element.style.alignItems = "center";
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to add one setting element in the setting tab.
|
||||||
|
public createSettingEl(contentEl: HTMLElement, name: string, desc: string, type: string, placeholder: string, holderValue: any, dataRecord: string, settingsKey: string) {
|
||||||
|
if (type === 'password') {
|
||||||
|
return new Setting(contentEl)
|
||||||
|
.setName(name)
|
||||||
|
.setDesc(desc)
|
||||||
|
.addText((text) => {
|
||||||
|
text.inputEl.type = type;
|
||||||
|
return text
|
||||||
|
.setPlaceholder(placeholder)
|
||||||
|
.setValue(holderValue)
|
||||||
|
.onChange(async (value) => {
|
||||||
|
this[dataRecord][settingsKey] = value; // Update the settings dictionary await this.plugin.saveSettings();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} else if (type === 'toggle') {
|
||||||
|
return new Setting(contentEl)
|
||||||
|
.setName(name)
|
||||||
|
.setDesc(desc)
|
||||||
|
.addToggle((toggle) =>
|
||||||
|
toggle
|
||||||
|
.setValue(holderValue)
|
||||||
|
.onChange(async (value) => {
|
||||||
|
this[dataRecord][settingsKey] = value; // Update the settings dictionary await this.plugin.saveSettings();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else if (type === 'text') {
|
||||||
|
return new Setting(contentEl)
|
||||||
|
.setName(name)
|
||||||
|
.setDesc(desc)
|
||||||
|
.addText((text) =>
|
||||||
|
text
|
||||||
|
.setPlaceholder(placeholder)
|
||||||
|
.setValue(holderValue)
|
||||||
|
.onChange(async (value) => {
|
||||||
|
this[dataRecord][settingsKey] = value; // Update the settings dictionary await this.plugin.saveSettings();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
307
src/ui/settingTabs.ts
Normal file
307
src/ui/settingTabs.ts
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
import { App, ButtonComponent, PluginSettingTab, Setting } from "obsidian";
|
||||||
|
import { i18nConfig } from "../lang/I18n";
|
||||||
|
import ObsidianSyncNotionPlugin from "../main";
|
||||||
|
import { SettingModal } from "./settingModal";
|
||||||
|
import { PreviewModal } from "./PreviewModal";
|
||||||
|
import { EditModal } from "./EditModal";
|
||||||
|
import { DeleteModal } from "./DeleteModal";
|
||||||
|
|
||||||
|
export interface PluginSettings {
|
||||||
|
NextButton: boolean;
|
||||||
|
notionAPINext: string;
|
||||||
|
databaseIDNext: string;
|
||||||
|
bannerUrl: string;
|
||||||
|
notionUser: string;
|
||||||
|
NotionLinkDisplay: boolean;
|
||||||
|
proxy: string;
|
||||||
|
GeneralButton: boolean;
|
||||||
|
tagButton: boolean;
|
||||||
|
customTitleButton: boolean;
|
||||||
|
customTitleName: string;
|
||||||
|
notionAPIGeneral: string;
|
||||||
|
databaseIDGeneral: string;
|
||||||
|
CustomButton: boolean;
|
||||||
|
CustomValues: string;
|
||||||
|
notionAPICustom: string;
|
||||||
|
databaseIDCustom: string;
|
||||||
|
[key: string]: any;
|
||||||
|
databaseDetails: Record<string, DatabaseDetails>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DatabaseDetails {
|
||||||
|
format: string;
|
||||||
|
fullName: string;
|
||||||
|
abName: string;
|
||||||
|
notionAPI: string;
|
||||||
|
databaseID: string;
|
||||||
|
tagButton: boolean;
|
||||||
|
customTitleButton: boolean;
|
||||||
|
customTitleName: string;
|
||||||
|
customProperties: { customName: string, customType: string, index: number }[];
|
||||||
|
// customValues: string;
|
||||||
|
saved: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_SETTINGS: PluginSettings = {
|
||||||
|
NextButton: true,
|
||||||
|
notionAPINext: "",
|
||||||
|
databaseIDNext: "",
|
||||||
|
bannerUrl: "",
|
||||||
|
notionUser: "",
|
||||||
|
NotionLinkDisplay: true,
|
||||||
|
proxy: "",
|
||||||
|
GeneralButton: true,
|
||||||
|
tagButton: true,
|
||||||
|
customTitleButton: false,
|
||||||
|
customTitleName: "",
|
||||||
|
notionAPIGeneral: "",
|
||||||
|
databaseIDGeneral: "",
|
||||||
|
CustomButton: false,
|
||||||
|
CustomValues: "",
|
||||||
|
notionAPICustom: "",
|
||||||
|
databaseIDCustom: "",
|
||||||
|
databaseDetails: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export class ObsidianSettingTab extends PluginSettingTab {
|
||||||
|
plugin: ObsidianSyncNotionPlugin;
|
||||||
|
databaseEl: HTMLDivElement;
|
||||||
|
|
||||||
|
constructor(app: App, plugin: ObsidianSyncNotionPlugin) {
|
||||||
|
super(app, plugin);
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
display(): void {
|
||||||
|
const { containerEl } = this;
|
||||||
|
|
||||||
|
containerEl.empty();
|
||||||
|
|
||||||
|
// General Settings
|
||||||
|
containerEl.createEl('h2', { text: i18nConfig.GeneralSetting });
|
||||||
|
|
||||||
|
this.createSettingEl(containerEl, i18nConfig.BannerUrl, i18nConfig.BannerUrlDesc, 'text', i18nConfig.BannerUrlText, this.plugin.settings.bannerUrl, 'bannerUrl')
|
||||||
|
|
||||||
|
this.createSettingEl(containerEl, i18nConfig.NotionUser, i18nConfig.NotionUserDesc, 'text', i18nConfig.NotionUserText, this.plugin.settings.notionUser, 'notionUser')
|
||||||
|
|
||||||
|
this.createSettingEl(containerEl, i18nConfig.NotionLinkDisplay, i18nConfig.NotionLinkDisplayDesc, 'toggle', i18nConfig.NotionLinkDisplay, this.plugin.settings.NotionLinkDisplay, 'NotionLinkDisplay')
|
||||||
|
|
||||||
|
|
||||||
|
// add new button
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName("Add New Database")
|
||||||
|
.setDesc("Add New Database")
|
||||||
|
.addButton((button: ButtonComponent): ButtonComponent => {
|
||||||
|
return button
|
||||||
|
.setTooltip("Add New Database")
|
||||||
|
.setIcon("plus")
|
||||||
|
.onClick(async () => {
|
||||||
|
let modal = new SettingModal(this.app, this.plugin, this);
|
||||||
|
|
||||||
|
modal.onClose = () => {
|
||||||
|
if (modal.data.saved) {
|
||||||
|
const dbDetails = {
|
||||||
|
format: modal.data.databaseFormat,
|
||||||
|
fullName: modal.data.databaseFullName,
|
||||||
|
abName: modal.data.databaseAbbreviateName,
|
||||||
|
notionAPI: modal.data.notionAPI,
|
||||||
|
databaseID: modal.data.databaseID,
|
||||||
|
tagButton: modal.data.tagButton,
|
||||||
|
customTitleButton: modal.data.customTitleButton,
|
||||||
|
customTitleName: modal.data.customTitleName,
|
||||||
|
customProperties: modal.data.customProperties,
|
||||||
|
// customValues: modal.data.customValues,
|
||||||
|
saved: modal.data.saved,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.plugin.addDatabaseDetails(dbDetails);
|
||||||
|
|
||||||
|
this.plugin.commands.updateCommand();
|
||||||
|
|
||||||
|
this.display()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modal.open();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// new section to display all created database
|
||||||
|
containerEl.createEl('h2', { text: "Database List" });
|
||||||
|
|
||||||
|
this.databaseEl = containerEl.createDiv('database-list');
|
||||||
|
// list all created database
|
||||||
|
this.showDatabase();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a function to create a div with a style for pop over elements
|
||||||
|
// public createStyleDiv(className: string, commandValue: boolean = false) {
|
||||||
|
// return this.containerEl.createDiv(className, (div) => {
|
||||||
|
// this.updateSettingEl(div, commandValue);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// update the setting display style in the setting tab
|
||||||
|
public updateSettingEl(element: HTMLElement, commandValue: boolean) {
|
||||||
|
element.style.borderTop = commandValue ? "1px solid var(--background-modifier-border)" : "none";
|
||||||
|
element.style.paddingTop = commandValue ? "0.75em" : "0";
|
||||||
|
element.style.display = commandValue ? "block" : "none";
|
||||||
|
element.style.alignItems = "center";
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to add one setting element in the setting tab.
|
||||||
|
public createSettingEl(containerEl: HTMLElement, name: string, desc: string, type: string, placeholder: string, holderValue: any, settingsKey: string) {
|
||||||
|
if (type === 'password') {
|
||||||
|
return new Setting(containerEl)
|
||||||
|
.setName(name)
|
||||||
|
.setDesc(desc)
|
||||||
|
.addText((text) => {
|
||||||
|
text.inputEl.type = type;
|
||||||
|
return text
|
||||||
|
.setPlaceholder(placeholder)
|
||||||
|
.setValue(holderValue)
|
||||||
|
.onChange(async (value) => {
|
||||||
|
this.plugin.settings[settingsKey] = value; // Update the plugin settings directly
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} else if (type === 'toggle') {
|
||||||
|
return new Setting(containerEl)
|
||||||
|
.setName(name)
|
||||||
|
.setDesc(desc)
|
||||||
|
.addToggle((toggle) =>
|
||||||
|
toggle
|
||||||
|
.setValue(holderValue)
|
||||||
|
.onChange(async (value) => {
|
||||||
|
this.plugin.settings[settingsKey] = value; // Update the plugin settings directly
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
await this.plugin.commands.updateCommand();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else if (type === 'text') {
|
||||||
|
return new Setting(containerEl)
|
||||||
|
.setName(name)
|
||||||
|
.setDesc(desc)
|
||||||
|
.addText((text) =>
|
||||||
|
text
|
||||||
|
.setPlaceholder(placeholder)
|
||||||
|
.setValue(holderValue)
|
||||||
|
.onChange(async (value) => {
|
||||||
|
this.plugin.settings[settingsKey] = value; // Update the plugin settings directly
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
await this.plugin.commands.updateCommand();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to show all the database details
|
||||||
|
showDatabase() {
|
||||||
|
this.databaseEl.empty();
|
||||||
|
|
||||||
|
for (let key in this.plugin.settings.databaseDetails) {
|
||||||
|
let dbDetails = this.plugin.settings.databaseDetails[key];
|
||||||
|
|
||||||
|
const databaseDiv = this.databaseEl.createDiv('database-div');
|
||||||
|
|
||||||
|
let settingEl = new Setting(databaseDiv)
|
||||||
|
.setName(`${dbDetails.fullName} (${dbDetails.abName})`)
|
||||||
|
.setDesc(dbDetails.format)
|
||||||
|
|
||||||
|
|
||||||
|
// add a button for preview data
|
||||||
|
settingEl
|
||||||
|
.addButton((button: ButtonComponent): ButtonComponent => {
|
||||||
|
return button
|
||||||
|
.setTooltip("Preview Database")
|
||||||
|
.setIcon("eye")
|
||||||
|
.onClick(async () => {
|
||||||
|
let modal = new PreviewModal(this.app, this.plugin, this, dbDetails);
|
||||||
|
|
||||||
|
modal.open();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// add a button for edit data
|
||||||
|
settingEl
|
||||||
|
.addButton((button: ButtonComponent): ButtonComponent => {
|
||||||
|
return button
|
||||||
|
.setTooltip("Edit Database")
|
||||||
|
.setIcon("pencil")
|
||||||
|
.onClick(async () => {
|
||||||
|
let modal = new EditModal(this.app, this.plugin, this, dbDetails);
|
||||||
|
|
||||||
|
modal.onClose = () => {
|
||||||
|
if (modal.dataTemp.savedTempInd) {
|
||||||
|
const dbDetailsNew: DatabaseDetails = {
|
||||||
|
format: modal.dataTemp.databaseFormatTemp,
|
||||||
|
fullName: modal.dataTemp.databaseFullNameTemp,
|
||||||
|
abName: modal.dataTemp.databaseAbbreviateNameTemp,
|
||||||
|
notionAPI: modal.dataTemp.notionAPITemp,
|
||||||
|
databaseID: modal.dataTemp.databaseIDTemp,
|
||||||
|
tagButton: modal.dataTemp.tagButtonTemp,
|
||||||
|
customTitleButton: modal.dataTemp.customTitleButtonTemp,
|
||||||
|
customTitleName: modal.dataTemp.customTitleNameTemp,
|
||||||
|
customProperties: modal.dataTemp.customPropertiesTemp,
|
||||||
|
// customValues: modal.data.customValues,
|
||||||
|
saved: modal.dataTemp.savedTemp,
|
||||||
|
}
|
||||||
|
|
||||||
|
const dbDetailsPrev: DatabaseDetails = {
|
||||||
|
format: modal.dataPrev.databaseFormatPrev,
|
||||||
|
fullName: modal.dataPrev.databaseFullNamePrev,
|
||||||
|
abName: modal.dataPrev.databaseAbbreviateNamePrev,
|
||||||
|
notionAPI: modal.dataPrev.notionAPIPrev,
|
||||||
|
databaseID: modal.dataPrev.databaseIDPrev,
|
||||||
|
tagButton: modal.dataPrev.tagButtonPrev,
|
||||||
|
customTitleButton: modal.dataPrev.customTitleButtonPrev,
|
||||||
|
customTitleName: modal.dataPrev.customTitleNamePrev,
|
||||||
|
customProperties: modal.dataPrev.customPropertiesPrev,
|
||||||
|
// customValues: modal.data.customValues,
|
||||||
|
saved: modal.dataPrev.savedPrev,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.plugin.deleteDatabaseDetails(dbDetailsPrev);
|
||||||
|
this.plugin.updateDatabaseDetails(dbDetailsNew);
|
||||||
|
|
||||||
|
this.plugin.commands.updateCommand();
|
||||||
|
|
||||||
|
this.display()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modal.open();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
settingEl
|
||||||
|
.addButton((button: ButtonComponent): ButtonComponent => {
|
||||||
|
return button
|
||||||
|
.setTooltip("Delete Database")
|
||||||
|
.setIcon("trash")
|
||||||
|
.onClick(async () => {
|
||||||
|
let modal = new DeleteModal(this.app, this.plugin, this, dbDetails);
|
||||||
|
|
||||||
|
modal.onClose = () => {
|
||||||
|
if (modal.data.deleted) {
|
||||||
|
this.plugin.deleteDatabaseDetails(dbDetails);
|
||||||
|
|
||||||
|
console.log(dbDetails.fullName + " deleted");
|
||||||
|
|
||||||
|
this.plugin.commands.updateCommand();
|
||||||
|
|
||||||
|
this.display()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modal.open();
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
44
src/upload/updateYaml.ts
Normal file
44
src/upload/updateYaml.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { App, Notice, TFile } from "obsidian";
|
||||||
|
import ObsidianSyncNotionPlugin from "../main";
|
||||||
|
import { DatabaseDetails } from "../ui/settingTabs";
|
||||||
|
import { i18nConfig } from "src/lang/I18n";
|
||||||
|
|
||||||
|
export async function updateYamlInfo(
|
||||||
|
yamlContent: string,
|
||||||
|
nowFile: TFile,
|
||||||
|
res: any,
|
||||||
|
app: App,
|
||||||
|
plugin: ObsidianSyncNotionPlugin,
|
||||||
|
dbDetails: DatabaseDetails,
|
||||||
|
) {
|
||||||
|
let { url, id } = res;
|
||||||
|
// replace www to notionID
|
||||||
|
const { notionUser, NotionLinkDisplay } = plugin.settings;
|
||||||
|
const { abName } = dbDetails
|
||||||
|
const notionIDKey = `NotionID-${abName}`;
|
||||||
|
const linkKey = `link-${abName}`;
|
||||||
|
|
||||||
|
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[notionIDKey]) {
|
||||||
|
delete yamlContent[notionIDKey]
|
||||||
|
}
|
||||||
|
if (yamlContent[linkKey]) {
|
||||||
|
delete yamlContent[linkKey]
|
||||||
|
}
|
||||||
|
// add new notionID and link
|
||||||
|
yamlContent[notionIDKey] = id;
|
||||||
|
(NotionLinkDisplay) ? yamlContent[linkKey] = url : null;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(url)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
new Notice(`${i18nConfig.CopyErrorMessage}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
128
src/upload/uploadCommand.ts
Normal file
128
src/upload/uploadCommand.ts
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
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 { 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";
|
||||||
|
|
||||||
|
export async function uploadCommandNext(
|
||||||
|
plugin: ObsidianSyncNotionPlugin,
|
||||||
|
settings: PluginSettings,
|
||||||
|
dbDetails: DatabaseDetails,
|
||||||
|
app: App,
|
||||||
|
) {
|
||||||
|
|
||||||
|
const { notionAPI, databaseID } = dbDetails;
|
||||||
|
|
||||||
|
// Check if the user has set up the Notion API and database ID
|
||||||
|
if (notionAPI === "" || databaseID === "") {
|
||||||
|
const setAPIMessage = i18nConfig["set-api-id"];
|
||||||
|
new Notice(setAPIMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
markDownData,
|
||||||
|
nowFile,
|
||||||
|
emoji,
|
||||||
|
cover,
|
||||||
|
tags,
|
||||||
|
type,
|
||||||
|
slug,
|
||||||
|
stats,
|
||||||
|
category,
|
||||||
|
summary,
|
||||||
|
paword,
|
||||||
|
favicon,
|
||||||
|
datetime
|
||||||
|
} = await getNowFileMarkdownContentNext(app, settings)
|
||||||
|
|
||||||
|
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 { response } = res;
|
||||||
|
if (response.status === 200) {
|
||||||
|
new Notice(`${i18nConfig["sync-preffix"]} ${basename} ${i18nConfig["sync-success"]}`).noticeEl.style.color = "green";
|
||||||
|
} else {
|
||||||
|
new Notice(`${i18nConfig["sync-fail"]} ${basename}`, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function uploadCommandGeneral(
|
||||||
|
plugin: ObsidianSyncNotionPlugin,
|
||||||
|
settings: PluginSettings,
|
||||||
|
dbDetails: DatabaseDetails,
|
||||||
|
app: App,
|
||||||
|
) {
|
||||||
|
|
||||||
|
const { notionAPI, databaseID } = dbDetails;
|
||||||
|
|
||||||
|
// Check if the user has set up the Notion API and database ID
|
||||||
|
if (notionAPI === "" || databaseID === "") {
|
||||||
|
const setAPIMessage = i18nConfig["set-api-id"];
|
||||||
|
new Notice(setAPIMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { markDownData, nowFile, cover, tags } = await getNowFileMarkdownContentGeneral(app, settings)
|
||||||
|
|
||||||
|
if (markDownData) {
|
||||||
|
const { basename } = nowFile;
|
||||||
|
|
||||||
|
const upload = new Upload2NotionGeneral(plugin, dbDetails);
|
||||||
|
const res = await upload.syncMarkdownToNotionGeneral(basename, cover, tags, markDownData, nowFile, this.app);
|
||||||
|
|
||||||
|
const { response } = res;
|
||||||
|
if (response.status === 200) {
|
||||||
|
new Notice(`${i18nConfig["sync-preffix"]} ${basename} ${i18nConfig["sync-success"]}`).noticeEl.style.color = "green";
|
||||||
|
} else {
|
||||||
|
new Notice(`${i18nConfig["sync-fail"]} ${basename}`, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function uploadCommandCustom(
|
||||||
|
plugin: ObsidianSyncNotionPlugin,
|
||||||
|
settings: PluginSettings,
|
||||||
|
dbDetails: DatabaseDetails,
|
||||||
|
app: App,
|
||||||
|
) {
|
||||||
|
|
||||||
|
const { notionAPI, databaseID } = settings;
|
||||||
|
|
||||||
|
// Check if the user has set up the Notion API and database ID
|
||||||
|
if (notionAPI === "" || databaseID === "") {
|
||||||
|
const setAPIMessage = i18nConfig["set-api-id"];
|
||||||
|
new Notice(setAPIMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { markDownData, nowFile, cover, customValues } = await getNowFileMarkdownContentCustom(app, dbDetails)
|
||||||
|
|
||||||
|
if (markDownData) {
|
||||||
|
const { basename } = nowFile;
|
||||||
|
|
||||||
|
const upload = new Upload2NotionCustom(plugin, dbDetails);
|
||||||
|
const res = await upload.syncMarkdownToNotionCustom(cover, customValues, markDownData, nowFile, this.app);
|
||||||
|
|
||||||
|
const { response } = res;
|
||||||
|
if (response.status === 200) {
|
||||||
|
new Notice(`${i18nConfig["sync-preffix"]} ${basename} ${i18nConfig["sync-success"]}`).noticeEl.style.color = "green";
|
||||||
|
} else {
|
||||||
|
new Notice(`${i18nConfig["sync-fail"]} ${basename}`, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
96
src/upload/upload_general/BaseUpload2NotionGeneral.ts
Normal file
96
src/upload/upload_general/BaseUpload2NotionGeneral.ts
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
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}`)
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
}
|
||||||
178
src/upload/upload_general/Upload2NotionGeneral.ts
Normal file
178
src/upload/upload_general/Upload2NotionGeneral.ts
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
import { App, Notice, 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, PluginSettings } from "../../ui/settingTabs";
|
||||||
|
import { UploadBaseGeneral } from "./BaseUpload2NotionGeneral";
|
||||||
|
import { updateYamlInfo } from "../updateYaml";
|
||||||
|
import fetch from 'node-fetch';
|
||||||
|
|
||||||
|
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,
|
||||||
|
) {
|
||||||
|
|
||||||
|
const {
|
||||||
|
databaseID,
|
||||||
|
customTitleButton,
|
||||||
|
customTitleName,
|
||||||
|
tagButton,
|
||||||
|
notionAPI
|
||||||
|
} = this.dbDetails;
|
||||||
|
|
||||||
|
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: 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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
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 {
|
||||||
|
console.log(`Page created: ${data.url}`);
|
||||||
|
console.log(`Page ID: ${data.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
response, // for status code
|
||||||
|
data // for id and url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async syncMarkdownToNotionGeneral(
|
||||||
|
title: string,
|
||||||
|
cover: string,
|
||||||
|
tags: 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;
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/upload/upload_general/getMarkdownGeneral.ts
Normal file
33
src/upload/upload_general/getMarkdownGeneral.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { App, Notice } from "obsidian";
|
||||||
|
import { i18nConfig } from "../../lang/I18n";
|
||||||
|
import { PluginSettings } from "../../ui/settingTabs";
|
||||||
|
|
||||||
|
export async function getNowFileMarkdownContentGeneral(
|
||||||
|
app: App,
|
||||||
|
settings: PluginSettings,
|
||||||
|
) {
|
||||||
|
const nowFile = app.workspace.getActiveFile();
|
||||||
|
let cover = '';
|
||||||
|
let tags = [];
|
||||||
|
|
||||||
|
const FileCache = app.metadataCache.getFileCache(nowFile);
|
||||||
|
try {
|
||||||
|
cover = FileCache.frontmatter.coverurl;
|
||||||
|
tags = FileCache.frontmatter.tags;
|
||||||
|
} catch (error) {
|
||||||
|
new Notice(i18nConfig["set-tags-fail"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nowFile) {
|
||||||
|
const markDownData = await nowFile.vault.read(nowFile);
|
||||||
|
return {
|
||||||
|
markDownData,
|
||||||
|
nowFile,
|
||||||
|
cover,
|
||||||
|
tags,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
new Notice(i18nConfig["open-file"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
96
src/upload/upload_next/BaseUpload2NotionNext.ts
Normal file
96
src/upload/upload_next/BaseUpload2NotionNext.ts
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
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}`)
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
}
|
||||||
334
src/upload/upload_next/Upload2NotionNext.ts
Normal file
334
src/upload/upload_next/Upload2NotionNext.ts
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
import { UploadBaseNext } from "./BaseUpload2NotionNext";
|
||||||
|
import { App, Notice, 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, PluginSettings } from "../../ui/settingTabs";
|
||||||
|
import { updateYamlInfo } from "../updateYaml";
|
||||||
|
import { LIMITS, paragraph } from "@jxpeng98/martian/src/notion";
|
||||||
|
import fetch from 'node-fetch';
|
||||||
|
|
||||||
|
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
|
||||||
|
) {
|
||||||
|
|
||||||
|
const {
|
||||||
|
databaseID,
|
||||||
|
notionAPI
|
||||||
|
} = this.dbDetails
|
||||||
|
|
||||||
|
|
||||||
|
const bodyString: 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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 {
|
||||||
|
console.log(`Page created: ${data.url}`);
|
||||||
|
console.log(`Page ID: ${data.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
response, // for status code
|
||||||
|
data // for id and url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (response && response.status === 200) {
|
||||||
|
await updateYamlInfo(markdown, nowFile, data, app, this.plugin, this.dbDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
60
src/upload/upload_next/getMarkdownNext.ts
Normal file
60
src/upload/upload_next/getMarkdownNext.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { App, Notice } from "obsidian";
|
||||||
|
import { i18nConfig } from "../../lang/I18n";
|
||||||
|
import { PluginSettings } from "../../ui/settingTabs";
|
||||||
|
|
||||||
|
export async function getNowFileMarkdownContentNext(
|
||||||
|
app: App,
|
||||||
|
settings: PluginSettings,
|
||||||
|
) {
|
||||||
|
const nowFile = app.workspace.getActiveFile();
|
||||||
|
let emoji = '';
|
||||||
|
let cover = '';
|
||||||
|
let tags = [];
|
||||||
|
let type = '';
|
||||||
|
let slug = '';
|
||||||
|
let stats = '';
|
||||||
|
let status = '';
|
||||||
|
let category = '';
|
||||||
|
let summary = '';
|
||||||
|
let paword = '';
|
||||||
|
let favicon = '';
|
||||||
|
let datetime = '';
|
||||||
|
|
||||||
|
const FileCache = app.metadataCache.getFileCache(nowFile);
|
||||||
|
try {
|
||||||
|
emoji = FileCache.frontmatter.titleicon;
|
||||||
|
cover = FileCache.frontmatter.coverurl;
|
||||||
|
tags = FileCache.frontmatter.tags;
|
||||||
|
type = FileCache.frontmatter.type;
|
||||||
|
slug = FileCache.frontmatter.slug;
|
||||||
|
stats = FileCache.frontmatter.stats || FileCache.frontmatter.status;
|
||||||
|
category = FileCache.frontmatter.category;
|
||||||
|
summary = FileCache.frontmatter.summary;
|
||||||
|
paword = FileCache.frontmatter.password;
|
||||||
|
favicon = FileCache.frontmatter.icon;
|
||||||
|
datetime = FileCache.frontmatter.date;
|
||||||
|
} catch (error) {
|
||||||
|
new Notice(i18nConfig["set-tags-fail"]);
|
||||||
|
}
|
||||||
|
if (nowFile) {
|
||||||
|
const markDownData = await nowFile.vault.read(nowFile);
|
||||||
|
return {
|
||||||
|
markDownData,
|
||||||
|
nowFile,
|
||||||
|
emoji,
|
||||||
|
cover,
|
||||||
|
tags,
|
||||||
|
type,
|
||||||
|
slug,
|
||||||
|
stats,
|
||||||
|
category,
|
||||||
|
summary,
|
||||||
|
paword,
|
||||||
|
favicon,
|
||||||
|
datetime,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
new Notice(i18nConfig["open-file"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/upload/upoload_custom/BaseUpload2NotionCustom.ts
Normal file
53
src/upload/upoload_custom/BaseUpload2NotionCustom.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
256
src/upload/upoload_custom/Upload2NotionCustom.ts
Normal file
256
src/upload/upoload_custom/Upload2NotionCustom.ts
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
import {App, Notice, TFile} from "obsidian";
|
||||||
|
import {markdownToBlocks} from "@jxpeng98/martian";
|
||||||
|
import * as yamlFrontMatter from "yaml-front-matter";
|
||||||
|
// import * as yaml from "yaml"
|
||||||
|
import MyPlugin from "src/main";
|
||||||
|
import {DatabaseDetails, PluginSettings} from "../../ui/settingTabs";
|
||||||
|
import {updateYamlInfo} from "../updateYaml";
|
||||||
|
import {UploadBaseCustom} from "./BaseUpload2NotionCustom";
|
||||||
|
import fetch from 'node-fetch';
|
||||||
|
|
||||||
|
|
||||||
|
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<string, 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(cover, customValues, childArr);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPage(
|
||||||
|
cover: string,
|
||||||
|
customValues: Record<string, string>,
|
||||||
|
childArr: any,
|
||||||
|
) {
|
||||||
|
|
||||||
|
const {
|
||||||
|
databaseID,
|
||||||
|
customProperties,
|
||||||
|
notionAPI
|
||||||
|
} = this.dbDetails;
|
||||||
|
|
||||||
|
const bodyString: any = this.buildBodyString(customProperties, customValues, 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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
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 {
|
||||||
|
console.log(`Page created: ${data.url}`);
|
||||||
|
console.log(`Page ID: ${data.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
response, // for status code
|
||||||
|
data // for id and url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async syncMarkdownToNotionCustom(
|
||||||
|
cover: string,
|
||||||
|
customValues: Record<string, string>,
|
||||||
|
markdown: string,
|
||||||
|
nowFile: TFile,
|
||||||
|
app: App,
|
||||||
|
): Promise<any> {
|
||||||
|
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 frontmasster =
|
||||||
|
app.metadataCache.getFileCache(nowFile)?.frontmatter;
|
||||||
|
const {abName} = this.dbDetails
|
||||||
|
const notionIDKey = `NotionID-${abName}`;
|
||||||
|
const notionID = frontmasster ? frontmasster[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<string, any>) {
|
||||||
|
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<string, string>,
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
57
src/upload/upoload_custom/getMarkdownCustom.ts
Normal file
57
src/upload/upoload_custom/getMarkdownCustom.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { App, Notice } from "obsidian";
|
||||||
|
import { i18nConfig } from "../../lang/I18n";
|
||||||
|
import { DatabaseDetails } from "../../ui/settingTabs";
|
||||||
|
|
||||||
|
export async function getNowFileMarkdownContentCustom(
|
||||||
|
app: App,
|
||||||
|
dbDetails: DatabaseDetails,
|
||||||
|
) {
|
||||||
|
const nowFile = app.workspace.getActiveFile();
|
||||||
|
if (!nowFile) {
|
||||||
|
new Notice(i18nConfig["open-file"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let cover = '';
|
||||||
|
let customValues: Record<string, any> = {};
|
||||||
|
|
||||||
|
const FileCache = app.metadataCache.getFileCache(nowFile);
|
||||||
|
try {
|
||||||
|
cover = FileCache.frontmatter.coverurl;
|
||||||
|
|
||||||
|
// Get custom property names from dbDetails excluding the title type property
|
||||||
|
const customPropertyNames = dbDetails.customProperties
|
||||||
|
.filter(property => property.customType !== 'title') // Exclude 'title' type property
|
||||||
|
.map(property => property.customName);
|
||||||
|
|
||||||
|
// Extract custom values from the front matter based on the names
|
||||||
|
customPropertyNames.forEach(propertyName => {
|
||||||
|
if (FileCache.frontmatter && FileCache.frontmatter[propertyName] !== undefined) {
|
||||||
|
customValues[propertyName] = FileCache.frontmatter[propertyName];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if any of the customProperties has a customType of '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) {
|
||||||
|
customValues[titleProperty.customName] = nowFile.basename; // Use 'basename' for the file name without extension
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
new Notice(i18nConfig["set-tags-fail"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nowFile) {
|
||||||
|
const markDownData = await nowFile.vault.read(nowFile);
|
||||||
|
return {
|
||||||
|
markDownData,
|
||||||
|
nowFile,
|
||||||
|
cover,
|
||||||
|
customValues,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
new Notice(i18nConfig["open-file"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
/* Sets all the text color to red! */
|
|
||||||
/* body {
|
|
||||||
color: red;
|
|
||||||
} */
|
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"inlineSourceMap": true,
|
"inlineSourceMap": true,
|
||||||
"inlineSources": true,
|
"inlineSources": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"target": "ES6",
|
"target": "ES6",
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
@@ -17,6 +19,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.ts"
|
"**/*.ts",
|
||||||
, "Upload2Notion.ts" ]
|
"src/Upload2Notion.ts",
|
||||||
|
"src/*.ts"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
24
update-version.js
Normal file
24
update-version.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// update package.json version
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
|
const packageJson = JSON.parse(fs.readFileSync("./package.json", "utf8"));
|
||||||
|
|
||||||
|
console.log(packageJson.version);
|
||||||
|
|
||||||
|
// get node args
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
|
packageJson.version = args[0];
|
||||||
|
|
||||||
|
console.log(packageJson.version);
|
||||||
|
|
||||||
|
// write package.json
|
||||||
|
fs.writeFileSync("./package.json", JSON.stringify(packageJson, null, 2));
|
||||||
|
|
||||||
|
// read manifest.json
|
||||||
|
const manifestJson = JSON.parse(fs.readFileSync("./manifest.json", "utf8"));
|
||||||
|
|
||||||
|
manifestJson.version = args[0];
|
||||||
|
|
||||||
|
// write manifest.json
|
||||||
|
fs.writeFileSync("./manifest.json", JSON.stringify(manifestJson, null, 2));
|
||||||
Reference in New Issue
Block a user