feat: 增加导出话题至Notion的功能 (#1331)
* feat: 新增导出至Notion的选项 * fix:添加多语言支持 * fix:添加提示语的多语言支持,以及防止重复导入的状态 * fix:修复多语言错误及调整UI样式统一
This commit is contained in:
parent
1d82552491
commit
50cc1c6b5a
@ -61,6 +61,7 @@
|
|||||||
"@llm-tools/embedjs-loader-web": "^0.1.25",
|
"@llm-tools/embedjs-loader-web": "^0.1.25",
|
||||||
"@llm-tools/embedjs-loader-xml": "^0.1.25",
|
"@llm-tools/embedjs-loader-xml": "^0.1.25",
|
||||||
"@llm-tools/embedjs-openai": "^0.1.25",
|
"@llm-tools/embedjs-openai": "^0.1.25",
|
||||||
|
"@notionhq/client": "^2.2.15",
|
||||||
"@types/react-infinite-scroll-component": "^5.0.0",
|
"@types/react-infinite-scroll-component": "^5.0.0",
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"apache-arrow": "^18.1.0",
|
"apache-arrow": "^18.1.0",
|
||||||
|
|||||||
@ -115,6 +115,7 @@
|
|||||||
"topics.edit.placeholder": "Enter new name",
|
"topics.edit.placeholder": "Enter new name",
|
||||||
"topics.edit.title": "Edit Name",
|
"topics.edit.title": "Edit Name",
|
||||||
"topics.export.image": "Export as image",
|
"topics.export.image": "Export as image",
|
||||||
|
"topics.export.notion": "Export to Notion",
|
||||||
"topics.export.md": "Export as markdown",
|
"topics.export.md": "Export as markdown",
|
||||||
"topics.export.title": "Export",
|
"topics.export.title": "Export",
|
||||||
"topics.export.word": "Export as Word",
|
"topics.export.word": "Export as Word",
|
||||||
@ -296,7 +297,11 @@
|
|||||||
"error.get_embedding_dimensions": "Failed to get embedding dimensions",
|
"error.get_embedding_dimensions": "Failed to get embedding dimensions",
|
||||||
"group.delete.title": "Delete Group Message",
|
"group.delete.title": "Delete Group Message",
|
||||||
"group.delete.content": "Deleting a group message will delete the user's question and all assistant's answers",
|
"group.delete.content": "Deleting a group message will delete the user's question and all assistant's answers",
|
||||||
"mention.title": "Switch model answer"
|
"mention.title": "Switch model answer",
|
||||||
|
"error.notion.export": "Notion import failed",
|
||||||
|
"error.notion.no_api_key": "Notion ApiKey or Notion DatabaseID is not configured",
|
||||||
|
"success.notion.export": "Notion import successful",
|
||||||
|
"warn.notion.exporting": "Notion is importing, please do not import repeatedly"
|
||||||
},
|
},
|
||||||
"minapp": {
|
"minapp": {
|
||||||
"title": "MinApp",
|
"title": "MinApp",
|
||||||
@ -421,7 +426,11 @@
|
|||||||
"webdav.autoSync.off": "Off",
|
"webdav.autoSync.off": "Off",
|
||||||
"webdav.noSync": "Waiting for next backup",
|
"webdav.noSync": "Waiting for next backup",
|
||||||
"webdav.syncError": "Backup Error",
|
"webdav.syncError": "Backup Error",
|
||||||
"webdav.lastSync": "Last Backup"
|
"webdav.lastSync": "Last Backup",
|
||||||
|
"notion.api_key":"Notion API Key",
|
||||||
|
"notion.database_id":"Notion Database ID",
|
||||||
|
"notion.title":"Notion Configuration"
|
||||||
|
|
||||||
},
|
},
|
||||||
"quickAssistant": {
|
"quickAssistant": {
|
||||||
"title": "Quick Assistant",
|
"title": "Quick Assistant",
|
||||||
|
|||||||
@ -111,6 +111,7 @@
|
|||||||
"topics.edit.title": "名前を編集",
|
"topics.edit.title": "名前を編集",
|
||||||
"topics.export.image": "画像としてエクスポート",
|
"topics.export.image": "画像としてエクスポート",
|
||||||
"topics.export.md": "Markdownとしてエクスポート",
|
"topics.export.md": "Markdownとしてエクスポート",
|
||||||
|
"topics.export.notion": "Notion にエクスポート",
|
||||||
"topics.export.title": "エクスポート",
|
"topics.export.title": "エクスポート",
|
||||||
"topics.export.word": "Wordとしてエクスポート",
|
"topics.export.word": "Wordとしてエクスポート",
|
||||||
"topics.list": "トピックリスト",
|
"topics.list": "トピックリスト",
|
||||||
@ -290,7 +291,11 @@
|
|||||||
"error.get_embedding_dimensions": "埋込み次元を取得できませんでした",
|
"error.get_embedding_dimensions": "埋込み次元を取得できませんでした",
|
||||||
"group.delete.title": "分組メッセージを削除",
|
"group.delete.title": "分組メッセージを削除",
|
||||||
"group.delete.content": "分組メッセージを削除するとユーザーの質問と助け手の回答がすべて削除されます",
|
"group.delete.content": "分組メッセージを削除するとユーザーの質問と助け手の回答がすべて削除されます",
|
||||||
"mention.title": "モデルを切り替える"
|
"mention.title": "モデルを切り替える",
|
||||||
|
"error.notion.export": "Notion インポートに失敗",
|
||||||
|
"error.notion.no_api_key": "Notion ApiKey または Notion DatabaseID が設定されていません",
|
||||||
|
"success.notion.export": "Notion へのインポートに成功",
|
||||||
|
"warn.notion.exporting": "Notion 正在インポート中です。重複インポートしないでください。"
|
||||||
},
|
},
|
||||||
"minapp": {
|
"minapp": {
|
||||||
"title": "ミニアプリ",
|
"title": "ミニアプリ",
|
||||||
@ -413,7 +418,10 @@
|
|||||||
"webdav.autoSync.off": "オフ",
|
"webdav.autoSync.off": "オフ",
|
||||||
"webdav.noSync": "次回のバックアップを待っています",
|
"webdav.noSync": "次回のバックアップを待っています",
|
||||||
"webdav.syncError": "バックアップエラー",
|
"webdav.syncError": "バックアップエラー",
|
||||||
"webdav.lastSync": "最終同期"
|
"webdav.lastSync": "最終同期",
|
||||||
|
"notion.api_key":"Notion APIキー",
|
||||||
|
"notion.database_id":"Notion データベースID",
|
||||||
|
"notion.title":"Notion 設定"
|
||||||
},
|
},
|
||||||
"quickAssistant": {
|
"quickAssistant": {
|
||||||
"title": "クイックアシスタント",
|
"title": "クイックアシスタント",
|
||||||
|
|||||||
@ -111,6 +111,7 @@
|
|||||||
"topics.edit.title": "Редактировать заголовок",
|
"topics.edit.title": "Редактировать заголовок",
|
||||||
"topics.export.image": "Экспорт как изображение",
|
"topics.export.image": "Экспорт как изображение",
|
||||||
"topics.export.md": "Экспорт как markdown",
|
"topics.export.md": "Экспорт как markdown",
|
||||||
|
"topics.export.notion": "Экспорт в Notion",
|
||||||
"topics.export.title": "Экспорт",
|
"topics.export.title": "Экспорт",
|
||||||
"topics.export.word": "Экспорт как Word",
|
"topics.export.word": "Экспорт как Word",
|
||||||
"topics.list": "Список топиков",
|
"topics.list": "Список топиков",
|
||||||
@ -291,7 +292,11 @@
|
|||||||
"error.get_embedding_dimensions": "Не удалось получить размерность встраивания",
|
"error.get_embedding_dimensions": "Не удалось получить размерность встраивания",
|
||||||
"group.delete.title": "Удалить группу сообщений",
|
"group.delete.title": "Удалить группу сообщений",
|
||||||
"group.delete.content": "Удаление группы сообщений удалит пользовательский вопрос и все ответы помощника",
|
"group.delete.content": "Удаление группы сообщений удалит пользовательский вопрос и все ответы помощника",
|
||||||
"mention.title": "Переключить модель ответа"
|
"mention.title": "Переключить модель ответа",
|
||||||
|
"error.notion.export": "Импорт в Notion не удался",
|
||||||
|
"error.notion.no_api_key": "Notion ApiKey или Notion DatabaseID не настроен",
|
||||||
|
"success.notion.export": "Импорт в Notion выполнен успешно",
|
||||||
|
"warn.notion.exporting": "Идет импорт в Notion, пожалуйста, не повторяйте импорт"
|
||||||
},
|
},
|
||||||
"minapp": {
|
"minapp": {
|
||||||
"title": "Встроенные приложения",
|
"title": "Встроенные приложения",
|
||||||
@ -414,7 +419,10 @@
|
|||||||
"webdav.autoSync.off": "Выключено",
|
"webdav.autoSync.off": "Выключено",
|
||||||
"webdav.noSync": "Ожидание следующего резервного копирования",
|
"webdav.noSync": "Ожидание следующего резервного копирования",
|
||||||
"webdav.syncError": "Ошибка резервного копирования",
|
"webdav.syncError": "Ошибка резервного копирования",
|
||||||
"webdav.lastSync": "Последняя синхронизация"
|
"webdav.lastSync": "Последняя синхронизация",
|
||||||
|
"notion.api_key":"Ключ API Notion",
|
||||||
|
"notion.database_id":"ID базы данных Notion",
|
||||||
|
"notion.title":"Настройки Notion"
|
||||||
},
|
},
|
||||||
"quickAssistant": {
|
"quickAssistant": {
|
||||||
"title": "Быстрый помощник",
|
"title": "Быстрый помощник",
|
||||||
|
|||||||
@ -116,6 +116,7 @@
|
|||||||
"topics.edit.title": "编辑话题名",
|
"topics.edit.title": "编辑话题名",
|
||||||
"topics.export.image": "导出为图片",
|
"topics.export.image": "导出为图片",
|
||||||
"topics.export.md": "导出为 Markdown",
|
"topics.export.md": "导出为 Markdown",
|
||||||
|
"topics.export.notion": "导出到 Notion",
|
||||||
"topics.export.title": "导出",
|
"topics.export.title": "导出",
|
||||||
"topics.export.word": "导出为 Word",
|
"topics.export.word": "导出为 Word",
|
||||||
"topics.list": "话题列表",
|
"topics.list": "话题列表",
|
||||||
@ -297,7 +298,12 @@
|
|||||||
"error.get_embedding_dimensions": "获取嵌入维度失败",
|
"error.get_embedding_dimensions": "获取嵌入维度失败",
|
||||||
"group.delete.title": "删除分组消息",
|
"group.delete.title": "删除分组消息",
|
||||||
"group.delete.content": "删除分组消息会删除用户提问和所有助手的回答",
|
"group.delete.content": "删除分组消息会删除用户提问和所有助手的回答",
|
||||||
"mention.title": "切换模型回答"
|
"mention.title": "切换模型回答",
|
||||||
|
"error.notion.export":"Notion 导入失败",
|
||||||
|
"error.notion.no_api_key":"未配置Notion ApiKey或Notion DatabaseID",
|
||||||
|
"success.notion.export":"导入Notion成功",
|
||||||
|
"warn.notion.exporting":"Notion正在导入,请勿重复导入"
|
||||||
|
|
||||||
},
|
},
|
||||||
"minapp": {
|
"minapp": {
|
||||||
"title": "小程序",
|
"title": "小程序",
|
||||||
@ -420,7 +426,10 @@
|
|||||||
"webdav.autoSync.off": "关闭",
|
"webdav.autoSync.off": "关闭",
|
||||||
"webdav.noSync": "等待下次备份",
|
"webdav.noSync": "等待下次备份",
|
||||||
"webdav.syncError": "备份错误",
|
"webdav.syncError": "备份错误",
|
||||||
"webdav.lastSync": "上次备份时间"
|
"webdav.lastSync": "上次备份时间",
|
||||||
|
"notion.api_key":"Notion 密钥",
|
||||||
|
"notion.database_id":"Notion 数据库ID",
|
||||||
|
"notion.title":"Notion 配置"
|
||||||
},
|
},
|
||||||
"quickAssistant": {
|
"quickAssistant": {
|
||||||
"title": "快捷助手",
|
"title": "快捷助手",
|
||||||
|
|||||||
@ -116,6 +116,7 @@
|
|||||||
"topics.edit.title": "編輯名稱",
|
"topics.edit.title": "編輯名稱",
|
||||||
"topics.export.image": "匯出為圖片",
|
"topics.export.image": "匯出為圖片",
|
||||||
"topics.export.md": "匯出為 Markdown",
|
"topics.export.md": "匯出為 Markdown",
|
||||||
|
"topics.export.notion": "匯出到 Notion",
|
||||||
"topics.export.title": "匯出",
|
"topics.export.title": "匯出",
|
||||||
"topics.export.word": "導出為 Word",
|
"topics.export.word": "導出為 Word",
|
||||||
"topics.list": "話題列表",
|
"topics.list": "話題列表",
|
||||||
@ -296,7 +297,11 @@
|
|||||||
"error.get_embedding_dimensions": "獲取嵌入維度失敗",
|
"error.get_embedding_dimensions": "獲取嵌入維度失敗",
|
||||||
"group.delete.title": "刪除分組消息",
|
"group.delete.title": "刪除分組消息",
|
||||||
"group.delete.content": "刪除分組消息會刪除用戶提問和所有助手的回答",
|
"group.delete.content": "刪除分組消息會刪除用戶提問和所有助手的回答",
|
||||||
"mention.title": "切換模型回答"
|
"mention.title": "切換模型回答",
|
||||||
|
"error.notion.export": "Notion 匯入失敗",
|
||||||
|
"error.notion.no_api_key": "未配置 Notion ApiKey 或 Notion DatabaseID",
|
||||||
|
"success.notion.export": "匯入 Notion 成功",
|
||||||
|
"warn.notion.exporting": "Notion 正在匯入,請勿重複匯入"
|
||||||
},
|
},
|
||||||
"minapp": {
|
"minapp": {
|
||||||
"title": "小程序",
|
"title": "小程序",
|
||||||
@ -419,7 +424,10 @@
|
|||||||
"webdav.autoSync.off": "關閉",
|
"webdav.autoSync.off": "關閉",
|
||||||
"webdav.noSync": "等待下次備份",
|
"webdav.noSync": "等待下次備份",
|
||||||
"webdav.syncError": "備份錯誤",
|
"webdav.syncError": "備份錯誤",
|
||||||
"webdav.lastSync": "上次同步時間"
|
"webdav.lastSync": "上次同步時間",
|
||||||
|
"notion.api_key": "Notion 金鑰",
|
||||||
|
"notion.database_id": "Notion 資料庫 ID",
|
||||||
|
"notion.title": "Notion 配置"
|
||||||
},
|
},
|
||||||
"quickAssistant": {
|
"quickAssistant": {
|
||||||
"title": "快捷助手",
|
"title": "快捷助手",
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
|||||||
import store from '@renderer/store'
|
import store from '@renderer/store'
|
||||||
import { setGenerating } from '@renderer/store/runtime'
|
import { setGenerating } from '@renderer/store/runtime'
|
||||||
import { Assistant, Topic } from '@renderer/types'
|
import { Assistant, Topic } from '@renderer/types'
|
||||||
import { exportTopicAsMarkdown, topicToMarkdown } from '@renderer/utils/export'
|
import { exportTopicAsMarkdown, exportTopicToNotion, topicToMarkdown } from '@renderer/utils/export'
|
||||||
import { Dropdown, MenuProps } from 'antd'
|
import { Dropdown, MenuProps } from 'antd'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { findIndex } from 'lodash'
|
import { findIndex } from 'lodash'
|
||||||
@ -133,6 +133,7 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
|
|||||||
key: 'markdown',
|
key: 'markdown',
|
||||||
onClick: () => exportTopicAsMarkdown(topic)
|
onClick: () => exportTopicAsMarkdown(topic)
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
label: t('chat.topics.export.word'),
|
label: t('chat.topics.export.word'),
|
||||||
key: 'word',
|
key: 'word',
|
||||||
@ -140,7 +141,12 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
|
|||||||
const markdown = await topicToMarkdown(topic)
|
const markdown = await topicToMarkdown(topic)
|
||||||
window.api.export.toWord(markdown, topic.name)
|
window.api.export.toWord(markdown, topic.name)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
label: t('chat.topics.export.notion'),
|
||||||
|
key: 'notion',
|
||||||
|
onClick: () => exportTopicToNotion(topic)
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -2,15 +2,72 @@ import { FileSearchOutlined, FolderOpenOutlined, SaveOutlined } from '@ant-desig
|
|||||||
import { HStack } from '@renderer/components/Layout'
|
import { HStack } from '@renderer/components/Layout'
|
||||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
import { backup, reset, restore } from '@renderer/services/BackupService'
|
import { backup, reset, restore } from '@renderer/services/BackupService'
|
||||||
|
import { RootState, useAppDispatch } from '@renderer/store'
|
||||||
|
import { setNotionApiKey, setNotionDatabaseID } from '@renderer/store/settings'
|
||||||
import { AppInfo } from '@renderer/types'
|
import { AppInfo } from '@renderer/types'
|
||||||
import { Button, Modal, Typography } from 'antd'
|
import { Button,Modal, Typography } from 'antd'
|
||||||
|
import Input from 'antd/es/input/Input'
|
||||||
import { FC, useEffect, useState } from 'react'
|
import { FC, useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
||||||
import WebDavSettings from './WebDavSettings'
|
import WebDavSettings from './WebDavSettings'
|
||||||
|
|
||||||
|
// 新增的 NotionSettings 组件
|
||||||
|
const NotionSettings: FC = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { theme } = useTheme()
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
|
// 这里可以添加 Notion 相关的状态和逻辑
|
||||||
|
// 例如:
|
||||||
|
const notionApiKey = useSelector((state: RootState) => state.settings.notionApiKey);
|
||||||
|
const notionDatabaseID = useSelector((state: RootState) => state.settings.notionDatabaseID);
|
||||||
|
|
||||||
|
const handleNotionTokenChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
dispatch(setNotionApiKey(e.target.value))
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNotionDatabaseIdChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
dispatch(setNotionDatabaseID(e.target.value))
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingGroup theme={theme}>
|
||||||
|
<SettingTitle>{t('settings.data.notion.title')}</SettingTitle>
|
||||||
|
<SettingDivider />
|
||||||
|
<SettingRow>
|
||||||
|
<SettingRowTitle>{t('settings.data.notion.api_key')}</SettingRowTitle>
|
||||||
|
<HStack alignItems="center" gap="5px">
|
||||||
|
<Input.Password
|
||||||
|
type="text"
|
||||||
|
value={notionApiKey || ''}
|
||||||
|
onChange={handleNotionTokenChange}
|
||||||
|
onBlur={handleNotionTokenChange}
|
||||||
|
style={{ width: 250 }}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
</SettingRow>
|
||||||
|
<SettingDivider /> {/* 添加分割线 */}
|
||||||
|
<SettingRow>
|
||||||
|
<SettingRowTitle>{t('settings.data.notion.database_id')}</SettingRowTitle>
|
||||||
|
<HStack alignItems="center" gap="5px">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
value={notionDatabaseID || ''}
|
||||||
|
onChange={handleNotionDatabaseIdChange}
|
||||||
|
onBlur={handleNotionDatabaseIdChange}
|
||||||
|
style={{ width: 250 }}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
</SettingRow>
|
||||||
|
</SettingGroup>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const DataSettings: FC = () => {
|
const DataSettings: FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [appInfo, setAppInfo] = useState<AppInfo>()
|
const [appInfo, setAppInfo] = useState<AppInfo>()
|
||||||
@ -79,6 +136,7 @@ const DataSettings: FC = () => {
|
|||||||
<SettingGroup theme={theme}>
|
<SettingGroup theme={theme}>
|
||||||
<WebDavSettings />
|
<WebDavSettings />
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
|
<NotionSettings />
|
||||||
<SettingGroup theme={theme}>
|
<SettingGroup theme={theme}>
|
||||||
<SettingTitle>{t('settings.data.data.title')}</SettingTitle>
|
<SettingTitle>{t('settings.data.data.title')}</SettingTitle>
|
||||||
<SettingDivider />
|
<SettingDivider />
|
||||||
@ -107,6 +165,7 @@ const DataSettings: FC = () => {
|
|||||||
</HStack>
|
</HStack>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
|
|
||||||
</SettingContainer>
|
</SettingContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,11 @@ export interface RuntimeState {
|
|||||||
resourcesPath: string
|
resourcesPath: string
|
||||||
update: UpdateState
|
update: UpdateState
|
||||||
webdavSync: WebDAVSyncState
|
webdavSync: WebDAVSyncState
|
||||||
|
export: ExportState
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExportState {
|
||||||
|
isExporting: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: RuntimeState = {
|
const initialState: RuntimeState = {
|
||||||
@ -45,6 +50,9 @@ const initialState: RuntimeState = {
|
|||||||
lastSyncTime: null,
|
lastSyncTime: null,
|
||||||
syncing: false,
|
syncing: false,
|
||||||
lastSyncError: null
|
lastSyncError: null
|
||||||
|
},
|
||||||
|
export: {
|
||||||
|
isExporting: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +83,11 @@ const runtimeSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setWebDAVSyncState: (state, action: PayloadAction<Partial<WebDAVSyncState>>) => {
|
setWebDAVSyncState: (state, action: PayloadAction<Partial<WebDAVSyncState>>) => {
|
||||||
state.webdavSync = { ...state.webdavSync, ...action.payload }
|
state.webdavSync = { ...state.webdavSync, ...action.payload }
|
||||||
}
|
},
|
||||||
|
setExportState: (state, action: PayloadAction<Partial<ExportState>>) => {
|
||||||
|
state.export = { ...state.export, ...action.payload }
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -87,7 +99,8 @@ export const {
|
|||||||
setFilesPath,
|
setFilesPath,
|
||||||
setResourcesPath,
|
setResourcesPath,
|
||||||
setUpdateState,
|
setUpdateState,
|
||||||
setWebDAVSyncState
|
setWebDAVSyncState,
|
||||||
|
setExportState
|
||||||
} = runtimeSlice.actions
|
} = runtimeSlice.actions
|
||||||
|
|
||||||
export default runtimeSlice.reducer
|
export default runtimeSlice.reducer
|
||||||
|
|||||||
@ -65,6 +65,8 @@ export interface SettingsState {
|
|||||||
enableQuickAssistant: boolean
|
enableQuickAssistant: boolean
|
||||||
clickTrayToShowQuickAssistant: boolean
|
clickTrayToShowQuickAssistant: boolean
|
||||||
multiModelMessageStyle: MultiModelMessageStyle
|
multiModelMessageStyle: MultiModelMessageStyle
|
||||||
|
notionDatabaseID: string | null
|
||||||
|
notionApiKey: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MultiModelMessageStyle = 'horizontal' | 'vertical' | 'fold'
|
export type MultiModelMessageStyle = 'horizontal' | 'vertical' | 'fold'
|
||||||
@ -115,7 +117,9 @@ const initialState: SettingsState = {
|
|||||||
narrowMode: false,
|
narrowMode: false,
|
||||||
enableQuickAssistant: false,
|
enableQuickAssistant: false,
|
||||||
clickTrayToShowQuickAssistant: false,
|
clickTrayToShowQuickAssistant: false,
|
||||||
multiModelMessageStyle: 'fold'
|
multiModelMessageStyle: 'fold',
|
||||||
|
notionDatabaseID: '',
|
||||||
|
notionApiKey: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingsSlice = createSlice({
|
const settingsSlice = createSlice({
|
||||||
@ -263,6 +267,12 @@ const settingsSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setMultiModelMessageStyle: (state, action: PayloadAction<'horizontal' | 'vertical' | 'fold'>) => {
|
setMultiModelMessageStyle: (state, action: PayloadAction<'horizontal' | 'vertical' | 'fold'>) => {
|
||||||
state.multiModelMessageStyle = action.payload
|
state.multiModelMessageStyle = action.payload
|
||||||
|
},
|
||||||
|
setNotionDatabaseID: (state, action: PayloadAction<string>) => {
|
||||||
|
state.notionDatabaseID = action.payload
|
||||||
|
},
|
||||||
|
setNotionApiKey: (state, action: PayloadAction<string>) => {
|
||||||
|
state.notionApiKey = action.payload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -312,7 +322,9 @@ export const {
|
|||||||
setNarrowMode,
|
setNarrowMode,
|
||||||
setClickTrayToShowQuickAssistant,
|
setClickTrayToShowQuickAssistant,
|
||||||
setEnableQuickAssistant,
|
setEnableQuickAssistant,
|
||||||
setMultiModelMessageStyle
|
setMultiModelMessageStyle,
|
||||||
|
setNotionDatabaseID,
|
||||||
|
setNotionApiKey
|
||||||
} = settingsSlice.actions
|
} = settingsSlice.actions
|
||||||
|
|
||||||
export default settingsSlice.reducer
|
export default settingsSlice.reducer
|
||||||
|
|||||||
@ -1,4 +1,8 @@
|
|||||||
|
import { Client } from '@notionhq/client'
|
||||||
import db from '@renderer/databases'
|
import db from '@renderer/databases'
|
||||||
|
import i18n from '@renderer/i18n'
|
||||||
|
import store from '@renderer/store'
|
||||||
|
import { setExportState } from '@renderer/store/runtime'
|
||||||
import { Message, Topic } from '@renderer/types'
|
import { Message, Topic } from '@renderer/types'
|
||||||
|
|
||||||
export const messageToMarkdown = (message: Message) => {
|
export const messageToMarkdown = (message: Message) => {
|
||||||
@ -29,3 +33,56 @@ export const exportTopicAsMarkdown = async (topic: Topic) => {
|
|||||||
const markdown = await topicToMarkdown(topic)
|
const markdown = await topicToMarkdown(topic)
|
||||||
window.api.file.save(fileName, markdown)
|
window.api.file.save(fileName, markdown)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const exportTopicToNotion = async (topic: Topic) => {
|
||||||
|
const { isExporting } = store.getState().runtime.export
|
||||||
|
if (isExporting) {
|
||||||
|
window.message.warning({ content: i18n.t('message.warn.notion.exporting'), key: 'notion-exporting' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setExportState({
|
||||||
|
isExporting: true
|
||||||
|
})
|
||||||
|
const { notionDatabaseID, notionApiKey } = store.getState().settings
|
||||||
|
if (!notionApiKey || !notionDatabaseID) {
|
||||||
|
window.message.error({ content: i18n.t('message.error.notion.no_api_key'), key: 'notion-no-apikey-error' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const notion = new Client({ auth: notionApiKey });
|
||||||
|
const markdown = await topicToMarkdown(topic);
|
||||||
|
const requestBody = JSON.stringify({ md: markdown })
|
||||||
|
|
||||||
|
const res = await fetch('https://md2notion.hilars.dev', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: requestBody
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
const notionBlocks = data;
|
||||||
|
|
||||||
|
const response = await notion.pages.create({
|
||||||
|
parent: { database_id: notionDatabaseID },
|
||||||
|
properties: {
|
||||||
|
Name: {
|
||||||
|
title: [{ text: { content: topic.name } }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
children: notionBlocks // 使用转换后的块
|
||||||
|
});
|
||||||
|
|
||||||
|
window.message.success({ content: i18n.t('message.success.notion.export'), key: 'notion-success' })
|
||||||
|
return response
|
||||||
|
|
||||||
|
} catch (error:any) {
|
||||||
|
window.message.error({ content: i18n.t('message.error.notion.export'), key: 'notion-error' })
|
||||||
|
return null
|
||||||
|
} finally {
|
||||||
|
setExportState({
|
||||||
|
isExporting: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
15
yarn.lock
15
yarn.lock
@ -1776,6 +1776,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@notionhq/client@npm:^2.2.15":
|
||||||
|
version: 2.2.15
|
||||||
|
resolution: "@notionhq/client@npm:2.2.15"
|
||||||
|
dependencies:
|
||||||
|
"@types/node-fetch": "npm:^2.5.10"
|
||||||
|
node-fetch: "npm:^2.6.1"
|
||||||
|
checksum: 10c0/4153c2e5b47d2ba141d025f2753d0e79ca9b9f25bd8bbdfa9dbf74fe4c2e157ea7964c59387d05163972c4575830bdc48d02db29270e244d81398df0f89fd7dd
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@npmcli/agent@npm:^3.0.0":
|
"@npmcli/agent@npm:^3.0.0":
|
||||||
version: 3.0.0
|
version: 3.0.0
|
||||||
resolution: "@npmcli/agent@npm:3.0.0"
|
resolution: "@npmcli/agent@npm:3.0.0"
|
||||||
@ -2640,7 +2650,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/node-fetch@npm:^2.6.4":
|
"@types/node-fetch@npm:^2.5.10, @types/node-fetch@npm:^2.6.4":
|
||||||
version: 2.6.12
|
version: 2.6.12
|
||||||
resolution: "@types/node-fetch@npm:2.6.12"
|
resolution: "@types/node-fetch@npm:2.6.12"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3011,6 +3021,7 @@ __metadata:
|
|||||||
"@llm-tools/embedjs-loader-web": "npm:^0.1.25"
|
"@llm-tools/embedjs-loader-web": "npm:^0.1.25"
|
||||||
"@llm-tools/embedjs-loader-xml": "npm:^0.1.25"
|
"@llm-tools/embedjs-loader-xml": "npm:^0.1.25"
|
||||||
"@llm-tools/embedjs-openai": "npm:^0.1.25"
|
"@llm-tools/embedjs-openai": "npm:^0.1.25"
|
||||||
|
"@notionhq/client": "npm:^2.2.15"
|
||||||
"@reduxjs/toolkit": "npm:^2.2.5"
|
"@reduxjs/toolkit": "npm:^2.2.5"
|
||||||
"@types/adm-zip": "npm:^0"
|
"@types/adm-zip": "npm:^0"
|
||||||
"@types/fs-extra": "npm:^11"
|
"@types/fs-extra": "npm:^11"
|
||||||
@ -9845,7 +9856,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"node-fetch@npm:^2.6.7":
|
"node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7":
|
||||||
version: 2.7.0
|
version: 2.7.0
|
||||||
resolution: "node-fetch@npm:2.7.0"
|
resolution: "node-fetch@npm:2.7.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user