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() {