Compare commits

...

4 Commits

Author SHA1 Message Date
ONLY-yours 28a56999a5 fix: fix test error 2025-09-05 17:50:00 +08:00
ONLY-yours 8920213e24 feat: add auto close notification modal switch in settings 2025-09-05 17:04:08 +08:00
ONLY-yours e41b2d5701 Merge remote-tracking branch 'origin/main' into feat/closeAutoDesktopUpdate 2025-09-05 15:27:50 +08:00
ONLY-yours 4f42de75e1 feat: add autoUpdateNotificationEnabled in settings 2025-09-05 15:22:37 +08:00
13 changed files with 210 additions and 45 deletions
+1
View File
@@ -25,6 +25,7 @@ export const defaultProxySettings: NetworkProxySettings = {
* 存储默认值
*/
export const STORE_DEFAULTS: ElectronMainStore = {
autoUpdateNotificationEnabled: true,
dataSyncConfig: { storageMode: 'local' },
encryptedTokens: {},
locale: 'auto',
@@ -0,0 +1,41 @@
import { createLogger } from '@/utils/logger';
import { ControllerModule, ipcClientEvent } from './index';
// Create logger
const logger = createLogger('controllers:DownloadCtr');
/**
* 下载控制器
* 处理桌面应用的下载相关功能,包括自动更新通知设置
*/
export default class DownloadCtr extends ControllerModule {
/**
* 获取自动更新通知设置
*/
@ipcClientEvent('getAutoUpdateNotificationEnabled')
async getAutoUpdateNotificationEnabled(): Promise<boolean> {
try {
const enabled = this.app.storeManager.get('autoUpdateNotificationEnabled', true);
logger.debug('Retrieved auto update notification setting:', enabled);
return enabled;
} catch (error) {
logger.error('Failed to get auto update notification setting:', error);
return true; // 默认启用
}
}
/**
* 设置自动更新通知
*/
@ipcClientEvent('setAutoUpdateNotificationEnabled')
async setAutoUpdateNotificationEnabled(enabled: boolean): Promise<void> {
try {
this.app.storeManager.set('autoUpdateNotificationEnabled', enabled);
logger.info('Auto update notification setting updated:', enabled);
} catch (error) {
logger.error('Failed to set auto update notification setting:', error);
throw error;
}
}
}
+1
View File
@@ -1,6 +1,7 @@
import { DataSyncConfig, NetworkProxySettings } from '@lobechat/electron-client-ipc';
export interface ElectronMainStore {
autoUpdateNotificationEnabled: boolean;
dataSyncConfig: DataSyncConfig;
encryptedTokens: {
accessToken?: string;
+1
View File
@@ -6,4 +6,5 @@ export const DEFAULT_COMMON_SETTINGS: UserGeneralConfig = {
highlighterTheme: 'lobe-theme',
mermaidTheme: 'lobe-theme',
transitionMode: 'fadeIn',
autoUpdateNotificationEnabled: true,
};
@@ -1,7 +1,9 @@
import { NetworkProxySettings } from '../types';
export interface DesktopSettingsDispatchEvents {
getAutoUpdateNotificationEnabled: () => boolean;
getProxySettings: () => NetworkProxySettings;
setAutoUpdateNotificationEnabled: (enabled: boolean) => void;
setProxySettings: (settings: Partial<NetworkProxySettings>) => void;
testProxyConfig: (data: { config: NetworkProxySettings; testUrl?: string }) => Promise<{
message?: string;
@@ -12,4 +12,6 @@ export interface UserGeneralConfig {
neutralColor?: NeutralColors;
primaryColor?: PrimaryColors;
transitionMode?: ResponseAnimationStyle;
// Desktop-specific settings
autoUpdateNotificationEnabled?: boolean;
}
@@ -1,4 +1,5 @@
import { Block, Button, Tag } from '@lobehub/ui';
import { Divider, Switch } from 'antd';
import { createStyles } from 'antd-style';
import Link from 'next/link';
import { memo } from 'react';
@@ -8,8 +9,9 @@ import { Flexbox } from 'react-layout-kit';
import { ProductLogo } from '@/components/Branding';
import { BRANDING_NAME } from '@/const/branding';
import { CHANGELOG_URL, MANUAL_UPGRADE_URL, OFFICIAL_SITE } from '@/const/url';
import { CURRENT_VERSION } from '@/const/version';
import { CURRENT_VERSION, isDesktop } from '@/const/version';
import { useNewVersion } from '@/features/User/UserPanel/useNewVersion';
import { useElectronStore } from '@/store/electron';
import { useGlobalStore } from '@/store/global';
const useStyles = createStyles(({ css, token }) => ({
@@ -21,54 +23,108 @@ const useStyles = createStyles(({ css, token }) => ({
const Version = memo<{ mobile?: boolean }>(({ mobile }) => {
const hasNewVersion = useNewVersion();
const [latestVersion] = useGlobalStore((s) => [s.latestVersion]);
const { t } = useTranslation('common');
const { t } = useTranslation(['common', 'electron']);
const { styles } = useStyles();
// Use electron store for auto update notification setting
const [
autoUpdateNotificationEnabled,
setAutoUpdateNotificationEnabled,
useFetchAutoUpdateNotificationSetting,
] = useElectronStore((s) => [
s.autoUpdateNotificationEnabled,
s.setAutoUpdateNotificationEnabled,
s.useFetchAutoUpdateNotificationSetting,
]);
// Fetch auto update notification setting (using SWR)
const { isLoading } = useFetchAutoUpdateNotificationSetting();
const handleAutoUpdateNotificationToggle = async (checked: boolean) => {
if (!isDesktop) return;
await setAutoUpdateNotificationEnabled(checked);
};
return (
<Flexbox
align={mobile ? 'stretch' : 'center'}
gap={16}
horizontal={!mobile}
justify={'space-between'}
width={'100%'}
>
<Flexbox align={'center'} flex={'none'} gap={16} horizontal>
<Link href={OFFICIAL_SITE} target={'_blank'}>
<Block
align={'center'}
className={styles.logo}
clickable
height={64}
justify={'center'}
width={64}
>
<ProductLogo size={52} />
</Block>
</Link>
<Flexbox align={'flex-start'} gap={6}>
<div style={{ fontSize: 18, fontWeight: 'bolder' }}>{BRANDING_NAME}</div>
<div>
<Tag>v{CURRENT_VERSION}</Tag>
{hasNewVersion && (
<Tag color={'info'}>
{t('upgradeVersion.newVersion', { version: `v${latestVersion}` })}
</Tag>
)}
</div>
<Flexbox gap={16} width={'100%'}>
<Flexbox
align={mobile ? 'stretch' : 'center'}
gap={16}
horizontal={!mobile}
justify={'space-between'}
width={'100%'}
>
<Flexbox align={'center'} flex={'none'} gap={16} horizontal>
<Link href={OFFICIAL_SITE} target={'_blank'}>
<Block
align={'center'}
className={styles.logo}
clickable
height={64}
justify={'center'}
width={64}
>
<ProductLogo size={52} />
</Block>
</Link>
<Flexbox align={'flex-start'} gap={6}>
<div style={{ fontSize: 18, fontWeight: 'bolder' }}>{BRANDING_NAME}</div>
<div>
<Tag>v{CURRENT_VERSION}</Tag>
{hasNewVersion && (
<Tag color={'info'}>
{t('upgradeVersion.newVersion', { version: `v${latestVersion}` })}
</Tag>
)}
</div>
</Flexbox>
</Flexbox>
<Flexbox flex={mobile ? 1 : undefined} gap={8} horizontal>
<Link href={CHANGELOG_URL} style={{ flex: 1 }} target={'_blank'}>
<Button block={mobile}>{t('changelog')}</Button>
</Link>
{hasNewVersion && (
<Link href={MANUAL_UPGRADE_URL} style={{ flex: 1 }} target={'_blank'}>
<Button block={mobile} type={'primary'}>
{t('upgradeVersion.action')}
</Button>
</Link>
)}
</Flexbox>
</Flexbox>
<Flexbox flex={mobile ? 1 : undefined} gap={8} horizontal>
<Link href={CHANGELOG_URL} style={{ flex: 1 }} target={'_blank'}>
<Button block={mobile}>{t('changelog')}</Button>
</Link>
{hasNewVersion && (
<Link href={MANUAL_UPGRADE_URL} style={{ flex: 1 }} target={'_blank'}>
<Button block={mobile} type={'primary'}>
{t('upgradeVersion.action')}
</Button>
</Link>
)}
</Flexbox>
{/* Desktop-only auto update notification setting */}
{isDesktop && (
<>
<Divider style={{ margin: '8px 0' }} />
<Flexbox
align={'center'}
horizontal
justify={'space-between'}
style={{ padding: '0 4px' }}
>
<Flexbox gap={4}>
<div style={{ fontSize: 14, fontWeight: 'medium' }}>
{t('updater.autoUpdateNotification', { ns: 'electron' })}
</div>
<div
style={{
color: 'var(--ant-color-text-secondary)',
fontSize: 12,
}}
>
{t('updater.autoUpdateNotificationDesc', { ns: 'electron' })}
</div>
</Flexbox>
<Switch
checked={autoUpdateNotificationEnabled}
loading={isLoading}
onChange={handleAutoUpdateNotificationToggle}
size="small"
/>
</Flexbox>
</>
)}
</Flexbox>
);
});
+10 -1
View File
@@ -5,6 +5,7 @@ import React, { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { autoUpdateService } from '@/services/electron/autoUpdate';
import { useElectronStore } from '@/store/electron';
import { formatSpeed } from '@/utils/format';
export const UpdateModal = memo(() => {
@@ -17,6 +18,9 @@ export const UpdateModal = memo(() => {
const [progress, setProgress] = useState<ProgressInfo | null>(null);
const [latestVersionInfo, setLatestVersionInfo] = useState<UpdateInfo | null>(null); // State for latest version modal
const { modal } = App.useApp();
// Get auto update notification setting from electron store
const autoUpdateNotificationEnabled = useElectronStore((s) => s.autoUpdateNotificationEnabled);
// --- Event Listeners ---
useWatchBroadcast('manualUpdateCheckStart', () => {
@@ -86,7 +90,12 @@ export const UpdateModal = memo(() => {
// This event implies a download finished, likely the one we started manually
setIsChecking(false);
setIsDownloading(false);
setDownloadedInfo(info);
// Only show the modal if auto update notifications are enabled
if (autoUpdateNotificationEnabled) {
setDownloadedInfo(info);
}
setProgress(null); // Clear progress
setLatestVersionInfo(null); // Ensure other modals are closed
setUpdateAvailableInfo(null);
+2
View File
@@ -84,6 +84,8 @@ const electron = {
},
},
updater: {
autoUpdateNotification: '自动更新提示',
autoUpdateNotificationDesc: '当有新版本可用时,是否显示弹窗提示更新',
checkingUpdate: '检查新版本',
checkingUpdateDesc: '正在获取版本信息...',
downloadNewVersion: '下载新版本',
+14
View File
@@ -5,6 +5,20 @@ import {
} from '@lobechat/electron-client-ipc';
class DesktopSettingsService {
/**
* 获取自动更新通知设置
*/
getAutoUpdateNotificationEnabled = async (): Promise<boolean> => {
return dispatch('getAutoUpdateNotificationEnabled');
};
/**
* 设置自动更新通知
*/
setAutoUpdateNotificationEnabled = async (enabled: boolean): Promise<void> => {
return dispatch('setAutoUpdateNotificationEnabled', enabled);
};
/**
* 获取远程服务器配置
*/
+33
View File
@@ -11,14 +11,18 @@ import type { ElectronStore } from '../store';
* 设置操作
*/
export interface ElectronSettingsAction {
refreshAutoUpdateNotificationSetting: () => Promise<void>;
refreshDesktopHotkeys: () => Promise<void>;
refreshProxySettings: () => Promise<void>;
setAutoUpdateNotificationEnabled: (enabled: boolean) => Promise<void>;
setProxySettings: (params: Partial<NetworkProxySettings>) => Promise<void>;
updateDesktopHotkey: (id: string, accelerator: string) => Promise<ShortcutUpdateResult>;
useFetchAutoUpdateNotificationSetting: () => SWRResponse;
useFetchDesktopHotkeys: () => SWRResponse;
useGetProxySettings: () => SWRResponse;
}
const ELECTRON_AUTO_UPDATE_NOTIFICATION_KEY = 'electron:getAutoUpdateNotificationEnabled';
const ELECTRON_PROXY_SETTINGS_KEY = 'electron:getProxySettings';
const ELECTRON_DESKTOP_HOTKEYS_KEY = 'electron:getDesktopHotkeys';
@@ -28,6 +32,10 @@ export const settingsSlice: StateCreator<
[],
ElectronSettingsAction
> = (set, get) => ({
refreshAutoUpdateNotificationSetting: async () => {
await mutate(ELECTRON_AUTO_UPDATE_NOTIFICATION_KEY);
},
refreshDesktopHotkeys: async () => {
await mutate(ELECTRON_DESKTOP_HOTKEYS_KEY);
},
@@ -36,6 +44,18 @@ export const settingsSlice: StateCreator<
await mutate(ELECTRON_PROXY_SETTINGS_KEY);
},
setAutoUpdateNotificationEnabled: async (enabled) => {
try {
// 更新设置
await desktopSettingsService.setAutoUpdateNotificationEnabled(enabled);
// 刷新状态
await get().refreshAutoUpdateNotificationSetting();
} catch (error) {
console.error('自动更新通知设置更新失败:', error);
}
},
setProxySettings: async (values) => {
try {
// 更新设置
@@ -68,6 +88,19 @@ export const settingsSlice: StateCreator<
}
},
useFetchAutoUpdateNotificationSetting: () =>
useSWR<boolean>(
ELECTRON_AUTO_UPDATE_NOTIFICATION_KEY,
async () => desktopSettingsService.getAutoUpdateNotificationEnabled(),
{
onSuccess: (data) => {
if (data !== get().autoUpdateNotificationEnabled) {
set({ autoUpdateNotificationEnabled: data });
}
},
},
),
useFetchDesktopHotkeys: () =>
useSWR<Record<string, string>>(
ELECTRON_DESKTOP_HOTKEYS_KEY,
+2
View File
@@ -17,6 +17,7 @@ export const defaultProxySettings: NetworkProxySettings = {
export interface ElectronState {
appState: ElectronAppState;
autoUpdateNotificationEnabled: boolean;
dataSyncConfig: DataSyncConfig;
desktopHotkeys: Record<string, string>;
isAppStateInit?: boolean;
@@ -30,6 +31,7 @@ export interface ElectronState {
export const initialState: ElectronState = {
appState: {},
autoUpdateNotificationEnabled: true,
dataSyncConfig: { storageMode: 'local' },
desktopHotkeys: {},
isAppStateInit: false,
@@ -17,6 +17,7 @@ describe('settingsSelectors', () => {
expect(result).toEqual({
animationMode: 'agile',
autoUpdateNotificationEnabled: true,
fontSize: 12,
highlighterTheme: 'lobe-theme',
mermaidTheme: 'lobe-theme',