From 1b57ffeb56bee3b51996c2076f9a5459d42b70c8 Mon Sep 17 00:00:00 2001 From: fullex <0xfullex@gmail.com> Date: Tue, 2 Sep 2025 22:11:15 +0800 Subject: [PATCH] refactor: enhance preference management and update component integrations - Updated preference handling to utilize the usePreference hook across various components, improving consistency and maintainability. - Introduced new preference types for ProxyMode and MultiModelFoldDisplayMode to enhance type safety. - Refactored components to replace useSettings with usePreference, streamlining preference management. - Cleaned up unused code and comments related to previous settings for better clarity. - Updated auto-generated preference mappings to reflect recent changes in preference structure. --- packages/shared/data/preferenceTypes.ts | 4 + packages/shared/data/preferences.ts | 35 ++- .../index.clientCompatibilityTypes.test.ts | 3 +- .../MinApp/MinappPopupContainer.tsx | 9 +- .../src/components/Popups/UserPopup.tsx | 6 +- src/renderer/src/hooks/useSettings.ts | 96 ++++---- src/renderer/src/pages/home/ChatNavbar.tsx | 11 +- .../home/Messages/MessageGroupModelList.tsx | 17 +- .../home/Messages/MessageGroupSettings.tsx | 12 +- src/renderer/src/pages/home/Navbar.tsx | 12 +- .../src/pages/home/Tabs/SettingsTab.tsx | 194 +++++++--------- .../MiniappSettings/MiniAppSettings.tsx | 35 ++- .../src/pages/settings/AboutSettings.tsx | 7 +- .../DataSettings/ExportMenuSettings.tsx | 28 ++- .../settings/DataSettings/JoplinSettings.tsx | 21 +- .../DataSettings/LocalBackupSettings.tsx | 71 ++---- .../DataSettings/MarkdownExportSettings.tsx | 53 +++-- .../settings/DataSettings/NotionSettings.tsx | 28 +-- .../settings/DataSettings/S3Settings.tsx | 119 +++++----- .../settings/DataSettings/SiyuanSettings.tsx | 23 +- .../settings/DataSettings/WebDavSettings.tsx | 72 ++---- .../settings/DataSettings/YuqueSettings.tsx | 16 +- .../src/pages/settings/GeneralSettings.tsx | 72 +++--- .../ModelSettings/QuickModelPopup.tsx | 17 +- .../pages/settings/QuickAssistantSettings.tsx | 26 +-- src/renderer/src/store/index.ts | 4 +- src/renderer/src/store/selectionStore.ts | 2 +- src/renderer/src/store/settings.ts | 216 +++++++++--------- 28 files changed, 538 insertions(+), 671 deletions(-) diff --git a/packages/shared/data/preferenceTypes.ts b/packages/shared/data/preferenceTypes.ts index e4a91c2546..b5f635b1d1 100644 --- a/packages/shared/data/preferenceTypes.ts +++ b/packages/shared/data/preferenceTypes.ts @@ -55,3 +55,7 @@ export type SidebarIcon = | 'notes' export type AssistantIconType = 'model' | 'emoji' | 'none' + +export type ProxyMode = 'system' | 'custom' | 'none' + +export type MultiModelFoldDisplayMode = 'expanded' | 'compact' diff --git a/packages/shared/data/preferences.ts b/packages/shared/data/preferences.ts index 4de693e89c..d1668e9122 100644 --- a/packages/shared/data/preferences.ts +++ b/packages/shared/data/preferences.ts @@ -1,6 +1,6 @@ /** * Auto-generated preferences configuration - * Generated at: 2025-09-02T08:08:51.242Z + * Generated at: 2025-09-02T11:59:32.955Z * * This file is automatically generated from classification.json * To update this file, modify classification.json and run: @@ -13,6 +13,8 @@ import { TRANSLATE_PROMPT } from '@shared/config/prompts' import type { AssistantIconType, AssistantTabSortType, + MultiModelFoldDisplayMode, + ProxyMode, SelectionActionItem, SelectionFilterMode, SelectionTriggerMode, @@ -52,15 +54,15 @@ export interface PreferencesType { // redux/settings/enableDataCollection 'app.privacy.data_collection.enabled': boolean // redux/settings/proxyBypassRules - 'app.proxy.bypass_rules': string | null + 'app.proxy.bypass_rules': string // redux/settings/proxyMode - 'app.proxy.mode': string + 'app.proxy.mode': ProxyMode // redux/settings/proxyUrl - 'app.proxy.url': string | null + 'app.proxy.url': string // redux/settings/enableSpellCheck 'app.spell_check.enabled': boolean // redux/settings/spellCheckLanguages - 'app.spell_check.languages': unknown[] + 'app.spell_check.languages': string[] // redux/settings/tray 'app.tray.enabled': boolean // redux/settings/trayOnClose @@ -138,9 +140,11 @@ export interface PreferencesType { // redux/settings/fontSize 'chat.message.font_size': number // redux/settings/mathEngine - 'chat.message.math_engine': string + 'chat.message.math.engine': string + // redux/settings/mathEnableSingleDollar + 'chat.message.math.single_dollar': boolean // redux/settings/foldDisplayMode - 'chat.message.multi_model.fold_display_mode': string + 'chat.message.multi_model.fold_display_mode': MultiModelFoldDisplayMode // redux/settings/gridColumns 'chat.message.multi_model.grid_columns': number // redux/settings/gridPopoverTrigger @@ -149,8 +153,12 @@ export interface PreferencesType { 'chat.message.multi_model.style': string // redux/settings/messageNavigation 'chat.message.navigation_mode': string + // redux/settings/renderInputMessageAsMarkdown + 'chat.message.render_as_markdown': boolean // redux/settings/showMessageDivider 'chat.message.show_divider': boolean + // redux/settings/showMessageOutline + 'chat.message.show_outline': boolean // redux/settings/showPrompt 'chat.message.show_prompt': boolean // redux/settings/messageStyle @@ -408,9 +416,9 @@ export const DefaultPreferences: PreferencesType = { 'app.notification.backup.enabled': false, 'app.notification.knowledge.enabled': false, 'app.privacy.data_collection.enabled': false, - 'app.proxy.bypass_rules': null, + 'app.proxy.bypass_rules': '', 'app.proxy.mode': 'system', - 'app.proxy.url': null, + 'app.proxy.url': '', 'app.spell_check.enabled': false, 'app.spell_check.languages': [], 'app.tray.enabled': true, @@ -451,13 +459,16 @@ export const DefaultPreferences: PreferencesType = { 'chat.message.confirm_regenerate': true, 'chat.message.font': 'system', 'chat.message.font_size': 14, - 'chat.message.math_engine': 'KaTeX', + 'chat.message.math.engine': 'KaTeX', + 'chat.message.math.single_dollar': true, 'chat.message.multi_model.fold_display_mode': 'expanded', 'chat.message.multi_model.grid_columns': 2, 'chat.message.multi_model.grid_popover_trigger': 'click', 'chat.message.multi_model.style': 'horizontal', 'chat.message.navigation_mode': 'none', + 'chat.message.render_as_markdown': false, 'chat.message.show_divider': true, + 'chat.message.show_outline': false, 'chat.message.show_prompt': true, 'chat.message.style': 'plain', 'chat.message.thought.auto_collapse': true, @@ -642,8 +653,8 @@ export const DefaultPreferences: PreferencesType = { /** * 生成统计: - * - 总配置项: 181 + * - 总配置项: 184 * - electronStore项: 1 - * - redux项: 180 + * - redux项: 183 * - localStorage项: 0 */ diff --git a/src/renderer/src/aiCore/__tests__/index.clientCompatibilityTypes.test.ts b/src/renderer/src/aiCore/__tests__/index.clientCompatibilityTypes.test.ts index 12571875db..fbfceb9d0a 100644 --- a/src/renderer/src/aiCore/__tests__/index.clientCompatibilityTypes.test.ts +++ b/src/renderer/src/aiCore/__tests__/index.clientCompatibilityTypes.test.ts @@ -75,7 +75,8 @@ vi.mock('@logger', () => ({ info: vi.fn(), warn: vi.fn(), error: vi.fn(), - silly: vi.fn() + silly: vi.fn(), + verbose: vi.fn() }) } })) diff --git a/src/renderer/src/components/MinApp/MinappPopupContainer.tsx b/src/renderer/src/components/MinApp/MinappPopupContainer.tsx index 08ee6db52a..b67f2bd561 100644 --- a/src/renderer/src/components/MinApp/MinappPopupContainer.tsx +++ b/src/renderer/src/components/MinApp/MinappPopupContainer.tsx @@ -10,6 +10,7 @@ import { PushpinOutlined, ReloadOutlined } from '@ant-design/icons' +import { usePreference } from '@data/hooks/usePreference' import { loggerService } from '@logger' import { isLinux, isMac, isWin } from '@renderer/config/constant' import { DEFAULT_MIN_APPS } from '@renderer/config/minapps' @@ -19,10 +20,7 @@ import { useMinapps } from '@renderer/hooks/useMinapps' import useNavBackgroundColor from '@renderer/hooks/useNavBackgroundColor' import { useNavbarPosition } from '@renderer/hooks/useNavbar' import { useRuntime } from '@renderer/hooks/useRuntime' -import { useSettings } from '@renderer/hooks/useSettings' import { useTimer } from '@renderer/hooks/useTimer' -import { useAppDispatch } from '@renderer/store' -import { setMinappsOpenLinkExternal } from '@renderer/store/settings' import { MinAppType } from '@renderer/types' import { delay } from '@renderer/utils' import { Alert, Avatar, Button, Drawer, Tooltip } from 'antd' @@ -142,12 +140,12 @@ const GoogleLoginTip = ({ /** The main container for MinApp popup */ const MinappPopupContainer: React.FC = () => { const { openedKeepAliveMinapps, openedOneOffMinapp, currentMinappId, minappShow } = useRuntime() + const [minappsOpenLinkExternal, setMinappsOpenLinkExternal] = usePreference('feature.minapp.open_link_external') const { closeMinapp, hideMinappPopup } = useMinappPopup() const { pinned, updatePinnedMinapps } = useMinapps() const { t } = useTranslation() const backgroundColor = useNavBackgroundColor() const { isTopNavbar } = useNavbarPosition() - const dispatch = useAppDispatch() /** control the drawer open or close */ const [isPopupShow, setIsPopupShow] = useState(true) @@ -166,7 +164,6 @@ const MinappPopupContainer: React.FC = () => { /** indicate whether the webview has loaded */ const webviewLoadedRefs = useRef>(new Map()) /** whether the minapps open link external is enabled */ - const { minappsOpenLinkExternal } = useSettings() const { isLeftNavbar } = useNavbarPosition() @@ -347,7 +344,7 @@ const MinappPopupContainer: React.FC = () => { /** set the open external status */ const handleToggleOpenExternal = () => { - dispatch(setMinappsOpenLinkExternal(!minappsOpenLinkExternal)) + setMinappsOpenLinkExternal(!minappsOpenLinkExternal) } /** navigate back in webview history */ diff --git a/src/renderer/src/components/Popups/UserPopup.tsx b/src/renderer/src/components/Popups/UserPopup.tsx index 8c6ce692e6..21cd5d4e66 100644 --- a/src/renderer/src/components/Popups/UserPopup.tsx +++ b/src/renderer/src/components/Popups/UserPopup.tsx @@ -5,7 +5,6 @@ import useAvatar from '@renderer/hooks/useAvatar' import ImageStorage from '@renderer/services/ImageStorage' import { useAppDispatch } from '@renderer/store' import { setAvatar } from '@renderer/store/runtime' -import { setUserName } from '@renderer/store/settings' import { compressImage, isEmoji } from '@renderer/utils' import { Avatar, Dropdown, Input, Modal, Popover, Upload } from 'antd' import React, { useState } from 'react' @@ -21,11 +20,12 @@ interface Props { } const PopupContainer: React.FC = ({ resolve }) => { + const [userName, setUserName] = usePreference('app.user.name') + const [open, setOpen] = useState(true) const [emojiPickerOpen, setEmojiPickerOpen] = useState(false) const [dropdownOpen, setDropdownOpen] = useState(false) const { t } = useTranslation() - const [userName] = usePreference('app.user.name') const dispatch = useAppDispatch() const avatar = useAvatar() @@ -170,7 +170,7 @@ const PopupContainer: React.FC = ({ resolve }) => { dispatch(setUserName(e.target.value.trim()))} + onChange={(e) => setUserName(e.target.value.trim())} style={{ flex: 1, textAlign: 'center', width: '100%' }} maxLength={30} /> diff --git a/src/renderer/src/hooks/useSettings.ts b/src/renderer/src/hooks/useSettings.ts index 05e94eb563..3be090d86b 100644 --- a/src/renderer/src/hooks/useSettings.ts +++ b/src/renderer/src/hooks/useSettings.ts @@ -1,68 +1,56 @@ -import store, { useAppDispatch, useAppSelector } from '@renderer/store' +import store, { useAppSelector } from '@renderer/store' import { - setAutoCheckUpdate as _setAutoCheckUpdate, - setLaunchOnBoot, - setLaunchToTray, - setSendMessageShortcut as _setSendMessageShortcut, - setTargetLanguage, - setTestChannel as _setTestChannel, - setTestPlan as _setTestPlan, - SettingsState, - setTray as _setTray, - setTrayOnClose + SettingsState // setWindowStyle } from '@renderer/store/settings' -import { TranslateLanguageCode } from '@renderer/types' -import { UpgradeChannel } from '@shared/config/constant' -import type { SendMessageShortcut } from '@shared/data/preferenceTypes' export function useSettings() { const settings = useAppSelector((state) => state.settings) - const dispatch = useAppDispatch() + // const dispatch = useAppDispatch() return { - ...settings, - setSendMessageShortcut(shortcut: SendMessageShortcut) { - dispatch(_setSendMessageShortcut(shortcut)) - }, + ...settings + // setSendMessageShortcut(shortcut: SendMessageShortcut) { + // dispatch(_setSendMessageShortcut(shortcut)) + // }, - setLaunch(isLaunchOnBoot: boolean | undefined, isLaunchToTray: boolean | undefined = undefined) { - if (isLaunchOnBoot !== undefined) { - dispatch(setLaunchOnBoot(isLaunchOnBoot)) - window.api.setLaunchOnBoot(isLaunchOnBoot) - } + // setLaunch(isLaunchOnBoot: boolean | undefined, isLaunchToTray: boolean | undefined = undefined) { + // if (isLaunchOnBoot !== undefined) { + // dispatch(setLaunchOnBoot(isLaunchOnBoot)) + // window.api.setLaunchOnBoot(isLaunchOnBoot) + // } - if (isLaunchToTray !== undefined) { - dispatch(setLaunchToTray(isLaunchToTray)) - window.api.setLaunchToTray(isLaunchToTray) - } - }, + // if (isLaunchToTray !== undefined) { + // dispatch(setLaunchToTray(isLaunchToTray)) + // window.api.setLaunchToTray(isLaunchToTray) + // } + // }, - setTray(isShowTray: boolean | undefined, isTrayOnClose: boolean | undefined = undefined) { - if (isShowTray !== undefined) { - dispatch(_setTray(isShowTray)) - window.api.setTray(isShowTray) - } - if (isTrayOnClose !== undefined) { - dispatch(setTrayOnClose(isTrayOnClose)) - window.api.setTrayOnClose(isTrayOnClose) - } - }, + // setTray(isShowTray: boolean | undefined, isTrayOnClose: boolean | undefined = undefined) { + // if (isShowTray !== undefined) { + // dispatch(_setTray(isShowTray)) + // window.api.setTray(isShowTray) + // } + // if (isTrayOnClose !== undefined) { + // dispatch(setTrayOnClose(isTrayOnClose)) + // window.api.setTrayOnClose(isTrayOnClose) + // } + // }, - setAutoCheckUpdate(isAutoUpdate: boolean) { - dispatch(_setAutoCheckUpdate(isAutoUpdate)) - window.api.setAutoUpdate(isAutoUpdate) - }, + // setAutoCheckUpdate(isAutoUpdate: boolean) { + // dispatch(_setAutoCheckUpdate(isAutoUpdate)) + // window.api.setAutoUpdate(isAutoUpdate) + // }, - setTestPlan(isTestPlan: boolean) { - dispatch(_setTestPlan(isTestPlan)) - window.api.setTestPlan(isTestPlan) - }, + // setTestPlan(isTestPlan: boolean) { + // dispatch(_setTestPlan(isTestPlan)) + // window.api.setTestPlan(isTestPlan) + // }, - setTestChannel(channel: UpgradeChannel) { - dispatch(_setTestChannel(channel)) - window.api.setTestChannel(channel) - }, + // setTestChannel(channel: UpgradeChannel) { + // dispatch(_setTestChannel(channel)) + // window.api.setTestChannel(channel) + // }, // setTheme(theme: ThemeMode) { // dispatch(setTheme(theme)) @@ -70,9 +58,9 @@ export function useSettings() { // setWindowStyle(windowStyle: 'transparent' | 'opaque') { // dispatch(setWindowStyle(windowStyle)) // }, - setTargetLanguage(targetLanguage: TranslateLanguageCode) { - dispatch(setTargetLanguage(targetLanguage)) - } + // setTargetLanguage(targetLanguage: TranslateLanguageCode) { + // dispatch(setTargetLanguage(targetLanguage)) + // } // setTopicPosition(topicPosition: 'left' | 'right') { // dispatch(setTopicPosition(topicPosition)) // }, diff --git a/src/renderer/src/pages/home/ChatNavbar.tsx b/src/renderer/src/pages/home/ChatNavbar.tsx index 8dbd05285e..c4a00daa49 100644 --- a/src/renderer/src/pages/home/ChatNavbar.tsx +++ b/src/renderer/src/pages/home/ChatNavbar.tsx @@ -7,8 +7,6 @@ import { modelGenerating } from '@renderer/hooks/useRuntime' import { useShortcut } from '@renderer/hooks/useShortcuts' import { useShowAssistants, useShowTopics } from '@renderer/hooks/useStore' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' -import { useAppDispatch } from '@renderer/store' -import { setNarrowMode } from '@renderer/store/settings' import { Assistant, Topic } from '@renderer/types' import { Tooltip } from 'antd' import { t } from 'i18next' @@ -30,14 +28,13 @@ interface Props { } const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant, activeTopic, setActiveTopic }) => { + const [topicPosition] = usePreference('topic.position') + const [narrowMode, setNarrowMode] = usePreference('chat.narrow_mode') + const { assistant } = useAssistant(activeAssistant.id) const { showAssistants, toggleShowAssistants } = useShowAssistants() - const [topicPosition] = usePreference('topic.position') - const [narrowMode] = usePreference('chat.narrow_mode') - const { showTopics, toggleShowTopics } = useShowTopics() - const dispatch = useAppDispatch() useShortcut('toggle_show_assistants', toggleShowAssistants) @@ -55,7 +52,7 @@ const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant, activeTo const handleNarrowModeToggle = async () => { await modelGenerating() - dispatch(setNarrowMode(!narrowMode)) + setNarrowMode(!narrowMode) } const onShowAssistantsDrawer = () => { diff --git a/src/renderer/src/pages/home/Messages/MessageGroupModelList.tsx b/src/renderer/src/pages/home/Messages/MessageGroupModelList.tsx index 917184f161..a5684a13bf 100644 --- a/src/renderer/src/pages/home/Messages/MessageGroupModelList.tsx +++ b/src/renderer/src/pages/home/Messages/MessageGroupModelList.tsx @@ -1,31 +1,26 @@ import { ArrowsAltOutlined, ShrinkOutlined } from '@ant-design/icons' +import { usePreference } from '@data/hooks/usePreference' import ModelAvatar from '@renderer/components/Avatar/ModelAvatar' import { HStack } from '@renderer/components/Layout' import Scrollbar from '@renderer/components/Scrollbar' -import { useSettings } from '@renderer/hooks/useSettings' -import { useAppDispatch } from '@renderer/store' -import { setFoldDisplayMode } from '@renderer/store/settings' import type { Model } from '@renderer/types' import { AssistantMessageStatus, type Message } from '@renderer/types/newMessage' import { lightbulbSoftVariants } from '@renderer/utils/motionVariants' +import type { MultiModelFoldDisplayMode } from '@shared/data/preferenceTypes' import { Avatar, Segmented as AntdSegmented, Tooltip } from 'antd' import { motion } from 'motion/react' import { FC, memo, useCallback } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' - interface MessageGroupModelListProps { messages: Message[] selectMessageId: string setSelectedMessage: (message: Message) => void } -type DisplayMode = 'compact' | 'expanded' - const MessageGroupModelList: FC = ({ messages, selectMessageId, setSelectedMessage }) => { - const dispatch = useAppDispatch() + const [foldDisplayMode, setFoldDisplayMode] = usePreference('chat.message.multi_model.fold_display_mode') const { t } = useTranslation() - const { foldDisplayMode } = useSettings() const isCompact = foldDisplayMode === 'compact' const isMessageProcessing = useCallback((message: Message) => { @@ -80,7 +75,7 @@ const MessageGroupModelList: FC = ({ messages, selec mouseLeaveDelay={0}> dispatch(setFoldDisplayMode(isCompact ? 'expanded' : 'compact'))}> + onClick={() => setFoldDisplayMode(isCompact ? 'expanded' : 'compact')}> {isCompact ? : } @@ -115,7 +110,7 @@ const Container = styled(HStack)` margin-left: 4px; ` -const DisplayModeToggle = styled.div<{ displayMode: DisplayMode }>` +const DisplayModeToggle = styled.div<{ displayMode: MultiModelFoldDisplayMode }>` display: flex; cursor: pointer; padding: 2px 6px 3px 6px; @@ -128,7 +123,7 @@ const DisplayModeToggle = styled.div<{ displayMode: DisplayMode }>` } ` -const ModelsContainer = styled(Scrollbar)<{ $displayMode: DisplayMode }>` +const ModelsContainer = styled(Scrollbar)<{ $displayMode: MultiModelFoldDisplayMode }>` display: flex; flex-direction: ${(props) => (props.$displayMode === 'expanded' ? 'column' : 'row')}; justify-content: ${(props) => (props.$displayMode === 'expanded' ? 'space-between' : 'flex-start')}; diff --git a/src/renderer/src/pages/home/Messages/MessageGroupSettings.tsx b/src/renderer/src/pages/home/Messages/MessageGroupSettings.tsx index 9ae20180f2..ca3f829a68 100644 --- a/src/renderer/src/pages/home/Messages/MessageGroupSettings.tsx +++ b/src/renderer/src/pages/home/Messages/MessageGroupSettings.tsx @@ -1,20 +1,18 @@ import { SettingOutlined } from '@ant-design/icons' +import { usePreference } from '@data/hooks/usePreference' import Selector from '@renderer/components/Selector' -import { useSettings } from '@renderer/hooks/useSettings' import { SettingDivider } from '@renderer/pages/settings' import { SettingRow } from '@renderer/pages/settings' -import { useAppDispatch } from '@renderer/store' -import { setGridColumns, setGridPopoverTrigger } from '@renderer/store/settings' import { Col, Row, Slider } from 'antd' import { Popover } from 'antd' import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' const MessageGroupSettings: FC = () => { - const dispatch = useAppDispatch() + const [gridPopoverTrigger, setGridPopoverTrigger] = usePreference('chat.message.multi_model.grid_popover_trigger') + const [gridColumns, setGridColumns] = usePreference('chat.message.multi_model.grid_columns') const { t } = useTranslation() - const { gridColumns, gridPopoverTrigger } = useSettings() const [gridColumnsValue, setGridColumnsValue] = useState(gridColumns) return ( @@ -28,7 +26,7 @@ const MessageGroupSettings: FC = () => { dispatch(setGridPopoverTrigger(value as 'hover' | 'click'))} + onChange={(value) => setGridPopoverTrigger(value as 'hover' | 'click')} options={[ { label: t('settings.messages.grid_popover_trigger.hover'), value: 'hover' }, { label: t('settings.messages.grid_popover_trigger.click'), value: 'click' } @@ -45,7 +43,7 @@ const MessageGroupSettings: FC = () => { value={gridColumnsValue} style={{ width: '100%' }} onChange={(value) => setGridColumnsValue(value)} - onChangeComplete={(value) => dispatch(setGridColumns(value))} + onChangeComplete={(value) => setGridColumns(value)} min={2} max={6} step={1} diff --git a/src/renderer/src/pages/home/Navbar.tsx b/src/renderer/src/pages/home/Navbar.tsx index 5f75c36c0a..c4bc37529e 100644 --- a/src/renderer/src/pages/home/Navbar.tsx +++ b/src/renderer/src/pages/home/Navbar.tsx @@ -1,3 +1,4 @@ +import { usePreference } from '@data/hooks/usePreference' import { Navbar, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar' import { HStack } from '@renderer/components/Layout' import SearchPopup from '@renderer/components/Popups/SearchPopup' @@ -5,12 +6,9 @@ import { isMac } from '@renderer/config/constant' import { useAssistant } from '@renderer/hooks/useAssistant' import { useFullscreen } from '@renderer/hooks/useFullscreen' import { modelGenerating } from '@renderer/hooks/useRuntime' -import { useSettings } from '@renderer/hooks/useSettings' import { useShortcut } from '@renderer/hooks/useShortcuts' import { useShowAssistants, useShowTopics } from '@renderer/hooks/useStore' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' -import { useAppDispatch } from '@renderer/store' -import { setNarrowMode } from '@renderer/store/settings' import { Assistant, Topic } from '@renderer/types' import { Tooltip } from 'antd' import { t } from 'i18next' @@ -22,7 +20,6 @@ import styled from 'styled-components' import AssistantsDrawer from './components/AssistantsDrawer' import SelectModelButton from './components/SelectModelButton' import UpdateAppButton from './components/UpdateAppButton' - interface Props { activeAssistant: Assistant activeTopic: Topic @@ -32,12 +29,13 @@ interface Props { } const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant, activeTopic, setActiveTopic }) => { + const [narrowMode, setNarrowMode] = usePreference('chat.narrow_mode') + const [topicPosition] = usePreference('topic.position') + const { assistant } = useAssistant(activeAssistant.id) const { showAssistants, toggleShowAssistants } = useShowAssistants() const isFullscreen = useFullscreen() - const { topicPosition, narrowMode } = useSettings() const { showTopics, toggleShowTopics } = useShowTopics() - const dispatch = useAppDispatch() useShortcut('toggle_show_assistants', toggleShowAssistants) @@ -55,7 +53,7 @@ const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant, activeTo const handleNarrowModeToggle = async () => { await modelGenerating() - dispatch(setNarrowMode(!narrowMode)) + setNarrowMode(!narrowMode) } const onShowAssistantsDrawer = () => { diff --git a/src/renderer/src/pages/home/Tabs/SettingsTab.tsx b/src/renderer/src/pages/home/Tabs/SettingsTab.tsx index 1c418f6a2c..406765d29a 100644 --- a/src/renderer/src/pages/home/Tabs/SettingsTab.tsx +++ b/src/renderer/src/pages/home/Tabs/SettingsTab.tsx @@ -1,3 +1,4 @@ +import { useMultiplePreferences, usePreference } from '@data/hooks/usePreference' import EditableNumber from '@renderer/components/EditableNumber' import { HStack } from '@renderer/components/Layout' import Scrollbar from '@renderer/components/Scrollbar' @@ -9,41 +10,11 @@ import { useCodeStyle } from '@renderer/context/CodeStyleProvider' import { useTheme } from '@renderer/context/ThemeProvider' import { useAssistant } from '@renderer/hooks/useAssistant' import { useProvider } from '@renderer/hooks/useProvider' -import { useSettings } from '@renderer/hooks/useSettings' import useTranslate from '@renderer/hooks/useTranslate' import { SettingDivider, SettingRow, SettingRowTitle } from '@renderer/pages/settings' import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings' import { CollapsibleSettingGroup } from '@renderer/pages/settings/SettingGroup' import { getDefaultModel } from '@renderer/services/AssistantService' -import { useAppDispatch } from '@renderer/store' -import { - setAutoTranslateWithSpace, - setCodeCollapsible, - setCodeEditor, - setCodeExecution, - setCodeImageTools, - setCodeShowLineNumbers, - setCodeViewer, - setCodeWrappable, - setConfirmDeleteMessage, - setConfirmRegenerateMessage, - setEnableQuickPanelTriggers, - setFontSize, - setMathEnableSingleDollar, - setMathEngine, - setMessageFont, - setMessageNavigation, - setMessageStyle, - setMultiModelMessageStyle, - setPasteLongTextAsFile, - setPasteLongTextThreshold, - setRenderInputMessageAsMarkdown, - setShowInputEstimatedTokens, - setShowMessageOutline, - setShowPrompt, - setShowTranslateConfirm, - setThoughtAutoCollapse -} from '@renderer/store/settings' import { Assistant, AssistantSettings, CodeStyleVarious, MathEngine } from '@renderer/types' import { modalConfirm } from '@renderer/utils' import { getSendMessageShortcutLabel } from '@renderer/utils/input' @@ -56,16 +27,64 @@ import { useTranslation } from 'react-i18next' import styled from 'styled-components' import OpenAISettingsGroup from './components/OpenAISettingsGroup' - interface Props { assistant: Assistant } const SettingsTab: FC = (props) => { + const [messageStyle, setMessageStyle] = usePreference('chat.message.style') + const [fontSize, setFontSize] = usePreference('chat.message.font_size') + const [language] = usePreference('app.language') + const [targetLanguage, setTargetLanguage] = usePreference('feature.translate.target_language') + const [sendMessageShortcut, setSendMessageShortcut] = usePreference('chat.input.send_message_shortcut') + const [messageFont, setMessageFont] = usePreference('chat.message.font') + const [showPrompt, setShowPrompt] = usePreference('chat.message.show_prompt') + const [confirmDeleteMessage, setConfirmDeleteMessage] = usePreference('chat.message.confirm_delete') + const [confirmRegenerateMessage, setConfirmRegenerateMessage] = usePreference('chat.message.confirm_regenerate') + const [showTranslateConfirm, setShowTranslateConfirm] = usePreference('chat.input.translate.show_confirm') + const [enableQuickPanelTriggers, setEnableQuickPanelTriggers] = usePreference( + 'chat.input.quick_panel.triggers_enabled' + ) + const [messageNavigation, setMessageNavigation] = usePreference('chat.message.navigation_mode') + const [thoughtAutoCollapse, setThoughtAutoCollapse] = usePreference('chat.message.thought.auto_collapse') + const [multiModelMessageStyle, setMultiModelMessageStyle] = usePreference('chat.message.multi_model.style') + const [pasteLongTextAsFile, setPasteLongTextAsFile] = usePreference('chat.input.paste_long_text_as_file') + const [pasteLongTextThreshold, setPasteLongTextThreshold] = usePreference('chat.input.paste_long_text_threshold') + const [mathEngine, setMathEngine] = usePreference('chat.message.math.engine') + const [mathEnableSingleDollar, setMathEnableSingleDollar] = usePreference('chat.message.math.single_dollar') + const [showInputEstimatedTokens, setShowInputEstimatedTokens] = usePreference('chat.input.show_estimated_tokens') + const [renderInputMessageAsMarkdown, setRenderInputMessageAsMarkdown] = usePreference( + 'chat.message.render_as_markdown' + ) + const [autoTranslateWithSpace, setAutoTranslateWithSpace] = usePreference( + 'chat.input.translate.auto_translate_with_space' + ) + const [showMessageOutline, setShowMessageOutline] = usePreference('chat.message.show_outline') + const [codeShowLineNumbers, setCodeShowLineNumbers] = usePreference('chat.code.show_line_numbers') + const [codeCollapsible, setCodeCollapsible] = usePreference('chat.code.collapsible') + const [codeWrappable, setCodeWrappable] = usePreference('chat.code.wrappable') + const [codeImageTools, setCodeImageTools] = usePreference('chat.code.image_tools') + const [codeEditor, setCodeEditor] = useMultiplePreferences({ + enabled: 'chat.code.editor.enabled', + themeLight: 'chat.code.editor.theme_light', + themeDark: 'chat.code.editor.theme_dark', + highlightActiveLine: 'chat.code.editor.highlight_active_line', + foldGutter: 'chat.code.editor.fold_gutter', + autocompletion: 'chat.code.editor.autocompletion', + keymap: 'chat.code.editor.keymap' + }) + const [codeViewer, setCodeViewer] = useMultiplePreferences({ + themeLight: 'chat.code.viewer.theme_light', + themeDark: 'chat.code.viewer.theme_dark' + }) + const [codeExecution, setCodeExecution] = useMultiplePreferences({ + enabled: 'chat.code.execution.enabled', + timeoutMinutes: 'chat.code.execution.timeout_minutes' + }) + const { assistant, updateAssistantSettings } = useAssistant(props.assistant.id) const { provider } = useProvider(assistant.model.provider) - const { messageStyle, fontSize, language } = useSettings() const { theme } = useTheme() const { themeNames } = useCodeStyle() @@ -80,39 +99,6 @@ const SettingsTab: FC = (props) => { const { t } = useTranslation() - const dispatch = useAppDispatch() - - const { - showPrompt, - messageFont, - showInputEstimatedTokens, - sendMessageShortcut, - setSendMessageShortcut, - targetLanguage, - setTargetLanguage, - pasteLongTextAsFile, - renderInputMessageAsMarkdown, - codeShowLineNumbers, - codeCollapsible, - codeWrappable, - codeEditor, - codeViewer, - codeImageTools, - codeExecution, - mathEngine, - mathEnableSingleDollar, - autoTranslateWithSpace, - pasteLongTextThreshold, - multiModelMessageStyle, - thoughtAutoCollapse, - messageNavigation, - enableQuickPanelTriggers, - showTranslateConfirm, - showMessageOutline, - confirmDeleteMessage, - confirmRegenerateMessage - } = useSettings() - const onUpdateAssistantSettings = (settings: Partial) => { updateAssistantSettings(settings) } @@ -156,9 +142,9 @@ const SettingsTab: FC = (props) => { (value: CodeStyleVarious) => { const field = theme === ThemeMode.light ? 'themeLight' : 'themeDark' const action = codeEditor.enabled ? setCodeEditor : setCodeViewer - dispatch(action({ [field]: value })) + action({ [field]: value }) }, - [dispatch, theme, codeEditor.enabled] + [theme, codeEditor.enabled, setCodeEditor, setCodeViewer] ) useEffect(() => { @@ -313,7 +299,7 @@ const SettingsTab: FC = (props) => { {t('settings.messages.prompt')} - dispatch(setShowPrompt(checked))} /> + setShowPrompt(checked)} /> @@ -321,7 +307,7 @@ const SettingsTab: FC = (props) => { dispatch(setMessageFont(checked ? 'serif' : 'system'))} + onChange={(checked) => setMessageFont(checked ? 'serif' : 'system')} /> @@ -335,24 +321,20 @@ const SettingsTab: FC = (props) => { dispatch(setThoughtAutoCollapse(checked))} + onChange={(checked) => setThoughtAutoCollapse(checked)} /> {t('settings.messages.show_message_outline')} - dispatch(setShowMessageOutline(checked))} - /> + setShowMessageOutline(checked)} /> {t('message.message.style.label')} dispatch(setMessageStyle(value as 'plain' | 'bubble'))} + onChange={(value) => setMessageStyle(value as 'plain' | 'bubble')} options={[ { value: 'plain', label: t('message.message.style.plain') }, { value: 'bubble', label: t('message.message.style.bubble') } @@ -364,7 +346,7 @@ const SettingsTab: FC = (props) => { {t('message.message.multi_model_style.label')} dispatch(setMultiModelMessageStyle(value))} + onChange={(value) => setMultiModelMessageStyle(value)} options={[ { value: 'fold', label: t('message.message.multi_model_style.fold.label') }, { value: 'vertical', label: t('message.message.multi_model_style.vertical') }, @@ -378,7 +360,7 @@ const SettingsTab: FC = (props) => { {t('settings.messages.navigation.label')} dispatch(setMessageNavigation(value as 'none' | 'buttons' | 'anchor'))} + onChange={(value) => setMessageNavigation(value as 'none' | 'buttons' | 'anchor')} options={[ { value: 'none', label: t('settings.messages.navigation.none') }, { value: 'buttons', label: t('settings.messages.navigation.buttons') }, @@ -395,7 +377,7 @@ const SettingsTab: FC = (props) => { setFontSizeValue(value)} - onChangeComplete={(value) => dispatch(setFontSize(value))} + onChangeComplete={(value) => setFontSize(value)} min={12} max={22} step={1} @@ -416,7 +398,7 @@ const SettingsTab: FC = (props) => { {t('settings.math.engine.label')} dispatch(setMathEngine(value as MathEngine))} + onChange={(value) => setMathEngine(value as MathEngine)} options={[ { value: 'KaTeX', label: 'KaTeX' }, { value: 'MathJax', label: 'MathJax' }, @@ -435,7 +417,7 @@ const SettingsTab: FC = (props) => { dispatch(setMathEnableSingleDollar(checked))} + onChange={(checked) => setMathEnableSingleDollar(checked)} /> @@ -465,7 +447,7 @@ const SettingsTab: FC = (props) => { dispatch(setCodeExecution({ enabled: checked }))} + onChange={(checked) => setCodeExecution({ enabled: checked })} /> {codeExecution.enabled && ( @@ -484,7 +466,7 @@ const SettingsTab: FC = (props) => { max={60} step={1} value={codeExecution.timeoutMinutes} - onChange={(value) => dispatch(setCodeExecution({ timeoutMinutes: value ?? 1 }))} + onChange={(value) => setCodeExecution({ timeoutMinutes: value ?? 1 })} style={{ width: 80 }} /> @@ -496,7 +478,7 @@ const SettingsTab: FC = (props) => { dispatch(setCodeEditor({ enabled: checked }))} + onChange={(checked) => setCodeEditor({ enabled: checked })} /> {codeEditor.enabled && ( @@ -507,7 +489,7 @@ const SettingsTab: FC = (props) => { dispatch(setCodeEditor({ highlightActiveLine: checked }))} + onChange={(checked) => setCodeEditor({ highlightActiveLine: checked })} /> @@ -516,7 +498,7 @@ const SettingsTab: FC = (props) => { dispatch(setCodeEditor({ foldGutter: checked }))} + onChange={(checked) => setCodeEditor({ foldGutter: checked })} /> @@ -525,7 +507,7 @@ const SettingsTab: FC = (props) => { dispatch(setCodeEditor({ autocompletion: checked }))} + onChange={(checked) => setCodeEditor({ autocompletion: checked })} /> @@ -534,7 +516,7 @@ const SettingsTab: FC = (props) => { dispatch(setCodeEditor({ keymap: checked }))} + onChange={(checked) => setCodeEditor({ keymap: checked })} /> @@ -545,31 +527,23 @@ const SettingsTab: FC = (props) => { dispatch(setCodeShowLineNumbers(checked))} + onChange={(checked) => setCodeShowLineNumbers(checked)} /> {t('chat.settings.code_collapsible')} - dispatch(setCodeCollapsible(checked))} - /> + setCodeCollapsible(checked)} /> {t('chat.settings.code_wrappable')} - dispatch(setCodeWrappable(checked))} /> + setCodeWrappable(checked)} /> {t('chat.settings.code_image_tools')} - dispatch(setCodeImageTools(checked))} - /> + setCodeImageTools(checked)} /> @@ -581,7 +555,7 @@ const SettingsTab: FC = (props) => { dispatch(setShowInputEstimatedTokens(checked))} + onChange={(checked) => setShowInputEstimatedTokens(checked)} /> @@ -590,7 +564,7 @@ const SettingsTab: FC = (props) => { dispatch(setPasteLongTextAsFile(checked))} + onChange={(checked) => setPasteLongTextAsFile(checked)} /> {pasteLongTextAsFile && ( @@ -604,7 +578,7 @@ const SettingsTab: FC = (props) => { max={10000} step={100} value={pasteLongTextThreshold} - onChange={(value) => dispatch(setPasteLongTextThreshold(value ?? 500))} + onChange={(value) => setPasteLongTextThreshold(value ?? 500)} style={{ width: 80 }} /> @@ -616,18 +590,18 @@ const SettingsTab: FC = (props) => { dispatch(setRenderInputMessageAsMarkdown(checked))} + onChange={(checked) => setRenderInputMessageAsMarkdown(checked)} /> - {!language.startsWith('en') && ( + {!(language || navigator.language).startsWith('en') && ( <> {t('settings.input.auto_translate_with_space')} dispatch(setAutoTranslateWithSpace(checked))} + onChange={(checked) => setAutoTranslateWithSpace(checked)} /> @@ -638,7 +612,7 @@ const SettingsTab: FC = (props) => { dispatch(setShowTranslateConfirm(checked))} + onChange={(checked) => setShowTranslateConfirm(checked)} /> @@ -647,7 +621,7 @@ const SettingsTab: FC = (props) => { dispatch(setEnableQuickPanelTriggers(checked))} + onChange={(checked) => setEnableQuickPanelTriggers(checked)} /> @@ -656,7 +630,7 @@ const SettingsTab: FC = (props) => { dispatch(setConfirmDeleteMessage(checked))} + onChange={(checked) => setConfirmDeleteMessage(checked)} /> @@ -665,7 +639,7 @@ const SettingsTab: FC = (props) => { dispatch(setConfirmRegenerateMessage(checked))} + onChange={(checked) => setConfirmRegenerateMessage(checked)} /> diff --git a/src/renderer/src/pages/minapps/MiniappSettings/MiniAppSettings.tsx b/src/renderer/src/pages/minapps/MiniappSettings/MiniAppSettings.tsx index 506d1092fb..a2b9206058 100644 --- a/src/renderer/src/pages/minapps/MiniappSettings/MiniAppSettings.tsx +++ b/src/renderer/src/pages/minapps/MiniappSettings/MiniAppSettings.tsx @@ -1,14 +1,8 @@ import { UndoOutlined } from '@ant-design/icons' // 导入重置图标 +import { usePreference } from '@data/hooks/usePreference' import { DEFAULT_MIN_APPS } from '@renderer/config/minapps' import { useMinapps } from '@renderer/hooks/useMinapps' -import { useSettings } from '@renderer/hooks/useSettings' import { SettingDescription, SettingDivider, SettingRowTitle, SettingTitle } from '@renderer/pages/settings' -import { useAppDispatch } from '@renderer/store' -import { - setMaxKeepAliveMinapps, - setMinappsOpenLinkExternal, - setShowOpenedMinappsInSidebar -} from '@renderer/store/settings' import { Button, message, Slider, Switch, Tooltip } from 'antd' import { FC, useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -21,8 +15,13 @@ const DEFAULT_MAX_KEEPALIVE = 3 const MiniAppSettings: FC = () => { const { t } = useTranslation() - const dispatch = useAppDispatch() - const { maxKeepAliveMinapps, showOpenedMinappsInSidebar, minappsOpenLinkExternal } = useSettings() + + const [maxKeepAliveMinapps, setMaxKeepAliveMinapps] = usePreference('feature.minapp.max_keep_alive') + const [showOpenedMinappsInSidebar, setShowOpenedMinappsInSidebar] = usePreference( + 'feature.minapp.show_opened_in_sidebar' + ) + const [minappsOpenLinkExternal, setMinappsOpenLinkExternal] = usePreference('feature.minapp.open_link_external') + const { minapps, disabled, updateMinapps, updateDisabledMinapps } = useMinapps() const [visibleMiniApps, setVisibleMiniApps] = useState(minapps) @@ -45,14 +44,14 @@ const MiniAppSettings: FC = () => { // 恢复默认缓存数量 const handleResetCacheLimit = useCallback(() => { - dispatch(setMaxKeepAliveMinapps(DEFAULT_MAX_KEEPALIVE)) + setMaxKeepAliveMinapps(DEFAULT_MAX_KEEPALIVE) messageApi.info(t('settings.miniapps.cache_change_notice')) - }, [dispatch, messageApi, t]) + }, [messageApi, t, setMaxKeepAliveMinapps]) // 处理缓存数量变更 const handleCacheChange = useCallback( (value: number) => { - dispatch(setMaxKeepAliveMinapps(value)) + setMaxKeepAliveMinapps(value) if (debounceTimerRef.current) { clearTimeout(debounceTimerRef.current) @@ -63,7 +62,7 @@ const MiniAppSettings: FC = () => { debounceTimerRef.current = null }, 500) }, - [dispatch, messageApi, t] + [messageApi, t, setMaxKeepAliveMinapps] ) // 组件卸载时清除定时器 @@ -97,10 +96,7 @@ const MiniAppSettings: FC = () => { {t('settings.miniapps.open_link_external.title')} - dispatch(setMinappsOpenLinkExternal(checked))} - /> + setMinappsOpenLinkExternal(checked)} /> {/* 缓存小程序数量设置 */} @@ -137,10 +133,7 @@ const MiniAppSettings: FC = () => { {t('settings.miniapps.sidebar_title')} {t('settings.miniapps.sidebar_description')} - dispatch(setShowOpenedMinappsInSidebar(checked))} - /> + setShowOpenedMinappsInSidebar(checked)} /> ) diff --git a/src/renderer/src/pages/settings/AboutSettings.tsx b/src/renderer/src/pages/settings/AboutSettings.tsx index 303de662ba..30ec91e045 100644 --- a/src/renderer/src/pages/settings/AboutSettings.tsx +++ b/src/renderer/src/pages/settings/AboutSettings.tsx @@ -1,11 +1,11 @@ import { GithubOutlined } from '@ant-design/icons' +import { usePreference } from '@data/hooks/usePreference' import IndicatorLight from '@renderer/components/IndicatorLight' import { HStack } from '@renderer/components/Layout' import { APP_NAME, AppLogo } from '@renderer/config/env' import { useTheme } from '@renderer/context/ThemeProvider' import { useMinappPopup } from '@renderer/hooks/useMinappPopup' import { useRuntime } from '@renderer/hooks/useRuntime' -import { useSettings } from '@renderer/hooks/useSettings' import i18n from '@renderer/i18n' import { handleSaveData, useAppDispatch } from '@renderer/store' import { setUpdateState } from '@renderer/store/runtime' @@ -25,10 +25,13 @@ import styled from 'styled-components' import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingTitle } from '.' const AboutSettings: FC = () => { + const [autoCheckUpdate, setAutoCheckUpdate] = usePreference('app.dist.auto_update.enabled') + const [testPlan, setTestPlan] = usePreference('app.dist.test_plan.enabled') + const [testChannel, setTestChannel] = usePreference('app.dist.test_plan.channel') + const [version, setVersion] = useState('') const [isPortable, setIsPortable] = useState(false) const { t } = useTranslation() - const { autoCheckUpdate, setAutoCheckUpdate, testPlan, setTestPlan, testChannel, setTestChannel } = useSettings() const { theme } = useTheme() const dispatch = useAppDispatch() const { update } = useRuntime() diff --git a/src/renderer/src/pages/settings/DataSettings/ExportMenuSettings.tsx b/src/renderer/src/pages/settings/DataSettings/ExportMenuSettings.tsx index 243f534878..5274a773f5 100644 --- a/src/renderer/src/pages/settings/DataSettings/ExportMenuSettings.tsx +++ b/src/renderer/src/pages/settings/DataSettings/ExportMenuSettings.tsx @@ -1,27 +1,31 @@ +import { useMultiplePreferences } from '@data/hooks/usePreference' import { useTheme } from '@renderer/context/ThemeProvider' -import { RootState, useAppDispatch } from '@renderer/store' -import { setExportMenuOptions } from '@renderer/store/settings' import { Switch } from 'antd' import { FC } from 'react' import { useTranslation } from 'react-i18next' -import { useSelector } from 'react-redux' import { SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..' - const ExportMenuOptions: FC = () => { const { t } = useTranslation() const { theme } = useTheme() - const dispatch = useAppDispatch() - const exportMenuOptions = useSelector((state: RootState) => state.settings.exportMenuOptions) + const [exportMenuOptions, setExportMenuOptions] = useMultiplePreferences({ + image: 'data.export.menus.image', + markdown: 'data.export.menus.markdown', + markdown_reason: 'data.export.menus.markdown_reason', + notion: 'data.export.menus.notion', + yuque: 'data.export.menus.yuque', + joplin: 'data.export.menus.joplin', + obsidian: 'data.export.menus.obsidian', + siyuan: 'data.export.menus.siyuan', + docx: 'data.export.menus.docx', + plain_text: 'data.export.menus.plain_text' + }) const handleToggleOption = (option: string, checked: boolean) => { - dispatch( - setExportMenuOptions({ - ...exportMenuOptions, - [option]: checked - }) - ) + setExportMenuOptions({ + [option]: checked + }) } return ( diff --git a/src/renderer/src/pages/settings/DataSettings/JoplinSettings.tsx b/src/renderer/src/pages/settings/DataSettings/JoplinSettings.tsx index 664b65a60c..0292a49623 100644 --- a/src/renderer/src/pages/settings/DataSettings/JoplinSettings.tsx +++ b/src/renderer/src/pages/settings/DataSettings/JoplinSettings.tsx @@ -1,33 +1,30 @@ import { InfoCircleOutlined } from '@ant-design/icons' +import { usePreference } from '@data/hooks/usePreference' import { HStack } from '@renderer/components/Layout' import { useTheme } from '@renderer/context/ThemeProvider' import { useMinappPopup } from '@renderer/hooks/useMinappPopup' -import { RootState, useAppDispatch } from '@renderer/store' -import { setJoplinExportReasoning, setJoplinToken, setJoplinUrl } from '@renderer/store/settings' import { Button, Space, Switch, Tooltip } from 'antd' import { Input } from 'antd' import { FC } from 'react' import { useTranslation } from 'react-i18next' -import { useSelector } from 'react-redux' import { SettingDivider, SettingGroup, SettingHelpText, SettingRow, SettingRowTitle, SettingTitle } from '..' const JoplinSettings: FC = () => { + const [joplinToken, setJoplinToken] = usePreference('data.integration.joplin.token') + const [joplinUrl, setJoplinUrl] = usePreference('data.integration.joplin.url') + const [joplinExportReasoning, setJoplinExportReasoning] = usePreference('data.integration.joplin.export_reasoning') + const { t } = useTranslation() const { theme } = useTheme() - const dispatch = useAppDispatch() const { openMinapp } = useMinappPopup() - const joplinToken = useSelector((state: RootState) => state.settings.joplinToken) - const joplinUrl = useSelector((state: RootState) => state.settings.joplinUrl) - const joplinExportReasoning = useSelector((state: RootState) => state.settings.joplinExportReasoning) - const handleJoplinTokenChange = (e: React.ChangeEvent) => { - dispatch(setJoplinToken(e.target.value)) + setJoplinToken(e.target.value) } const handleJoplinUrlChange = (e: React.ChangeEvent) => { - dispatch(setJoplinUrl(e.target.value)) + setJoplinUrl(e.target.value) } const handleJoplinUrlBlur = (e: React.FocusEvent) => { @@ -35,7 +32,7 @@ const JoplinSettings: FC = () => { // 确保URL以/结尾,但只在失去焦点时执行 if (url && !url.endsWith('/')) { url = `${url}/` - dispatch(setJoplinUrl(url)) + setJoplinUrl(url) } } @@ -74,7 +71,7 @@ const JoplinSettings: FC = () => { } const handleToggleJoplinExportReasoning = (checked: boolean) => { - dispatch(setJoplinExportReasoning(checked)) + setJoplinExportReasoning(checked) } return ( diff --git a/src/renderer/src/pages/settings/DataSettings/LocalBackupSettings.tsx b/src/renderer/src/pages/settings/DataSettings/LocalBackupSettings.tsx index 97e9105180..f0ef202510 100644 --- a/src/renderer/src/pages/settings/DataSettings/LocalBackupSettings.tsx +++ b/src/renderer/src/pages/settings/DataSettings/LocalBackupSettings.tsx @@ -1,20 +1,13 @@ import { DeleteOutlined, FolderOpenOutlined, SaveOutlined, SyncOutlined, WarningOutlined } from '@ant-design/icons' +import { usePreference } from '@data/hooks/usePreference' import { loggerService } from '@logger' import { HStack } from '@renderer/components/Layout' import { LocalBackupManager } from '@renderer/components/LocalBackupManager' import { LocalBackupModal, useLocalBackupModal } from '@renderer/components/LocalBackupModals' import Selector from '@renderer/components/Selector' import { useTheme } from '@renderer/context/ThemeProvider' -import { useSettings } from '@renderer/hooks/useSettings' import { startAutoSync, stopAutoSync } from '@renderer/services/BackupService' -import { useAppDispatch, useAppSelector } from '@renderer/store' -import { - setLocalBackupAutoSync, - setLocalBackupDir as _setLocalBackupDir, - setLocalBackupMaxBackups as _setLocalBackupMaxBackups, - setLocalBackupSkipBackupFile as _setLocalBackupSkipBackupFile, - setLocalBackupSyncInterval as _setLocalBackupSyncInterval -} from '@renderer/store/settings' +import { useAppSelector } from '@renderer/store' import { AppInfo } from '@renderer/types' import { Button, Input, Switch, Tooltip } from 'antd' import dayjs from 'dayjs' @@ -22,27 +15,18 @@ import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { SettingDivider, SettingGroup, SettingHelpText, SettingRow, SettingRowTitle, SettingTitle } from '..' - const logger = loggerService.withContext('LocalBackupSettings') const LocalBackupSettings: React.FC = () => { - const dispatch = useAppDispatch() + const [, setLocalBackupAutoSync] = usePreference('data.backup.local.auto_sync') + const [localBackupDir, setLocalBackupDir] = usePreference('data.backup.local.dir') + const [localBackupMaxBackups, setLocalBackupMaxBackups] = usePreference('data.backup.local.max_backups') + const [localBackupSkipBackupFile, setLocalBackupSkipBackupFile] = usePreference('data.backup.local.skip_backup_file') + const [localBackupSyncInterval, setLocalBackupSyncInterval] = usePreference('data.backup.local.sync_interval') - const { - localBackupDir: localBackupDirSetting, - localBackupSyncInterval: localBackupSyncIntervalSetting, - localBackupMaxBackups: localBackupMaxBackupsSetting, - localBackupSkipBackupFile: localBackupSkipBackupFileSetting - } = useSettings() - - const [localBackupDir, setLocalBackupDir] = useState(localBackupDirSetting) const [resolvedLocalBackupDir, setResolvedLocalBackupDir] = useState(undefined) - const [localBackupSkipBackupFile, setLocalBackupSkipBackupFile] = useState(localBackupSkipBackupFileSetting) const [backupManagerVisible, setBackupManagerVisible] = useState(false) - const [syncInterval, setSyncInterval] = useState(localBackupSyncIntervalSetting) - const [maxBackups, setMaxBackups] = useState(localBackupMaxBackupsSetting) - const [appInfo, setAppInfo] = useState() useEffect(() => { @@ -50,10 +34,10 @@ const LocalBackupSettings: React.FC = () => { }, []) useEffect(() => { - if (localBackupDirSetting) { - window.api.resolvePath(localBackupDirSetting).then(setResolvedLocalBackupDir) + if (localBackupDir) { + window.api.resolvePath(localBackupDir).then(setResolvedLocalBackupDir) } - }, [localBackupDirSetting]) + }, [localBackupDir]) const { theme } = useTheme() @@ -62,13 +46,12 @@ const LocalBackupSettings: React.FC = () => { const { localBackupSync } = useAppSelector((state) => state.backup) const onSyncIntervalChange = (value: number) => { - setSyncInterval(value) - dispatch(_setLocalBackupSyncInterval(value)) + setLocalBackupSyncInterval(value) if (value === 0) { - dispatch(setLocalBackupAutoSync(false)) + setLocalBackupAutoSync(false) stopAutoSync('local') } else { - dispatch(setLocalBackupAutoSync(true)) + setLocalBackupAutoSync(true) startAutoSync(false, 'local') } } @@ -105,7 +88,7 @@ const LocalBackupSettings: React.FC = () => { } const handleLocalBackupDirChange = async (value: string) => { - if (value === localBackupDirSetting) { + if (value === localBackupDir) { return } @@ -115,29 +98,26 @@ const LocalBackupSettings: React.FC = () => { } if (await checkLocalBackupDirValid(value)) { - setLocalBackupDir(value) - dispatch(_setLocalBackupDir(value)) + await setLocalBackupDir(value) setResolvedLocalBackupDir(await window.api.resolvePath(value)) - dispatch(setLocalBackupAutoSync(true)) + await setLocalBackupAutoSync(true) startAutoSync(true, 'local') return } - if (localBackupDirSetting) { - setLocalBackupDir(localBackupDirSetting) + if (localBackupDir) { + await setLocalBackupDir(localBackupDir) return } } const onMaxBackupsChange = (value: number) => { - setMaxBackups(value) - dispatch(_setLocalBackupMaxBackups(value)) + setLocalBackupMaxBackups(value) } const onSkipBackupFilesChange = (value: boolean) => { setLocalBackupSkipBackupFile(value) - dispatch(_setLocalBackupSkipBackupFile(value)) } const handleBrowseDirectory = async () => { @@ -157,10 +137,9 @@ const LocalBackupSettings: React.FC = () => { } } - const handleClearDirectory = () => { - setLocalBackupDir('') - dispatch(_setLocalBackupDir('')) - dispatch(setLocalBackupAutoSync(false)) + const handleClearDirectory = async () => { + await setLocalBackupDir('') + await setLocalBackupAutoSync(false) stopAutoSync('local') } @@ -238,7 +217,7 @@ const LocalBackupSettings: React.FC = () => { {t('settings.data.local.autoSync.label')} { {t('settings.data.local.maxBackups.label')} { {t('settings.data.backup.skip_file_data_help')} - {localBackupSync && syncInterval > 0 && ( + {localBackupSync && localBackupSyncInterval > 0 && ( <> diff --git a/src/renderer/src/pages/settings/DataSettings/MarkdownExportSettings.tsx b/src/renderer/src/pages/settings/DataSettings/MarkdownExportSettings.tsx index 50b03b9ae4..610b568018 100644 --- a/src/renderer/src/pages/settings/DataSettings/MarkdownExportSettings.tsx +++ b/src/renderer/src/pages/settings/DataSettings/MarkdownExportSettings.tsx @@ -1,70 +1,69 @@ import { DeleteOutlined, FolderOpenOutlined } from '@ant-design/icons' +import { usePreference } from '@data/hooks/usePreference' import { HStack } from '@renderer/components/Layout' import { useTheme } from '@renderer/context/ThemeProvider' -import { RootState, useAppDispatch } from '@renderer/store' -import { - setExcludeCitationsInExport, - setForceDollarMathInMarkdown, - setmarkdownExportPath, - setShowModelNameInMarkdown, - setShowModelProviderInMarkdown, - setStandardizeCitationsInExport, - setUseTopicNamingForMessageTitle -} from '@renderer/store/settings' import { Button, Switch } from 'antd' import Input from 'antd/es/input/Input' import { FC } from 'react' import { useTranslation } from 'react-i18next' -import { useSelector } from 'react-redux' import { SettingDivider, SettingGroup, SettingHelpText, SettingRow, SettingRowTitle, SettingTitle } from '..' const MarkdownExportSettings: FC = () => { const { t } = useTranslation() const { theme } = useTheme() - const dispatch = useAppDispatch() - const markdownExportPath = useSelector((state: RootState) => state.settings.markdownExportPath) - const forceDollarMathInMarkdown = useSelector((state: RootState) => state.settings.forceDollarMathInMarkdown) - const useTopicNamingForMessageTitle = useSelector((state: RootState) => state.settings.useTopicNamingForMessageTitle) - const showModelNameInExport = useSelector((state: RootState) => state.settings.showModelNameInMarkdown) - const showModelProviderInMarkdown = useSelector((state: RootState) => state.settings.showModelProviderInMarkdown) - const excludeCitationsInExport = useSelector((state: RootState) => state.settings.excludeCitationsInExport) - const standardizeCitationsInExport = useSelector((state: RootState) => state.settings.standardizeCitationsInExport) + const [markdownExportPath, setmarkdownExportPath] = usePreference('data.export.markdown.path') + const [forceDollarMathInMarkdown, setForceDollarMathInMarkdown] = usePreference( + 'data.export.markdown.force_dollar_math' + ) + const [useTopicNamingForMessageTitle, setUseTopicNamingForMessageTitle] = usePreference( + 'data.export.markdown.use_topic_naming_for_message_title' + ) + const [showModelNameInExport, setShowModelNameInMarkdown] = usePreference('data.export.markdown.show_model_name') + const [showModelProviderInMarkdown, setShowModelProviderInMarkdown] = usePreference( + 'data.export.markdown.show_model_provider' + ) + const [excludeCitationsInExport, setExcludeCitationsInExport] = usePreference( + 'data.export.markdown.exclude_citations' + ) + const [standardizeCitationsInExport, setStandardizeCitationsInExport] = usePreference( + 'data.export.markdown.standardize_citations' + ) const handleSelectFolder = async () => { const path = await window.api.file.selectFolder() if (path) { - dispatch(setmarkdownExportPath(path)) + setmarkdownExportPath(path) } } const handleClearPath = () => { - dispatch(setmarkdownExportPath(null)) + setmarkdownExportPath(null) } const handleToggleForceDollarMath = (checked: boolean) => { - dispatch(setForceDollarMathInMarkdown(checked)) + setForceDollarMathInMarkdown(checked) } const handleToggleTopicNaming = (checked: boolean) => { - dispatch(setUseTopicNamingForMessageTitle(checked)) + setUseTopicNamingForMessageTitle(checked) } const handleToggleShowModelName = (checked: boolean) => { - dispatch(setShowModelNameInMarkdown(checked)) + setShowModelNameInMarkdown(checked) } const handleToggleShowModelProvider = (checked: boolean) => { - dispatch(setShowModelProviderInMarkdown(checked)) + setShowModelProviderInMarkdown(checked) } const handleToggleExcludeCitations = (checked: boolean) => { - dispatch(setExcludeCitationsInExport(checked)) + setExcludeCitationsInExport(checked) } const handleToggleStandardizeCitations = (checked: boolean) => { - dispatch(setStandardizeCitationsInExport(checked)) + setStandardizeCitationsInExport(checked) } return ( diff --git a/src/renderer/src/pages/settings/DataSettings/NotionSettings.tsx b/src/renderer/src/pages/settings/DataSettings/NotionSettings.tsx index 3b97b5cbcc..8277255b5e 100644 --- a/src/renderer/src/pages/settings/DataSettings/NotionSettings.tsx +++ b/src/renderer/src/pages/settings/DataSettings/NotionSettings.tsx @@ -1,43 +1,35 @@ import { InfoCircleOutlined } from '@ant-design/icons' +import { usePreference } from '@data/hooks/usePreference' import { Client } from '@notionhq/client' import { HStack } from '@renderer/components/Layout' import { useTheme } from '@renderer/context/ThemeProvider' import { useMinappPopup } from '@renderer/hooks/useMinappPopup' -import { RootState, useAppDispatch } from '@renderer/store' -import { - setNotionApiKey, - setNotionDatabaseID, - setNotionExportReasoning, - setNotionPageNameKey -} from '@renderer/store/settings' import { Button, Space, Switch, Tooltip } from 'antd' import { Input } from 'antd' import { FC } from 'react' import { useTranslation } from 'react-i18next' -import { useSelector } from 'react-redux' import { SettingDivider, SettingGroup, SettingHelpText, SettingRow, SettingRowTitle, SettingTitle } from '..' const NotionSettings: FC = () => { + const [notionApiKey, setNotionApiKey] = usePreference('data.integration.notion.api_key') + const [notionDatabaseID, setNotionDatabaseID] = usePreference('data.integration.notion.database_id') + const [notionPageNameKey, setNotionPageNameKey] = usePreference('data.integration.notion.page_name_key') + const [notionExportReasoning, setNotionExportReasoning] = usePreference('data.integration.notion.export_reasoning') + const { t } = useTranslation() const { theme } = useTheme() - const dispatch = useAppDispatch() const { openMinapp } = useMinappPopup() - const notionApiKey = useSelector((state: RootState) => state.settings.notionApiKey) - const notionDatabaseID = useSelector((state: RootState) => state.settings.notionDatabaseID) - const notionPageNameKey = useSelector((state: RootState) => state.settings.notionPageNameKey) - const notionExportReasoning = useSelector((state: RootState) => state.settings.notionExportReasoning) - const handleNotionTokenChange = (e: React.ChangeEvent) => { - dispatch(setNotionApiKey(e.target.value)) + setNotionApiKey(e.target.value) } const handleNotionDatabaseIdChange = (e: React.ChangeEvent) => { - dispatch(setNotionDatabaseID(e.target.value)) + setNotionDatabaseID(e.target.value) } const handleNotionPageNameKeyChange = (e: React.ChangeEvent) => { - dispatch(setNotionPageNameKey(e.target.value)) + setNotionPageNameKey(e.target.value) } const handleNotionConnectionCheck = () => { @@ -75,7 +67,7 @@ const NotionSettings: FC = () => { } const handleNotionExportReasoningChange = (checked: boolean) => { - dispatch(setNotionExportReasoning(checked)) + setNotionExportReasoning(checked) } return ( diff --git a/src/renderer/src/pages/settings/DataSettings/S3Settings.tsx b/src/renderer/src/pages/settings/DataSettings/S3Settings.tsx index 1a6b5901c4..a6d801e2b8 100644 --- a/src/renderer/src/pages/settings/DataSettings/S3Settings.tsx +++ b/src/renderer/src/pages/settings/DataSettings/S3Settings.tsx @@ -1,15 +1,13 @@ import { FolderOpenOutlined, InfoCircleOutlined, SaveOutlined, SyncOutlined, WarningOutlined } from '@ant-design/icons' +import { usePreference } from '@data/hooks/usePreference' import { HStack } from '@renderer/components/Layout' import { S3BackupManager } from '@renderer/components/S3BackupManager' import { S3BackupModal, useS3BackupModal } from '@renderer/components/S3Modals' import Selector from '@renderer/components/Selector' import { useTheme } from '@renderer/context/ThemeProvider' import { useMinappPopup } from '@renderer/hooks/useMinappPopup' -import { useSettings } from '@renderer/hooks/useSettings' import { startAutoSync, stopAutoSync } from '@renderer/services/BackupService' -import { useAppDispatch, useAppSelector } from '@renderer/store' -import { setS3Partial } from '@renderer/store/settings' -import { S3Config } from '@renderer/types' +import { useAppSelector } from '@renderer/store' import { Button, Input, Switch, Tooltip } from 'antd' import dayjs from 'dayjs' import { FC, useState } from 'react' @@ -18,45 +16,32 @@ import { useTranslation } from 'react-i18next' import { SettingDivider, SettingGroup, SettingHelpText, SettingRow, SettingRowTitle, SettingTitle } from '..' const S3Settings: FC = () => { - const { s3 = {} as S3Config } = useSettings() + const [, setS3AutoSync] = usePreference('data.backup.s3.auto_sync') + const [s3Endpoint, setS3Endpoint] = usePreference('data.backup.s3.endpoint') + const [s3Region, setS3Region] = usePreference('data.backup.s3.region') + const [s3Bucket, setS3Bucket] = usePreference('data.backup.s3.bucket') + const [s3AccessKeyId, setS3AccessKeyId] = usePreference('data.backup.s3.access_key_id') + const [s3SecretAccessKey, setS3SecretAccessKey] = usePreference('data.backup.s3.secret_access_key') + const [s3Root, setS3Root] = usePreference('data.backup.s3.root') + const [s3SkipBackupFile, setS3SkipBackupFile] = usePreference('data.backup.s3.skip_backup_file') + const [s3SyncInterval, setS3SyncInterval] = usePreference('data.backup.s3.sync_interval') + const [s3MaxBackups, setS3MaxBackups] = usePreference('data.backup.s3.max_backups') - const { - endpoint: s3EndpointInit = '', - region: s3RegionInit = '', - bucket: s3BucketInit = '', - accessKeyId: s3AccessKeyIdInit = '', - secretAccessKey: s3SecretAccessKeyInit = '', - root: s3RootInit = '', - syncInterval: s3SyncIntervalInit = 0, - maxBackups: s3MaxBackupsInit = 5, - skipBackupFile: s3SkipBackupFileInit = false - } = s3 - - const [endpoint, setEndpoint] = useState(s3EndpointInit) - const [region, setRegion] = useState(s3RegionInit) - const [bucket, setBucket] = useState(s3BucketInit) - const [accessKeyId, setAccessKeyId] = useState(s3AccessKeyIdInit) - const [secretAccessKey, setSecretAccessKey] = useState(s3SecretAccessKeyInit) - const [root, setRoot] = useState(s3RootInit) - const [skipBackupFile, setSkipBackupFile] = useState(s3SkipBackupFileInit) const [backupManagerVisible, setBackupManagerVisible] = useState(false) - const [syncInterval, setSyncInterval] = useState(s3SyncIntervalInit) - const [maxBackups, setMaxBackups] = useState(s3MaxBackupsInit) - - const dispatch = useAppDispatch() const { theme } = useTheme() const { t } = useTranslation() const { openMinapp } = useMinappPopup() const { s3Sync } = useAppSelector((state) => state.backup) - const onSyncIntervalChange = (value: number) => { - setSyncInterval(value) - dispatch(setS3Partial({ syncInterval: value, autoSync: value !== 0 })) + const onSyncIntervalChange = async (value: number) => { + setS3SyncInterval(value) if (value === 0) { + await setS3AutoSync(false) stopAutoSync('s3') } else { + await setS3AutoSync(true) startAutoSync(false, 's3') } } @@ -70,17 +55,15 @@ const S3Settings: FC = () => { } const onMaxBackupsChange = (value: number) => { - setMaxBackups(value) - dispatch(setS3Partial({ maxBackups: value })) + setS3MaxBackups(value) } const onSkipBackupFilesChange = (value: boolean) => { - setSkipBackupFile(value) - dispatch(setS3Partial({ skipBackupFile: value })) + setS3SkipBackupFile(value) } const renderSyncStatus = () => { - if (!endpoint) return null + if (!s3Endpoint) return null if (!s3Sync?.lastSyncTime && !s3Sync?.syncing && !s3Sync?.lastSyncError) { return {t('settings.data.s3.syncStatus.noSync')} @@ -128,11 +111,11 @@ const S3Settings: FC = () => { {t('settings.data.s3.endpoint.label')} setEndpoint(e.target.value)} + value={s3Endpoint} + onChange={(e) => setS3Endpoint(e.target.value)} style={{ width: 250 }} type="url" - onBlur={() => dispatch(setS3Partial({ endpoint: endpoint || '' }))} + onBlur={(e) => setS3Endpoint(e.target.value)} /> @@ -140,10 +123,10 @@ const S3Settings: FC = () => { {t('settings.data.s3.region.label')} setRegion(e.target.value)} + value={s3Region} + onChange={(e) => setS3Region(e.target.value)} style={{ width: 250 }} - onBlur={() => dispatch(setS3Partial({ region: region || '' }))} + onBlur={(e) => setS3Region(e.target.value)} /> @@ -151,10 +134,10 @@ const S3Settings: FC = () => { {t('settings.data.s3.bucket.label')} setBucket(e.target.value)} + value={s3Bucket} + onChange={(e) => setS3Bucket(e.target.value)} style={{ width: 250 }} - onBlur={() => dispatch(setS3Partial({ bucket: bucket || '' }))} + onBlur={(e) => setS3Bucket(e.target.value)} /> @@ -162,10 +145,10 @@ const S3Settings: FC = () => { {t('settings.data.s3.accessKeyId.label')} setAccessKeyId(e.target.value)} + value={s3AccessKeyId} + onChange={(e) => setS3AccessKeyId(e.target.value)} style={{ width: 250 }} - onBlur={() => dispatch(setS3Partial({ accessKeyId: accessKeyId || '' }))} + onBlur={(e) => setS3AccessKeyId(e.target.value)} /> @@ -173,10 +156,10 @@ const S3Settings: FC = () => { {t('settings.data.s3.secretAccessKey.label')} setSecretAccessKey(e.target.value)} + value={s3SecretAccessKey} + onChange={(e) => setS3SecretAccessKey(e.target.value)} style={{ width: 250 }} - onBlur={() => dispatch(setS3Partial({ secretAccessKey: secretAccessKey || '' }))} + onBlur={(e) => setS3SecretAccessKey(e.target.value)} /> @@ -184,10 +167,10 @@ const S3Settings: FC = () => { {t('settings.data.s3.root.label')} setRoot(e.target.value)} + value={s3Root} + onChange={(e) => setS3Root(e.target.value)} style={{ width: 250 }} - onBlur={() => dispatch(setS3Partial({ root: root || '' }))} + onBlur={(e) => setS3Root(e.target.value)} /> @@ -198,13 +181,13 @@ const S3Settings: FC = () => { onClick={showBackupModal} icon={} loading={backuping} - disabled={!endpoint || !region || !bucket || !accessKeyId || !secretAccessKey}> + disabled={!s3Endpoint || !s3Region || !s3Bucket || !s3AccessKeyId || !s3SecretAccessKey}> {t('settings.data.s3.backup.button')} @@ -214,9 +197,9 @@ const S3Settings: FC = () => { {t('settings.data.s3.autoSync.label')} { {t('settings.data.s3.maxBackups.label')} { {t('settings.data.s3.skipBackupFile.label')} - + {t('settings.data.s3.skipBackupFile.help')} - {syncInterval > 0 && ( + {s3SyncInterval > 0 && ( <> @@ -281,12 +264,12 @@ const S3Settings: FC = () => { visible={backupManagerVisible} onClose={closeBackupManager} s3Config={{ - endpoint, - region, - bucket, - accessKeyId, - secretAccessKey, - root + endpoint: s3Endpoint, + region: s3Region, + bucket: s3Bucket, + accessKeyId: s3AccessKeyId, + secretAccessKey: s3SecretAccessKey, + root: s3Root }} /> diff --git a/src/renderer/src/pages/settings/DataSettings/SiyuanSettings.tsx b/src/renderer/src/pages/settings/DataSettings/SiyuanSettings.tsx index 8045fd5f16..16d8f6e4c8 100644 --- a/src/renderer/src/pages/settings/DataSettings/SiyuanSettings.tsx +++ b/src/renderer/src/pages/settings/DataSettings/SiyuanSettings.tsx @@ -1,45 +1,42 @@ import { InfoCircleOutlined } from '@ant-design/icons' +import { usePreference } from '@data/hooks/usePreference' import { loggerService } from '@logger' import { HStack } from '@renderer/components/Layout' import { useTheme } from '@renderer/context/ThemeProvider' import { useMinappPopup } from '@renderer/hooks/useMinappPopup' -import { RootState, useAppDispatch } from '@renderer/store' -import { setSiyuanApiUrl, setSiyuanBoxId, setSiyuanRootPath, setSiyuanToken } from '@renderer/store/settings' import { Button, Space, Tooltip } from 'antd' import { Input } from 'antd' import { FC } from 'react' import { useTranslation } from 'react-i18next' -import { useSelector } from 'react-redux' import { SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..' const logger = loggerService.withContext('SiyuanSettings') const SiyuanSettings: FC = () => { + const [siyuanApiUrl, setSiyuanApiUrl] = usePreference('data.integration.siyuan.api_url') + const [siyuanToken, setSiyuanToken] = usePreference('data.integration.siyuan.token') + const [siyuanBoxId, setSiyuanBoxId] = usePreference('data.integration.siyuan.box_id') + const [siyuanRootPath, setSiyuanRootPath] = usePreference('data.integration.siyuan.root_path') + const { openMinapp } = useMinappPopup() const { t } = useTranslation() const { theme } = useTheme() - const dispatch = useAppDispatch() - - const siyuanApiUrl = useSelector((state: RootState) => state.settings.siyuanApiUrl) - const siyuanToken = useSelector((state: RootState) => state.settings.siyuanToken) - const siyuanBoxId = useSelector((state: RootState) => state.settings.siyuanBoxId) - const siyuanRootPath = useSelector((state: RootState) => state.settings.siyuanRootPath) const handleApiUrlChange = (e: React.ChangeEvent) => { - dispatch(setSiyuanApiUrl(e.target.value)) + setSiyuanApiUrl(e.target.value) } const handleTokenChange = (e: React.ChangeEvent) => { - dispatch(setSiyuanToken(e.target.value)) + setSiyuanToken(e.target.value) } const handleBoxIdChange = (e: React.ChangeEvent) => { - dispatch(setSiyuanBoxId(e.target.value)) + setSiyuanBoxId(e.target.value) } const handleRootPathChange = (e: React.ChangeEvent) => { - dispatch(setSiyuanRootPath(e.target.value)) + setSiyuanRootPath(e.target.value) } const handleSiyuanHelpClick = () => { diff --git a/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx b/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx index 729cdafe4c..016cd3e96b 100644 --- a/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx +++ b/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx @@ -1,23 +1,12 @@ import { FolderOpenOutlined, SaveOutlined, SyncOutlined, WarningOutlined } from '@ant-design/icons' +import { usePreference } from '@data/hooks/usePreference' import { HStack } from '@renderer/components/Layout' import Selector from '@renderer/components/Selector' import { WebdavBackupManager } from '@renderer/components/WebdavBackupManager' import { useWebdavBackupModal, WebdavBackupModal } from '@renderer/components/WebdavModals' import { useTheme } from '@renderer/context/ThemeProvider' -import { useSettings } from '@renderer/hooks/useSettings' import { startAutoSync, stopAutoSync } from '@renderer/services/BackupService' -import { useAppDispatch, useAppSelector } from '@renderer/store' -import { - setWebdavAutoSync, - setWebdavDisableStream as _setWebdavDisableStream, - setWebdavHost as _setWebdavHost, - setWebdavMaxBackups as _setWebdavMaxBackups, - setWebdavPass as _setWebdavPass, - setWebdavPath as _setWebdavPath, - setWebdavSkipBackupFile as _setWebdavSkipBackupFile, - setWebdavSyncInterval as _setWebdavSyncInterval, - setWebdavUser as _setWebdavUser -} from '@renderer/store/settings' +import { useAppSelector } from '@renderer/store' import { Button, Input, Switch, Tooltip } from 'antd' import dayjs from 'dayjs' import { FC, useState } from 'react' @@ -26,29 +15,18 @@ import { useTranslation } from 'react-i18next' import { SettingDivider, SettingGroup, SettingHelpText, SettingRow, SettingRowTitle, SettingTitle } from '..' const WebDavSettings: FC = () => { - const { - webdavHost: webDAVHost, - webdavUser: webDAVUser, - webdavPass: webDAVPass, - webdavPath: webDAVPath, - webdavSyncInterval: webDAVSyncInterval, - webdavMaxBackups: webDAVMaxBackups, - webdavSkipBackupFile: webdDAVSkipBackupFile, - webdavDisableStream: webDAVDisableStream - } = useSettings() + const [, setWebdavAutoSync] = usePreference('data.backup.webdav.auto_sync') + const [webdavDisableStream, setWebdavDisableStream] = usePreference('data.backup.webdav.disable_stream') + const [webdavHost, setWebdavHost] = usePreference('data.backup.webdav.host') + const [webdavMaxBackups, setWebdavMaxBackups] = usePreference('data.backup.webdav.max_backups') + const [webdavPass, setWebdavPass] = usePreference('data.backup.webdav.pass') + const [webdavPath, setWebdavPath] = usePreference('data.backup.webdav.path') + const [webdavSkipBackupFile, setWebdavSkipBackupFile] = usePreference('data.backup.webdav.skip_backup_file') + const [webdavSyncInterval, setWebdavSyncInterval] = usePreference('data.backup.webdav.sync_interval') + const [webdavUser, setWebdavUser] = usePreference('data.backup.webdav.user') - const [webdavHost, setWebdavHost] = useState(webDAVHost) - const [webdavUser, setWebdavUser] = useState(webDAVUser) - const [webdavPass, setWebdavPass] = useState(webDAVPass) - const [webdavPath, setWebdavPath] = useState(webDAVPath) - const [webdavSkipBackupFile, setWebdavSkipBackupFile] = useState(webdDAVSkipBackupFile) - const [webdavDisableStream, setWebdavDisableStream] = useState(webDAVDisableStream) const [backupManagerVisible, setBackupManagerVisible] = useState(false) - const [syncInterval, setSyncInterval] = useState(webDAVSyncInterval) - const [maxBackups, setMaxBackups] = useState(webDAVMaxBackups) - - const dispatch = useAppDispatch() const { theme } = useTheme() const { t } = useTranslation() @@ -57,31 +35,27 @@ const WebDavSettings: FC = () => { // 把之前备份的文件定时上传到 webdav,首先先配置 webdav 的 host, port, user, pass, path - const onSyncIntervalChange = (value: number) => { - setSyncInterval(value) - dispatch(_setWebdavSyncInterval(value)) + const onSyncIntervalChange = async (value: number) => { + setWebdavSyncInterval(value) if (value === 0) { - dispatch(setWebdavAutoSync(false)) + await setWebdavAutoSync(false) stopAutoSync('webdav') } else { - dispatch(setWebdavAutoSync(true)) + await setWebdavAutoSync(true) startAutoSync(false, 'webdav') } } const onMaxBackupsChange = (value: number) => { - setMaxBackups(value) - dispatch(_setWebdavMaxBackups(value)) + setWebdavMaxBackups(value) } const onSkipBackupFilesChange = (value: boolean) => { setWebdavSkipBackupFile(value) - dispatch(_setWebdavSkipBackupFile(value)) } const onDisableStreamChange = (value: boolean) => { setWebdavDisableStream(value) - dispatch(_setWebdavDisableStream(value)) } const renderSyncStatus = () => { @@ -131,7 +105,7 @@ const WebDavSettings: FC = () => { onChange={(e) => setWebdavHost(e.target.value)} style={{ width: 250 }} type="url" - onBlur={() => dispatch(_setWebdavHost(webdavHost || ''))} + onBlur={() => setWebdavHost(webdavHost || '')} /> @@ -142,7 +116,7 @@ const WebDavSettings: FC = () => { value={webdavUser} onChange={(e) => setWebdavUser(e.target.value)} style={{ width: 250 }} - onBlur={() => dispatch(_setWebdavUser(webdavUser || ''))} + onBlur={() => setWebdavUser(webdavUser || '')} /> @@ -153,7 +127,7 @@ const WebDavSettings: FC = () => { value={webdavPass} onChange={(e) => setWebdavPass(e.target.value)} style={{ width: 250 }} - onBlur={() => dispatch(_setWebdavPass(webdavPass || ''))} + onBlur={() => setWebdavPass(webdavPass || '')} /> @@ -164,7 +138,7 @@ const WebDavSettings: FC = () => { value={webdavPath} onChange={(e) => setWebdavPath(e.target.value)} style={{ width: 250 }} - onBlur={() => dispatch(_setWebdavPath(webdavPath || ''))} + onBlur={() => setWebdavPath(webdavPath || '')} /> @@ -184,7 +158,7 @@ const WebDavSettings: FC = () => { {t('settings.data.webdav.autoSync.label')} { {t('settings.data.webdav.maxBackups')} { {t('settings.data.webdav.disableStream.help')} - {webdavSync && syncInterval > 0 && ( + {webdavSync && webdavSyncInterval > 0 && ( <> diff --git a/src/renderer/src/pages/settings/DataSettings/YuqueSettings.tsx b/src/renderer/src/pages/settings/DataSettings/YuqueSettings.tsx index 57ee29246c..4165d03dd9 100644 --- a/src/renderer/src/pages/settings/DataSettings/YuqueSettings.tsx +++ b/src/renderer/src/pages/settings/DataSettings/YuqueSettings.tsx @@ -1,32 +1,30 @@ import { InfoCircleOutlined } from '@ant-design/icons' +import { usePreference } from '@data/hooks/usePreference' import { HStack } from '@renderer/components/Layout' import { useTheme } from '@renderer/context/ThemeProvider' import { useMinappPopup } from '@renderer/hooks/useMinappPopup' -import { RootState, useAppDispatch } from '@renderer/store' -import { setYuqueRepoId, setYuqueToken, setYuqueUrl } from '@renderer/store/settings' import { Button, Space, Tooltip } from 'antd' import { Input } from 'antd' import { FC } from 'react' import { useTranslation } from 'react-i18next' -import { useSelector } from 'react-redux' import { SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..' const YuqueSettings: FC = () => { const { t } = useTranslation() const { theme } = useTheme() - const dispatch = useAppDispatch() const { openMinapp } = useMinappPopup() - const yuqueToken = useSelector((state: RootState) => state.settings.yuqueToken) - const yuqueUrl = useSelector((state: RootState) => state.settings.yuqueUrl) + const [yuqueToken, setYuqueToken] = usePreference('data.integration.yuque.token') + const [yuqueUrl, setYuqueUrl] = usePreference('data.integration.yuque.url') + const [, setYuqueRepoId] = usePreference('data.integration.yuque.repo_id') const handleYuqueTokenChange = (e: React.ChangeEvent) => { - dispatch(setYuqueToken(e.target.value)) + setYuqueToken(e.target.value) } const handleYuqueRepoUrlChange = (e: React.ChangeEvent) => { - dispatch(setYuqueUrl(e.target.value)) + setYuqueUrl(e.target.value) } const handleYuqueConnectionCheck = async () => { @@ -60,7 +58,7 @@ const YuqueSettings: FC = () => { return } const data = await repoIDResponse.json() - dispatch(setYuqueRepoId(data.data.id)) + setYuqueRepoId(data.data.id) window.message.success(t('settings.data.yuque.check.success')) } diff --git a/src/renderer/src/pages/settings/GeneralSettings.tsx b/src/renderer/src/pages/settings/GeneralSettings.tsx index 6efc961ee0..1f2e49a735 100644 --- a/src/renderer/src/pages/settings/GeneralSettings.tsx +++ b/src/renderer/src/pages/settings/GeneralSettings.tsx @@ -1,22 +1,11 @@ import { InfoCircleOutlined } from '@ant-design/icons' -import { usePreference } from '@data/hooks/usePreference' +import { useMultiplePreferences, usePreference } from '@data/hooks/usePreference' import InfoTooltip from '@renderer/components/InfoTooltip' import { HStack } from '@renderer/components/Layout' import Selector from '@renderer/components/Selector' import { useTheme } from '@renderer/context/ThemeProvider' -import { useSettings } from '@renderer/hooks/useSettings' import { useTimer } from '@renderer/hooks/useTimer' import i18n from '@renderer/i18n' -import { RootState, useAppDispatch } from '@renderer/store' -import { - setEnableDataCollection, - setEnableSpellCheck, - setNotificationSettings, - setProxyBypassRules as _setProxyBypassRules, - setProxyMode, - setProxyUrl as _setProxyUrl, - setSpellCheckLanguages -} from '@renderer/store/settings' import { NotificationSource } from '@renderer/types/notification' import { isValidProxyUrl } from '@renderer/utils' import { defaultByPassRules, defaultLanguage } from '@shared/config/constant' @@ -24,32 +13,33 @@ import { LanguageVarious } from '@shared/data/preferenceTypes' import { Flex, Input, Switch, Tooltip } from 'antd' import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useSelector } from 'react-redux' import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '.' const GeneralSettings: FC = () => { - const { - proxyUrl: storeProxyUrl, - proxyBypassRules: storeProxyBypassRules, - setLaunch, - setTray, - launchOnBoot, - launchToTray, - trayOnClose, - tray, - proxyMode: storeProxyMode, - enableDataCollection, - enableSpellCheck - } = useSettings() const [language, setLanguage] = usePreference('app.language') const [disableHardwareAcceleration, setDisableHardwareAcceleration] = usePreference( 'app.disable_hardware_acceleration' ) const [enableDeveloperMode, setEnableDeveloperMode] = usePreference('app.developer_mode.enabled') + const [launchOnBoot, setLaunchOnBoot] = usePreference('app.launch_on_boot') + const [launchToTray, setLaunchToTray] = usePreference('app.tray.on_launch') + const [trayOnClose, setTrayOnClose] = usePreference('app.tray.on_close') + const [tray, setTray] = usePreference('app.tray.enabled') + const [enableDataCollection, setEnableDataCollection] = usePreference('app.privacy.data_collection.enabled') + const [storeProxyMode, setProxyMode] = usePreference('app.proxy.mode') + const [storeProxyBypassRules, _setProxyBypassRules] = usePreference('app.proxy.bypass_rules') + const [storeProxyUrl, _setProxyUrl] = usePreference('app.proxy.url') + const [enableSpellCheck, setEnableSpellCheck] = usePreference('app.spell_check.enabled') + const [spellCheckLanguages, setSpellCheckLanguages] = usePreference('app.spell_check.languages') + const [notificationSettings, setNotificationSettings] = useMultiplePreferences({ + assistant: 'app.notification.assistant.enabled', + backup: 'app.notification.backup.enabled', + knowledge: 'app.notification.knowledge.enabled' + }) - const [proxyUrl, setProxyUrl] = useState(storeProxyUrl) - const [proxyBypassRules, setProxyBypassRules] = useState(storeProxyBypassRules) + const [proxyUrl, setProxyUrl] = useState(storeProxyUrl) + const [proxyBypassRules, setProxyBypassRules] = useState(storeProxyBypassRules) const { theme } = useTheme() const { setTimeoutTimer } = useTimer() @@ -63,7 +53,7 @@ const GeneralSettings: FC = () => { } const updateTrayOnClose = (isTrayOnClose: boolean) => { - setTray(undefined, isTrayOnClose) + setTrayOnClose(isTrayOnClose) //in case tray is not enabled, enable it if (isTrayOnClose && !tray) { updateTray(true) @@ -71,17 +61,17 @@ const GeneralSettings: FC = () => { } const updateLaunchOnBoot = (isLaunchOnBoot: boolean) => { - setLaunch(isLaunchOnBoot) + setLaunchOnBoot(isLaunchOnBoot) } const updateLaunchToTray = (isLaunchToTray: boolean) => { - setLaunch(undefined, isLaunchToTray) + setLaunchToTray(isLaunchToTray) if (isLaunchToTray && !tray) { updateTray(true) } } - const dispatch = useAppDispatch() + // const dispatch = useAppDispatch() const { t } = useTranslation() const onSelectLanguage = (value: LanguageVarious) => { @@ -93,7 +83,7 @@ const GeneralSettings: FC = () => { } const handleSpellCheckChange = (checked: boolean) => { - dispatch(setEnableSpellCheck(checked)) + setEnableSpellCheck(checked) window.api.setEnableSpellCheck(checked) } @@ -103,11 +93,11 @@ const GeneralSettings: FC = () => { return } - dispatch(_setProxyUrl(proxyUrl)) + _setProxyUrl(proxyUrl) } const onSetProxyBypassRules = () => { - dispatch(_setProxyBypassRules(proxyBypassRules)) + _setProxyBypassRules(proxyBypassRules) } const proxyModeOptions: { value: 'system' | 'custom' | 'none'; label: string }[] = [ @@ -117,7 +107,7 @@ const GeneralSettings: FC = () => { ] const onProxyModeChange = (mode: 'system' | 'custom' | 'none') => { - dispatch(setProxyMode(mode)) + setProxyMode(mode) } const languagesOptions: { value: LanguageVarious; label: string; flag: string }[] = [ @@ -132,11 +122,8 @@ const GeneralSettings: FC = () => { { value: 'pt-PT', label: 'Português', flag: '🇵🇹' } ] - const notificationSettings = useSelector((state: RootState) => state.settings.notification) - const spellCheckLanguages = useSelector((state: RootState) => state.settings.spellCheckLanguages) - const handleNotificationChange = (type: NotificationSource, value: boolean) => { - dispatch(setNotificationSettings({ ...notificationSettings, [type]: value })) + setNotificationSettings({ [type]: value }) } // Define available spell check languages with display names (only commonly supported languages) @@ -153,8 +140,7 @@ const GeneralSettings: FC = () => { ] const handleSpellCheckLanguagesChange = (selectedLanguages: string[]) => { - dispatch(setSpellCheckLanguages(selectedLanguages)) - window.api.setSpellCheckLanguages(selectedLanguages) + setSpellCheckLanguages(selectedLanguages) } const handleHardwareAccelerationChange = (checked: boolean) => { @@ -339,7 +325,7 @@ const GeneralSettings: FC = () => { { - dispatch(setEnableDataCollection(v)) + setEnableDataCollection(v) window.api.config.set('enableDataCollection', v) }} /> diff --git a/src/renderer/src/pages/settings/ModelSettings/QuickModelPopup.tsx b/src/renderer/src/pages/settings/ModelSettings/QuickModelPopup.tsx index 985e8c6e67..664e584169 100644 --- a/src/renderer/src/pages/settings/ModelSettings/QuickModelPopup.tsx +++ b/src/renderer/src/pages/settings/ModelSettings/QuickModelPopup.tsx @@ -1,9 +1,7 @@ import { QuestionCircleOutlined } from '@ant-design/icons' +import { usePreference } from '@data/hooks/usePreference' import { ResetIcon } from '@renderer/components/Icons' import { HStack } from '@renderer/components/Layout' -import { useSettings } from '@renderer/hooks/useSettings' -import { useAppDispatch } from '@renderer/store' -import { setEnableTopicNaming, setTopicNamingPrompt } from '@renderer/store/settings' import { Button, Divider, Flex, Input, Modal, Popover, Switch } from 'antd' import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -16,10 +14,11 @@ interface Props { } const PopupContainer: React.FC = ({ resolve }) => { + const [enableTopicNaming, setEnableTopicNaming] = usePreference('topic.naming.enabled') + const [topicNamingPrompt, setTopicNamingPrompt] = usePreference('topic.naming_prompt') + const [open, setOpen] = useState(true) const { t } = useTranslation() - const { enableTopicNaming, topicNamingPrompt } = useSettings() - const dispatch = useAppDispatch() const onOk = () => { setOpen(false) @@ -34,8 +33,8 @@ const PopupContainer: React.FC = ({ resolve }) => { } const handleReset = useCallback(() => { - dispatch(setTopicNamingPrompt('')) - }, [dispatch]) + setTopicNamingPrompt('') + }, [setTopicNamingPrompt]) TopicNamingModalPopup.hide = onCancel @@ -58,7 +57,7 @@ const PopupContainer: React.FC = ({ resolve }) => {
{t('settings.models.topic_naming.auto')}
- dispatch(setEnableTopicNaming(v))} /> + setEnableTopicNaming(v)} />
@@ -72,7 +71,7 @@ const PopupContainer: React.FC = ({ resolve }) => { dispatch(setTopicNamingPrompt(e.target.value))} + onChange={(e) => setTopicNamingPrompt(e.target.value)} placeholder={t('prompts.title')} style={{ width: '100%' }} /> diff --git a/src/renderer/src/pages/settings/QuickAssistantSettings.tsx b/src/renderer/src/pages/settings/QuickAssistantSettings.tsx index f4de427cc5..3d184e0f58 100644 --- a/src/renderer/src/pages/settings/QuickAssistantSettings.tsx +++ b/src/renderer/src/pages/settings/QuickAssistantSettings.tsx @@ -1,16 +1,11 @@ import { InfoCircleOutlined } from '@ant-design/icons' +import { usePreference } from '@data/hooks/usePreference' import ModelAvatar from '@renderer/components/Avatar/ModelAvatar' import { HStack } from '@renderer/components/Layout' import { useTheme } from '@renderer/context/ThemeProvider' import { useAssistants, useDefaultAssistant, useDefaultModel } from '@renderer/hooks/useAssistant' -import { useSettings } from '@renderer/hooks/useSettings' import { useAppDispatch, useAppSelector } from '@renderer/store' import { setQuickAssistantId } from '@renderer/store/llm' -import { - setClickTrayToShowQuickAssistant, - setEnableQuickAssistant, - setReadClipboardAtStartup -} from '@renderer/store/settings' import HomeWindow from '@renderer/windows/mini/home/HomeWindow' import { Button, Select, Switch, Tooltip } from 'antd' import { FC } from 'react' @@ -20,9 +15,17 @@ import styled from 'styled-components' import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '.' const QuickAssistantSettings: FC = () => { + const [enableQuickAssistant, setEnableQuickAssistant] = usePreference('feature.quick_assistant.enabled') + const [clickTrayToShowQuickAssistant, setClickTrayToShowQuickAssistant] = usePreference( + 'feature.quick_assistant.click_tray_to_show' + ) + const [readClipboardAtStartup, setReadClipboardAtStartup] = usePreference( + 'feature.quick_assistant.read_clipboard_at_startup' + ) + const [, setTray] = usePreference('app.tray.enabled') + const { t } = useTranslation() const { theme } = useTheme() - const { enableQuickAssistant, clickTrayToShowQuickAssistant, setTray, readClipboardAtStartup } = useSettings() const dispatch = useAppDispatch() const { assistants } = useAssistants() const { quickAssistantId } = useAppSelector((state) => state.llm) @@ -30,8 +33,7 @@ const QuickAssistantSettings: FC = () => { const { defaultModel } = useDefaultModel() const handleEnableQuickAssistant = async (enable: boolean) => { - dispatch(setEnableQuickAssistant(enable)) - await window.api.config.set('enableQuickAssistant', enable, true) + await setEnableQuickAssistant(enable) !enable && window.api.miniWindow.close() @@ -50,14 +52,12 @@ const QuickAssistantSettings: FC = () => { } const handleClickTrayToShowQuickAssistant = async (checked: boolean) => { - dispatch(setClickTrayToShowQuickAssistant(checked)) - await window.api.config.set('clickTrayToShowQuickAssistant', checked) + await setClickTrayToShowQuickAssistant(checked) checked && setTray(true) } const handleClickReadClipboardAtStartup = async (checked: boolean) => { - dispatch(setReadClipboardAtStartup(checked)) - await window.api.config.set('readClipboardAtStartup', checked) + await setReadClipboardAtStartup(checked) window.api.miniWindow.close() } diff --git a/src/renderer/src/store/index.ts b/src/renderer/src/store/index.ts index 8a66373558..7c9cb714fa 100644 --- a/src/renderer/src/store/index.ts +++ b/src/renderer/src/store/index.ts @@ -26,7 +26,7 @@ import ocr from './ocr' import paintings from './paintings' import preprocess from './preprocess' import runtime from './runtime' -// import selectionStore from './selectionStore' +import selectionStore from './selectionStore' import settings from './settings' import shortcuts from './shortcuts' import tabs from './tabs' @@ -52,7 +52,7 @@ const rootReducer = combineReducers({ mcp, memory, copilot, - // selectionStore, + selectionStore, tabs, preprocess, messages: newMessagesReducer, diff --git a/src/renderer/src/store/selectionStore.ts b/src/renderer/src/store/selectionStore.ts index 448cb45f0a..a9298c1810 100644 --- a/src/renderer/src/store/selectionStore.ts +++ b/src/renderer/src/store/selectionStore.ts @@ -1,7 +1,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' import type { SelectionActionItem, SelectionFilterMode, SelectionTriggerMode } from '@shared/data/preferenceTypes' -interface SelectionState { +export interface SelectionState { selectionEnabled: boolean triggerMode: SelectionTriggerMode isCompact: boolean diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index 46e06626fd..683263a424 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -863,133 +863,133 @@ const settingsSlice = createSlice({ }) export const { - setShowModelNameInMarkdown, - setShowModelProviderInMarkdown, + // setShowModelNameInMarkdown, + // setShowModelProviderInMarkdown, // setShowAssistants, // toggleShowAssistants, // setShowTopics, // toggleShowTopics, - setAssistantsTabSortType, - setSendMessageShortcut, - setLanguage, - setTargetLanguage, - setProxyMode, - setProxyUrl, - setProxyBypassRules, - setUserName, - setShowPrompt, - setShowMessageDivider, - setMessageFont, - setShowInputEstimatedTokens, - setLaunchOnBoot, - setLaunchToTray, - setTrayOnClose, - setTray, - setTheme, - setUserTheme, - setFontSize, - setWindowStyle, - setTopicPosition, - setShowTopicTime, - setPinTopicsToTop, - setAssistantIconType, - setPasteLongTextAsFile, - setAutoCheckUpdate, - setTestPlan, - setTestChannel, - setRenderInputMessageAsMarkdown, - setClickAssistantToShowTopic, + // setAssistantsTabSortType, + // setSendMessageShortcut, + // setLanguage, + // setTargetLanguage, + // setProxyMode, + // setProxyUrl, + // setProxyBypassRules, + // setUserName, + // setShowPrompt, + // setShowMessageDivider, + // setMessageFont, + // setShowInputEstimatedTokens, + // setLaunchOnBoot, + // setLaunchToTray, + // setTrayOnClose, + // setTray, + // setTheme, + // setUserTheme, + // setFontSize, + // setWindowStyle, + // setTopicPosition, + // setShowTopicTime, + // setPinTopicsToTop, + // setAssistantIconType, + // setPasteLongTextAsFile, + // setAutoCheckUpdate, + // setTestPlan, + // setTestChannel, + // setRenderInputMessageAsMarkdown, + // setClickAssistantToShowTopic, setSkipBackupFile, - setWebdavHost, - setWebdavUser, - setWebdavPass, - setWebdavPath, - setWebdavAutoSync, - setWebdavSyncInterval, - setWebdavMaxBackups, - setWebdavSkipBackupFile, - setWebdavDisableStream, - setCodeExecution, - setCodeEditor, - setCodeViewer, - setCodeShowLineNumbers, - setCodeCollapsible, - setCodeWrappable, - setCodeImageTools, - setMathEngine, - setMathEnableSingleDollar, - setFoldDisplayMode, - setGridColumns, - setGridPopoverTrigger, - setMessageStyle, + // setWebdavHost, + // setWebdavUser, + // setWebdavPass, + // setWebdavPath, + // setWebdavAutoSync, + // setWebdavSyncInterval, + // setWebdavMaxBackups, + // setWebdavSkipBackupFile, + // setWebdavDisableStream, + // setCodeExecution, + // setCodeEditor, + // setCodeViewer, + // setCodeShowLineNumbers, + // setCodeCollapsible, + // setCodeWrappable, + // setCodeImageTools, + // setMathEngine, + // setMathEnableSingleDollar, + // setFoldDisplayMode, + // setGridColumns, + // setGridPopoverTrigger, + // setMessageStyle, setTranslateModelPrompt, - setAutoTranslateWithSpace, - setShowTranslateConfirm, - setEnableTopicNaming, - setPasteLongTextThreshold, - setCustomCss, - setTopicNamingPrompt, - setSidebarIcons, - setNarrowMode, - setClickTrayToShowQuickAssistant, - setEnableQuickAssistant, - setReadClipboardAtStartup, - setMultiModelMessageStyle, - setNotionDatabaseID, - setNotionApiKey, - setNotionPageNameKey, - setmarkdownExportPath, - setForceDollarMathInMarkdown, - setUseTopicNamingForMessageTitle, - setThoughtAutoCollapse, - setNotionExportReasoning, - setExcludeCitationsInExport, - setStandardizeCitationsInExport, - setYuqueToken, - setYuqueRepoId, - setYuqueUrl, - setJoplinToken, - setJoplinUrl, - setJoplinExportReasoning, + // setAutoTranslateWithSpace, + // setShowTranslateConfirm, + // setEnableTopicNaming, + // setPasteLongTextThreshold, + // setCustomCss, + // setTopicNamingPrompt, + // setSidebarIcons, + // setNarrowMode, + // setClickTrayToShowQuickAssistant, + // setEnableQuickAssistant, + // setReadClipboardAtStartup, + // setMultiModelMessageStyle, + // setNotionDatabaseID, + // setNotionApiKey, + // setNotionPageNameKey, + // setmarkdownExportPath, + // setForceDollarMathInMarkdown, + // setUseTopicNamingForMessageTitle, + // setThoughtAutoCollapse, + // setNotionExportReasoning, + // setExcludeCitationsInExport, + // setStandardizeCitationsInExport, + // setYuqueToken, + // setYuqueRepoId, + // setYuqueUrl, + // setJoplinToken, + // setJoplinUrl, + // setJoplinExportReasoning, setMessageNavigation, setDefaultObsidianVault, setDefaultAgent, - setSiyuanApiUrl, - setSiyuanToken, - setSiyuanBoxId, + // setSiyuanApiUrl, + // setSiyuanToken, + // setSiyuanBoxId, setAgentssubscribeUrl, - setSiyuanRootPath, - setMaxKeepAliveMinapps, - setShowOpenedMinappsInSidebar, - setMinappsOpenLinkExternal, - setEnableDataCollection, - setEnableSpellCheck, - setSpellCheckLanguages, - setExportMenuOptions, - setEnableQuickPanelTriggers, - setConfirmDeleteMessage, - setConfirmRegenerateMessage, + // setSiyuanRootPath, + // setMaxKeepAliveMinapps, + // setShowOpenedMinappsInSidebar, + // setMinappsOpenLinkExternal, + // setEnableDataCollection, + // setEnableSpellCheck, + // setSpellCheckLanguages, + // setExportMenuOptions, + // setEnableQuickPanelTriggers, + // setConfirmDeleteMessage, + // setConfirmRegenerateMessage, // setDisableHardwareAcceleration, setOpenAISummaryText, setOpenAIVerbosity, - setNotificationSettings, + // setNotificationSettings, // Local backup settings - setLocalBackupDir, - setLocalBackupAutoSync, - setLocalBackupSyncInterval, - setLocalBackupMaxBackups, - setLocalBackupSkipBackupFile, + // setLocalBackupDir, + // setLocalBackupAutoSync, + // setLocalBackupSyncInterval, + // setLocalBackupMaxBackups, + // setLocalBackupSkipBackupFile, setDefaultPaintingProvider, - setS3, - setS3Partial, - setEnableDeveloperMode, - setNavbarPosition, - setShowMessageOutline, + // setS3, + // setS3Partial, + // setEnableDeveloperMode, + // setNavbarPosition, + // setShowMessageOutline, // API Server actions setApiServerEnabled, setApiServerPort, setApiServerApiKey, - setShowWorkspace, + // setShowWorkspace, toggleShowWorkspace } = settingsSlice.actions