From 3bc977cebdcef011748108643eac68a57dd6ff55 Mon Sep 17 00:00:00 2001 From: JiangYingjin Date: Mon, 3 Mar 2025 13:06:43 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=A7=E5=B9=85=E4=BC=98=E5=8C=96=E5=90=8C?= =?UTF-8?q?=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/client/platforms/google.ts | 7 +++- app/components/chat.tsx | 1 + app/components/home.tsx | 3 ++ app/components/settings.tsx | 22 +++++++++- app/store/chat.ts | 15 ++++++- app/store/sync.ts | 76 ++++++++++++++++++++++++++-------- 6 files changed, 102 insertions(+), 22 deletions(-) diff --git a/app/client/platforms/google.ts b/app/client/platforms/google.ts index c10e4969d..ee36ca9f2 100644 --- a/app/client/platforms/google.ts +++ b/app/client/platforms/google.ts @@ -83,7 +83,12 @@ export class GeminiProApi implements LLMApi { } const messages = _messages.map((v) => { let parts: any[] = [{ text: getMessageTextContent(v) }]; - if (isVisionModel(options.config.model, accessStore.visionModels)) { + if ( + isVisionModel( + options.config.model, + useAccessStore.getState().visionModels, + ) + ) { const images = getMessageImages(v); if (images.length > 0) { multimodal = true; diff --git a/app/components/chat.tsx b/app/components/chat.tsx index d0dec7be5..73f7c5ce3 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -1167,6 +1167,7 @@ function _Chat() { const onDelete = (msgId: string) => { deleteMessage(msgId); + syncStore.upload(); }; const onResend = (message: ChatMessage) => { diff --git a/app/components/home.tsx b/app/components/home.tsx index 5da490378..3568cf54d 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -28,6 +28,7 @@ import { AuthPage } from "./auth"; import { getClientConfig } from "../config/client"; import { type ClientApi, getClientApi } from "../client/api"; import { useAccessStore } from "../store"; +import { useSyncStore } from "../store/sync"; import clsx from "clsx"; export function Loading(props: { noLogo?: boolean }) { @@ -239,6 +240,8 @@ export function Home() { return ; } + useSyncStore.getState().download(); + return ( diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 6c193702c..8acbce94a 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -534,7 +534,22 @@ function SyncItems() { text={Locale.UI.Overwrite} onClick={async () => { try { - await syncStore.overwrite(); + await syncStore.upload(); + showToast(Locale.Settings.Sync.Success); + } catch (e) { + showToast(Locale.Settings.Sync.Fail); + console.error("[Sync]", e); + } + }} + /> + )} + {couldSync && ( + } + text={Locale.UI.Overwrite} + onClick={async () => { + try { + await syncStore.download(); showToast(Locale.Settings.Sync.Success); } catch (e) { showToast(Locale.Settings.Sync.Fail); @@ -1408,7 +1423,10 @@ export function Settings() { } - onClick={() => navigate(Path.Home)} + onClick={() => { + navigate(Path.Home); + useSyncStore.getState().sync(); + }} bordered /> diff --git a/app/store/chat.ts b/app/store/chat.ts index 63d7394ec..12b038e69 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -29,6 +29,7 @@ import { ModelConfig, ModelType, useAppConfig } from "./config"; import { useAccessStore } from "./access"; import { collectModelsWithDefaultModel } from "../utils/model"; import { createEmptyMask, Mask } from "./mask"; +import { useSyncStore } from "./sync"; const localStorage = safeLocalStorage(); @@ -226,6 +227,8 @@ export const useChatStore = createPersistStore( currentSessionIndex: 0, sessions: [newSession, ...state.sessions], })); + + useSyncStore.getState().upload(); }, clearSessions() { @@ -233,6 +236,8 @@ export const useChatStore = createPersistStore( sessions: [createEmptySession()], currentSessionIndex: 0, })); + + useSyncStore.getState().upload(); }, selectSession(index: number) { @@ -264,6 +269,8 @@ export const useChatStore = createPersistStore( sessions: newSessions, }; }); + + useSyncStore.getState().upload(); }, newSession(mask?: Mask) { @@ -327,6 +334,8 @@ export const useChatStore = createPersistStore( sessions, })); + useSyncStore.getState().upload(); + showToast( Locale.Home.DeleteToast, { @@ -433,6 +442,8 @@ export const useChatStore = createPersistStore( get().onNewMessage(botMessage, session); } ChatControllerPool.remove(session.id, botMessage.id); + + useSyncStore.getState().upload(); }, onBeforeTool(tool: ChatMessageTool) { (botMessage.tools = botMessage?.tools || []).push(tool); @@ -727,8 +738,10 @@ export const useChatStore = createPersistStore( console.log("[Memory] ", message); get().updateTargetSession(session, (session) => { session.lastSummarizeIndex = lastSummarizeIndex; - session.memoryPrompt = message; // Update the memory prompt for stored it in local storage + session.memoryPrompt = message; }); + + useSyncStore.getState().upload(); } }, onError(err) { diff --git a/app/store/sync.ts b/app/store/sync.ts index 105e3e83e..76699fbae 100644 --- a/app/store/sync.ts +++ b/app/store/sync.ts @@ -22,6 +22,12 @@ export interface WebDavConfig { const isApp = !!getClientConfig()?.isApp; export type SyncStore = GetStoreState; +export enum SyncAction { + SYNC = "SYNC", + UPLOAD = "UPLOAD", + DOWNLOAD = "DOWNLOAD", +} + const DEFAULT_SYNC_STATE = { provider: ProviderType.WebDAV, useProxy: true, @@ -88,41 +94,75 @@ export const useSyncStore = createPersistStore( return client; }, - async sync(overwrite = false) { + async sync(action: SyncAction = SyncAction.SYNC) { + if (!(await this.hasAccount())) { + console.log("[Sync] No account found, skipping sync."); + return; + } + const localState = getLocalAppState(); const provider = get().provider; const config = get()[provider]; const client = this.getClient(); - try { - const remoteState = await client.get(config.username); - if (!remoteState || remoteState === "") { - await client.set(config.username, JSON.stringify(localState)); - console.log( - "[Sync] Remote state is empty, using local state instead.", - ); - return; - } else { - if (!overwrite) { + if (action === SyncAction.SYNC) { + console.log("[Sync] Syncing state", config.username); + try { + const remoteState = await client.get(config.username); + if (!remoteState || remoteState === "") { + await client.set(config.username, JSON.stringify(localState)); + console.log( + "[Sync] Remote state is empty, using local state instead.", + ); + return; + } else { const parsedRemoteState = JSON.parse( await client.get(config.username), ) as AppState; mergeAppState(localState, parsedRemoteState); setLocalAppState(localState); } + } catch (e) { + console.log("[Sync] failed to get remote state", e); + throw e; + } + await client.set(config.username, JSON.stringify(localState)); + } else if (action === SyncAction.UPLOAD) { + console.log("[Sync] Uploading state", localState); + await client.set(config.username, JSON.stringify(localState)); + } else if (action === SyncAction.DOWNLOAD) { + console.log("[Sync] Downloading state", config.username); + const remoteState = await client.get(config.username); + if (!remoteState || remoteState === "") { + console.log( + "[Sync] Remote state is empty, using local state instead.", + ); + return; + } else { + const parsedRemoteState = JSON.parse(remoteState) as AppState; + setLocalAppState(parsedRemoteState); } - } catch (e) { - console.log("[Sync] failed to get remote state", e); - throw e; } - await client.set(config.username, JSON.stringify(localState)); - this.markSyncTime(); }, - async overwrite() { - await this.sync(true); + async download() { + await this.sync(SyncAction.DOWNLOAD); + }, + + async upload() { + await this.sync(SyncAction.UPLOAD); + }, + + async hasAccount() { + const provider = get().provider; + const config = get()[provider] as any; + console.log("[Sync] hasAccount", provider, config); + // console.log("[Sync] hasAccount", !!(provider === ProviderType.WebDAV ? config.username && config.password : config.username && config.apiKey)); + return provider === ProviderType.WebDAV + ? config.username && config.password + : config.username && config.apiKey; }, async check() {