mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
✨ feat(desktop): add device gateway status indicator in titlebar (#13260)
* support desktop gateway * support device mode * ✨ feat(desktop): add device gateway status indicator in titlebar Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ✅ test(desktop): update getDeviceInfo test to include name and description fields Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ✏️ chore(i18n): update gateway status copy to reference Gateway instead of cloud Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ✏️ chore(i18n): translate Gateway to 网关 in zh-CN Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ✏️ chore(i18n): simplify description placeholder to Optional Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ✏️ chore(desktop): use fixed title 'Connect to Gateway' in device popover Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -28,7 +28,9 @@ export const defaultProxySettings: NetworkProxySettings = {
|
||||
export const STORE_DEFAULTS: ElectronMainStore = {
|
||||
dataSyncConfig: { storageMode: 'cloud' },
|
||||
encryptedTokens: {},
|
||||
gatewayDeviceDescription: '',
|
||||
gatewayDeviceId: '',
|
||||
gatewayDeviceName: '',
|
||||
gatewayUrl: 'https://device-gateway.lobehub.com',
|
||||
locale: 'auto',
|
||||
networkProxy: defaultProxySettings,
|
||||
|
||||
@@ -70,13 +70,27 @@ export default class GatewayConnectionCtr extends ControllerModule {
|
||||
|
||||
@IpcMethod()
|
||||
async getDeviceInfo(): Promise<{
|
||||
description: string;
|
||||
deviceId: string;
|
||||
hostname: string;
|
||||
name: string;
|
||||
platform: string;
|
||||
}> {
|
||||
return this.service.getDeviceInfo();
|
||||
}
|
||||
|
||||
@IpcMethod()
|
||||
async setDeviceName(params: { name: string }): Promise<{ success: boolean }> {
|
||||
this.service.setDeviceName(params.name);
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
@IpcMethod()
|
||||
async setDeviceDescription(params: { description: string }): Promise<{ success: boolean }> {
|
||||
this.service.setDeviceDescription(params.description);
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
// ─── Auto Connect ───
|
||||
|
||||
private async tryAutoConnect() {
|
||||
|
||||
@@ -553,8 +553,10 @@ describe('GatewayConnectionCtr', () => {
|
||||
|
||||
const info = await ctr.getDeviceInfo();
|
||||
expect(info).toEqual({
|
||||
description: '',
|
||||
deviceId: 'my-device',
|
||||
hostname: 'mock-hostname',
|
||||
name: 'mock-hostname',
|
||||
platform: process.platform,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -84,12 +84,32 @@ export default class GatewayConnectionService extends ServiceModule {
|
||||
|
||||
getDeviceInfo() {
|
||||
return {
|
||||
description: this.getDeviceDescription(),
|
||||
deviceId: this.getDeviceId(),
|
||||
hostname: os.hostname(),
|
||||
name: this.getDeviceName(),
|
||||
platform: process.platform,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Device Name & Description ───
|
||||
|
||||
getDeviceName(): string {
|
||||
return (this.app.storeManager.get('gatewayDeviceName') as string) || os.hostname();
|
||||
}
|
||||
|
||||
setDeviceName(name: string) {
|
||||
this.app.storeManager.set('gatewayDeviceName', name);
|
||||
}
|
||||
|
||||
getDeviceDescription(): string {
|
||||
return (this.app.storeManager.get('gatewayDeviceDescription') as string) || '';
|
||||
}
|
||||
|
||||
setDeviceDescription(description: string) {
|
||||
this.app.storeManager.set('gatewayDeviceDescription', description);
|
||||
}
|
||||
|
||||
// ─── Connection Logic ───
|
||||
|
||||
async connect(): Promise<{ error?: string; success: boolean }> {
|
||||
|
||||
@@ -12,7 +12,9 @@ export interface ElectronMainStore {
|
||||
lastRefreshAt?: number;
|
||||
refreshToken?: string;
|
||||
};
|
||||
gatewayDeviceDescription: string;
|
||||
gatewayDeviceId: string;
|
||||
gatewayDeviceName: string;
|
||||
gatewayUrl: string;
|
||||
locale: string;
|
||||
networkProxy: NetworkProxySettings;
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
{
|
||||
"gateway.description": "Description",
|
||||
"gateway.descriptionPlaceholder": "Optional",
|
||||
"gateway.deviceName": "Device Name",
|
||||
"gateway.deviceNamePlaceholder": "Enter device name",
|
||||
"gateway.enableConnection": "Connect to Gateway",
|
||||
"gateway.statusConnected": "Connected to Gateway",
|
||||
"gateway.statusConnecting": "Connecting to Gateway...",
|
||||
"gateway.statusDisconnected": "Not connected to Gateway",
|
||||
"gateway.title": "Device Gateway",
|
||||
"navigation.chat": "Chat",
|
||||
"navigation.discover": "Discover",
|
||||
"navigation.discoverAssistants": "Discover Assistants",
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
{
|
||||
"gateway.description": "描述",
|
||||
"gateway.descriptionPlaceholder": "可选",
|
||||
"gateway.deviceName": "设备名称",
|
||||
"gateway.deviceNamePlaceholder": "输入设备名称",
|
||||
"gateway.enableConnection": "连接到网关",
|
||||
"gateway.statusConnected": "已连接到网关",
|
||||
"gateway.statusConnecting": "正在连接到网关...",
|
||||
"gateway.statusDisconnected": "未连接到网关",
|
||||
"gateway.title": "设备网关",
|
||||
"navigation.chat": "对话",
|
||||
"navigation.discover": "发现",
|
||||
"navigation.discoverAssistants": "发现助理",
|
||||
|
||||
180
src/features/Electron/connection/DeviceGateway.tsx
Normal file
180
src/features/Electron/connection/DeviceGateway.tsx
Normal file
@@ -0,0 +1,180 @@
|
||||
import { useWatchBroadcast } from '@lobechat/electron-client-ipc';
|
||||
import { ActionIcon, Flexbox } from '@lobehub/ui';
|
||||
import { Input, Popover, Switch } from 'antd';
|
||||
import { createStyles, cssVar } from 'antd-style';
|
||||
import { HardDrive } from 'lucide-react';
|
||||
import { memo, useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useElectronStore } from '@/store/electron';
|
||||
import { electronSyncSelectors } from '@/store/electron/selectors';
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
fieldLabel: css`
|
||||
font-size: 12px;
|
||||
color: ${cssVar.colorTextDescription};
|
||||
`,
|
||||
greenDot: css`
|
||||
position: absolute;
|
||||
inset-block-end: 0;
|
||||
inset-inline-end: 0;
|
||||
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border: 1.5px solid ${cssVar.colorBgContainer};
|
||||
border-radius: 50%;
|
||||
|
||||
background: #52c41a;
|
||||
`,
|
||||
input: css`
|
||||
border: none;
|
||||
background: ${token.colorFillTertiary};
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: ${token.colorFillSecondary};
|
||||
}
|
||||
`,
|
||||
popoverContent: css`
|
||||
width: 280px;
|
||||
padding-block: 4px;
|
||||
padding-inline: 0;
|
||||
`,
|
||||
statusTitle: css`
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: ${cssVar.colorText};
|
||||
`,
|
||||
}));
|
||||
|
||||
const DeviceGateway = memo(() => {
|
||||
const { t } = useTranslation('electron');
|
||||
const { styles } = useStyles();
|
||||
|
||||
const [
|
||||
gatewayStatus,
|
||||
connectGateway,
|
||||
disconnectGateway,
|
||||
setGatewayConnectionStatus,
|
||||
useFetchGatewayStatus,
|
||||
useFetchGatewayDeviceInfo,
|
||||
updateDeviceName,
|
||||
updateDeviceDescription,
|
||||
gatewayDeviceInfo,
|
||||
] = useElectronStore((s) => [
|
||||
s.gatewayConnectionStatus,
|
||||
s.connectGateway,
|
||||
s.disconnectGateway,
|
||||
s.setGatewayConnectionStatus,
|
||||
s.useFetchGatewayStatus,
|
||||
s.useFetchGatewayDeviceInfo,
|
||||
s.updateDeviceName,
|
||||
s.updateDeviceDescription,
|
||||
s.gatewayDeviceInfo,
|
||||
]);
|
||||
|
||||
useFetchGatewayStatus();
|
||||
useFetchGatewayDeviceInfo();
|
||||
|
||||
useWatchBroadcast('gatewayConnectionStatusChanged', ({ status }) => {
|
||||
setGatewayConnectionStatus(status);
|
||||
});
|
||||
|
||||
const isConnected = gatewayStatus === 'connected';
|
||||
const isConnecting = gatewayStatus === 'connecting' || gatewayStatus === 'reconnecting';
|
||||
|
||||
const [localName, setLocalName] = useState<string | undefined>();
|
||||
const [localDescription, setLocalDescription] = useState<string | undefined>();
|
||||
|
||||
const handleSwitchChange = useCallback(
|
||||
async (checked: boolean) => {
|
||||
if (checked) {
|
||||
await connectGateway();
|
||||
} else {
|
||||
await disconnectGateway();
|
||||
}
|
||||
},
|
||||
[connectGateway, disconnectGateway],
|
||||
);
|
||||
|
||||
const handleNameBlur = useCallback(() => {
|
||||
if (localName !== undefined && localName !== gatewayDeviceInfo?.name) {
|
||||
updateDeviceName(localName);
|
||||
}
|
||||
setLocalName(undefined);
|
||||
}, [localName, gatewayDeviceInfo?.name, updateDeviceName]);
|
||||
|
||||
const handleDescriptionBlur = useCallback(() => {
|
||||
if (localDescription !== undefined && localDescription !== gatewayDeviceInfo?.description) {
|
||||
updateDeviceDescription(localDescription);
|
||||
}
|
||||
setLocalDescription(undefined);
|
||||
}, [localDescription, gatewayDeviceInfo?.description, updateDeviceDescription]);
|
||||
|
||||
const popoverContent = (
|
||||
<Flexbox className={styles.popoverContent} gap={16}>
|
||||
<Flexbox horizontal align="center" justify="space-between">
|
||||
<span className={styles.statusTitle}>{t('gateway.enableConnection')}</span>
|
||||
<Switch
|
||||
checked={isConnected || isConnecting}
|
||||
loading={isConnecting}
|
||||
size="small"
|
||||
onChange={handleSwitchChange}
|
||||
/>
|
||||
</Flexbox>
|
||||
|
||||
<Flexbox gap={4}>
|
||||
<span className={styles.fieldLabel}>{t('gateway.deviceName')}</span>
|
||||
<Input
|
||||
className={styles.input}
|
||||
placeholder={t('gateway.deviceNamePlaceholder')}
|
||||
size="small"
|
||||
value={localName ?? gatewayDeviceInfo?.name ?? ''}
|
||||
variant="filled"
|
||||
onBlur={handleNameBlur}
|
||||
onChange={(e) => setLocalName(e.target.value)}
|
||||
onPressEnter={handleNameBlur}
|
||||
/>
|
||||
</Flexbox>
|
||||
|
||||
<Flexbox gap={4}>
|
||||
<span className={styles.fieldLabel}>{t('gateway.description')}</span>
|
||||
<Input.TextArea
|
||||
autoSize={{ maxRows: 3, minRows: 2 }}
|
||||
className={styles.input}
|
||||
placeholder={t('gateway.descriptionPlaceholder')}
|
||||
size="small"
|
||||
value={localDescription ?? gatewayDeviceInfo?.description ?? ''}
|
||||
variant="filled"
|
||||
onBlur={handleDescriptionBlur}
|
||||
onChange={(e) => setLocalDescription(e.target.value)}
|
||||
/>
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover arrow={false} content={popoverContent} placement="bottomRight" trigger="click">
|
||||
<div style={{ position: 'relative' }}>
|
||||
<ActionIcon
|
||||
icon={HardDrive}
|
||||
loading={isConnecting}
|
||||
size="small"
|
||||
title={t('gateway.title')}
|
||||
tooltipProps={{ placement: 'bottomRight' }}
|
||||
/>
|
||||
{isConnected && <div className={styles.greenDot} />}
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
|
||||
const DeviceGatewayWithAuth = memo(() => {
|
||||
const isSyncActive = useElectronStore(electronSyncSelectors.isSyncActive);
|
||||
|
||||
if (!isSyncActive) return null;
|
||||
|
||||
return <DeviceGateway />;
|
||||
});
|
||||
|
||||
export default DeviceGatewayWithAuth;
|
||||
@@ -7,6 +7,7 @@ import { electronStylish } from '@/styles/electron';
|
||||
import { getPlatform } from '@/utils/platform';
|
||||
|
||||
import Connection from '../connection/Connection';
|
||||
import DeviceGateway from '../connection/DeviceGateway';
|
||||
import { useTabNavigation } from '../navigation/useTabNavigation';
|
||||
import { useWatchThemeUpdate } from '../system/useWatchThemeUpdate';
|
||||
import { UpdateNotification } from '../updater/UpdateNotification';
|
||||
@@ -39,6 +40,7 @@ const TitleBar = memo(() => {
|
||||
<Flexbox horizontal align={'center'} gap={4}>
|
||||
<Flexbox horizontal className={electronStylish.nodrag} gap={8}>
|
||||
<UpdateNotification />
|
||||
<DeviceGateway />
|
||||
<Connection />
|
||||
</Flexbox>
|
||||
{showCustomWinControl && (
|
||||
|
||||
@@ -68,6 +68,15 @@ export default {
|
||||
'proxy.validation.serverRequired': 'Server address is required when proxy is enabled',
|
||||
'proxy.validation.typeRequired': 'Proxy type is required when proxy is enabled',
|
||||
'proxy.validation.usernameRequired': 'Username is required when authentication is enabled',
|
||||
'gateway.description': 'Description',
|
||||
'gateway.descriptionPlaceholder': 'Optional',
|
||||
'gateway.deviceName': 'Device Name',
|
||||
'gateway.deviceNamePlaceholder': 'Enter device name',
|
||||
'gateway.enableConnection': 'Connect to Gateway',
|
||||
'gateway.statusConnected': 'Connected to Gateway',
|
||||
'gateway.statusConnecting': 'Connecting to Gateway...',
|
||||
'gateway.statusDisconnected': 'Not connected to Gateway',
|
||||
'gateway.title': 'Device Gateway',
|
||||
'remoteServer.authError': 'Authorization failed: {{error}}',
|
||||
'remoteServer.authPending': 'Please complete the authorization in your browser',
|
||||
'remoteServer.configDesc': 'Connect to the remote LobeHub server to enable data synchronization',
|
||||
|
||||
29
src/services/electron/gatewayConnection.ts
Normal file
29
src/services/electron/gatewayConnection.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { ensureElectronIpc } from '@/utils/electron/ipc';
|
||||
|
||||
class GatewayConnectionService {
|
||||
connect = async () => {
|
||||
return ensureElectronIpc().gatewayConnection.connect();
|
||||
};
|
||||
|
||||
disconnect = async () => {
|
||||
return ensureElectronIpc().gatewayConnection.disconnect();
|
||||
};
|
||||
|
||||
getConnectionStatus = async () => {
|
||||
return ensureElectronIpc().gatewayConnection.getConnectionStatus();
|
||||
};
|
||||
|
||||
getDeviceInfo = async () => {
|
||||
return ensureElectronIpc().gatewayConnection.getDeviceInfo();
|
||||
};
|
||||
|
||||
setDeviceDescription = async (description: string) => {
|
||||
return ensureElectronIpc().gatewayConnection.setDeviceDescription({ description });
|
||||
};
|
||||
|
||||
setDeviceName = async (name: string) => {
|
||||
return ensureElectronIpc().gatewayConnection.setDeviceName({ name });
|
||||
};
|
||||
}
|
||||
|
||||
export const gatewayConnectionService = new GatewayConnectionService();
|
||||
111
src/store/electron/actions/gateway.ts
Normal file
111
src/store/electron/actions/gateway.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import type { GatewayConnectionStatus } from '@lobechat/electron-client-ipc';
|
||||
import { type SWRResponse } from 'swr';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import { mutate } from '@/libs/swr';
|
||||
import { gatewayConnectionService } from '@/services/electron/gatewayConnection';
|
||||
import { type StoreSetter } from '@/store/types';
|
||||
|
||||
import { type ElectronStore } from '../store';
|
||||
|
||||
const GATEWAY_DEVICE_INFO_KEY = 'electron:getGatewayDeviceInfo';
|
||||
|
||||
type Setter = StoreSetter<ElectronStore>;
|
||||
export const gatewaySlice = (set: Setter, get: () => ElectronStore, _api?: unknown) =>
|
||||
new ElectronGatewayActionImpl(set, get, _api);
|
||||
|
||||
export interface GatewayDeviceInfo {
|
||||
description: string;
|
||||
deviceId: string;
|
||||
hostname: string;
|
||||
name: string;
|
||||
platform: string;
|
||||
}
|
||||
|
||||
export class ElectronGatewayActionImpl {
|
||||
readonly #get: () => ElectronStore;
|
||||
readonly #set: Setter;
|
||||
|
||||
constructor(set: Setter, get: () => ElectronStore, _api?: unknown) {
|
||||
void _api;
|
||||
this.#set = set;
|
||||
this.#get = get;
|
||||
}
|
||||
|
||||
connectGateway = async (): Promise<void> => {
|
||||
this.#set({ gatewayConnectionStatus: 'connecting' });
|
||||
try {
|
||||
const result = await gatewayConnectionService.connect();
|
||||
if (!result.success) {
|
||||
this.#set({ gatewayConnectionStatus: 'disconnected' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Gateway connect failed:', error);
|
||||
this.#set({ gatewayConnectionStatus: 'disconnected' });
|
||||
}
|
||||
};
|
||||
|
||||
disconnectGateway = async (): Promise<void> => {
|
||||
try {
|
||||
await gatewayConnectionService.disconnect();
|
||||
this.#set({ gatewayConnectionStatus: 'disconnected' });
|
||||
} catch (error) {
|
||||
console.error('Gateway disconnect failed:', error);
|
||||
}
|
||||
};
|
||||
|
||||
refreshGatewayDeviceInfo = async (): Promise<void> => {
|
||||
await mutate(GATEWAY_DEVICE_INFO_KEY);
|
||||
};
|
||||
|
||||
setGatewayConnectionStatus = (status: GatewayConnectionStatus): void => {
|
||||
this.#set({ gatewayConnectionStatus: status }, false, 'setGatewayConnectionStatus');
|
||||
};
|
||||
|
||||
updateDeviceDescription = async (description: string): Promise<void> => {
|
||||
try {
|
||||
await gatewayConnectionService.setDeviceDescription(description);
|
||||
await this.#get().refreshGatewayDeviceInfo();
|
||||
} catch (error) {
|
||||
console.error('Update device description failed:', error);
|
||||
}
|
||||
};
|
||||
|
||||
updateDeviceName = async (name: string): Promise<void> => {
|
||||
try {
|
||||
await gatewayConnectionService.setDeviceName(name);
|
||||
await this.#get().refreshGatewayDeviceInfo();
|
||||
} catch (error) {
|
||||
console.error('Update device name failed:', error);
|
||||
}
|
||||
};
|
||||
|
||||
useFetchGatewayDeviceInfo = (): SWRResponse<GatewayDeviceInfo> => {
|
||||
return useSWR<GatewayDeviceInfo>(
|
||||
GATEWAY_DEVICE_INFO_KEY,
|
||||
async () => gatewayConnectionService.getDeviceInfo() as Promise<GatewayDeviceInfo>,
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
this.#set({ gatewayDeviceInfo: data }, false, 'setGatewayDeviceInfo');
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
useFetchGatewayStatus = (): SWRResponse<{ status: GatewayConnectionStatus }> => {
|
||||
return useSWR<{ status: GatewayConnectionStatus }>(
|
||||
'electron:getGatewayConnectionStatus',
|
||||
async () => gatewayConnectionService.getConnectionStatus(),
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
this.#set({ gatewayConnectionStatus: data.status }, false, 'setGatewayConnectionStatus');
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export type ElectronGatewayAction = Pick<
|
||||
ElectronGatewayActionImpl,
|
||||
keyof ElectronGatewayActionImpl
|
||||
>;
|
||||
@@ -1,9 +1,11 @@
|
||||
import {
|
||||
type DataSyncConfig,
|
||||
type ElectronAppState,
|
||||
type GatewayConnectionStatus,
|
||||
type NetworkProxySettings,
|
||||
} from '@lobechat/electron-client-ipc';
|
||||
|
||||
import { type GatewayDeviceInfo } from './actions/gateway';
|
||||
import { type NavigationHistoryState } from './actions/navigationHistory';
|
||||
import { navigationHistoryInitialState } from './actions/navigationHistory';
|
||||
import { type RecentPagesState } from './actions/recentPages';
|
||||
@@ -26,6 +28,8 @@ export interface ElectronState extends NavigationHistoryState, RecentPagesState,
|
||||
appState: ElectronAppState;
|
||||
dataSyncConfig: DataSyncConfig;
|
||||
desktopHotkeys: Record<string, string>;
|
||||
gatewayConnectionStatus: GatewayConnectionStatus;
|
||||
gatewayDeviceInfo?: GatewayDeviceInfo;
|
||||
isAppStateInit?: boolean;
|
||||
isConnectingServer?: boolean;
|
||||
isConnectionDrawerOpen?: boolean;
|
||||
@@ -43,6 +47,7 @@ export const initialState: ElectronState = {
|
||||
appState: {},
|
||||
dataSyncConfig: { storageMode: 'cloud' },
|
||||
desktopHotkeys: {},
|
||||
gatewayConnectionStatus: 'disconnected',
|
||||
isAppStateInit: false,
|
||||
isConnectingServer: false,
|
||||
isConnectionDrawerOpen: false,
|
||||
|
||||
@@ -7,6 +7,8 @@ import { expose } from '../middleware/expose';
|
||||
import { flattenActions } from '../utils/flattenActions';
|
||||
import { type ElectronAppAction } from './actions/app';
|
||||
import { createElectronAppSlice } from './actions/app';
|
||||
import { type ElectronGatewayAction } from './actions/gateway';
|
||||
import { gatewaySlice } from './actions/gateway';
|
||||
import { type NavigationHistoryAction } from './actions/navigationHistory';
|
||||
import { createNavigationHistorySlice } from './actions/navigationHistory';
|
||||
import { type RecentPagesAction } from './actions/recentPages';
|
||||
@@ -27,6 +29,7 @@ export interface ElectronStore
|
||||
ElectronState,
|
||||
ElectronRemoteServerAction,
|
||||
ElectronAppAction,
|
||||
ElectronGatewayAction,
|
||||
ElectronSettingsAction,
|
||||
NavigationHistoryAction,
|
||||
RecentPagesAction,
|
||||
@@ -36,6 +39,7 @@ export interface ElectronStore
|
||||
|
||||
type ElectronStoreAction = ElectronRemoteServerAction &
|
||||
ElectronAppAction &
|
||||
ElectronGatewayAction &
|
||||
ElectronSettingsAction &
|
||||
NavigationHistoryAction &
|
||||
RecentPagesAction &
|
||||
@@ -48,6 +52,7 @@ const createStore: StateCreator<ElectronStore, [['zustand/devtools', never]]> =
|
||||
...flattenActions<ElectronStoreAction>([
|
||||
remoteSyncSlice(...parameters),
|
||||
createElectronAppSlice(...parameters),
|
||||
gatewaySlice(...parameters),
|
||||
settingsSlice(...parameters),
|
||||
createNavigationHistorySlice(...parameters),
|
||||
createRecentPagesSlice(...parameters),
|
||||
|
||||
Reference in New Issue
Block a user