大幅优化同步

This commit is contained in:
JiangYingjin 2025-03-03 13:06:43 +08:00
parent 88f8ca822f
commit 3bc977cebd
6 changed files with 102 additions and 22 deletions

View File

@ -83,7 +83,12 @@ export class GeminiProApi implements LLMApi {
} }
const messages = _messages.map((v) => { const messages = _messages.map((v) => {
let parts: any[] = [{ text: getMessageTextContent(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); const images = getMessageImages(v);
if (images.length > 0) { if (images.length > 0) {
multimodal = true; multimodal = true;

View File

@ -1167,6 +1167,7 @@ function _Chat() {
const onDelete = (msgId: string) => { const onDelete = (msgId: string) => {
deleteMessage(msgId); deleteMessage(msgId);
syncStore.upload();
}; };
const onResend = (message: ChatMessage) => { const onResend = (message: ChatMessage) => {

View File

@ -28,6 +28,7 @@ import { AuthPage } from "./auth";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { type ClientApi, getClientApi } from "../client/api"; import { type ClientApi, getClientApi } from "../client/api";
import { useAccessStore } from "../store"; import { useAccessStore } from "../store";
import { useSyncStore } from "../store/sync";
import clsx from "clsx"; import clsx from "clsx";
export function Loading(props: { noLogo?: boolean }) { export function Loading(props: { noLogo?: boolean }) {
@ -239,6 +240,8 @@ export function Home() {
return <Loading />; return <Loading />;
} }
useSyncStore.getState().download();
return ( return (
<ErrorBoundary> <ErrorBoundary>
<Router> <Router>

View File

@ -534,7 +534,22 @@ function SyncItems() {
text={Locale.UI.Overwrite} text={Locale.UI.Overwrite}
onClick={async () => { onClick={async () => {
try { try {
await syncStore.overwrite(); await syncStore.upload();
showToast(Locale.Settings.Sync.Success);
} catch (e) {
showToast(Locale.Settings.Sync.Fail);
console.error("[Sync]", e);
}
}}
/>
)}
{couldSync && (
<IconButton
icon={<DownloadIcon />}
text={Locale.UI.Overwrite}
onClick={async () => {
try {
await syncStore.download();
showToast(Locale.Settings.Sync.Success); showToast(Locale.Settings.Sync.Success);
} catch (e) { } catch (e) {
showToast(Locale.Settings.Sync.Fail); showToast(Locale.Settings.Sync.Fail);
@ -1408,7 +1423,10 @@ export function Settings() {
<IconButton <IconButton
aria={Locale.UI.Close} aria={Locale.UI.Close}
icon={<CloseIcon />} icon={<CloseIcon />}
onClick={() => navigate(Path.Home)} onClick={() => {
navigate(Path.Home);
useSyncStore.getState().sync();
}}
bordered bordered
/> />
</div> </div>

View File

@ -29,6 +29,7 @@ import { ModelConfig, ModelType, useAppConfig } from "./config";
import { useAccessStore } from "./access"; import { useAccessStore } from "./access";
import { collectModelsWithDefaultModel } from "../utils/model"; import { collectModelsWithDefaultModel } from "../utils/model";
import { createEmptyMask, Mask } from "./mask"; import { createEmptyMask, Mask } from "./mask";
import { useSyncStore } from "./sync";
const localStorage = safeLocalStorage(); const localStorage = safeLocalStorage();
@ -226,6 +227,8 @@ export const useChatStore = createPersistStore(
currentSessionIndex: 0, currentSessionIndex: 0,
sessions: [newSession, ...state.sessions], sessions: [newSession, ...state.sessions],
})); }));
useSyncStore.getState().upload();
}, },
clearSessions() { clearSessions() {
@ -233,6 +236,8 @@ export const useChatStore = createPersistStore(
sessions: [createEmptySession()], sessions: [createEmptySession()],
currentSessionIndex: 0, currentSessionIndex: 0,
})); }));
useSyncStore.getState().upload();
}, },
selectSession(index: number) { selectSession(index: number) {
@ -264,6 +269,8 @@ export const useChatStore = createPersistStore(
sessions: newSessions, sessions: newSessions,
}; };
}); });
useSyncStore.getState().upload();
}, },
newSession(mask?: Mask) { newSession(mask?: Mask) {
@ -327,6 +334,8 @@ export const useChatStore = createPersistStore(
sessions, sessions,
})); }));
useSyncStore.getState().upload();
showToast( showToast(
Locale.Home.DeleteToast, Locale.Home.DeleteToast,
{ {
@ -433,6 +442,8 @@ export const useChatStore = createPersistStore(
get().onNewMessage(botMessage, session); get().onNewMessage(botMessage, session);
} }
ChatControllerPool.remove(session.id, botMessage.id); ChatControllerPool.remove(session.id, botMessage.id);
useSyncStore.getState().upload();
}, },
onBeforeTool(tool: ChatMessageTool) { onBeforeTool(tool: ChatMessageTool) {
(botMessage.tools = botMessage?.tools || []).push(tool); (botMessage.tools = botMessage?.tools || []).push(tool);
@ -727,8 +738,10 @@ export const useChatStore = createPersistStore(
console.log("[Memory] ", message); console.log("[Memory] ", message);
get().updateTargetSession(session, (session) => { get().updateTargetSession(session, (session) => {
session.lastSummarizeIndex = lastSummarizeIndex; session.lastSummarizeIndex = lastSummarizeIndex;
session.memoryPrompt = message; // Update the memory prompt for stored it in local storage session.memoryPrompt = message;
}); });
useSyncStore.getState().upload();
} }
}, },
onError(err) { onError(err) {

View File

@ -22,6 +22,12 @@ export interface WebDavConfig {
const isApp = !!getClientConfig()?.isApp; const isApp = !!getClientConfig()?.isApp;
export type SyncStore = GetStoreState<typeof useSyncStore>; export type SyncStore = GetStoreState<typeof useSyncStore>;
export enum SyncAction {
SYNC = "SYNC",
UPLOAD = "UPLOAD",
DOWNLOAD = "DOWNLOAD",
}
const DEFAULT_SYNC_STATE = { const DEFAULT_SYNC_STATE = {
provider: ProviderType.WebDAV, provider: ProviderType.WebDAV,
useProxy: true, useProxy: true,
@ -88,41 +94,75 @@ export const useSyncStore = createPersistStore(
return client; 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 localState = getLocalAppState();
const provider = get().provider; const provider = get().provider;
const config = get()[provider]; const config = get()[provider];
const client = this.getClient(); const client = this.getClient();
try { if (action === SyncAction.SYNC) {
const remoteState = await client.get(config.username); console.log("[Sync] Syncing state", config.username);
if (!remoteState || remoteState === "") { try {
await client.set(config.username, JSON.stringify(localState)); const remoteState = await client.get(config.username);
console.log( if (!remoteState || remoteState === "") {
"[Sync] Remote state is empty, using local state instead.", await client.set(config.username, JSON.stringify(localState));
); console.log(
return; "[Sync] Remote state is empty, using local state instead.",
} else { );
if (!overwrite) { return;
} else {
const parsedRemoteState = JSON.parse( const parsedRemoteState = JSON.parse(
await client.get(config.username), await client.get(config.username),
) as AppState; ) as AppState;
mergeAppState(localState, parsedRemoteState); mergeAppState(localState, parsedRemoteState);
setLocalAppState(localState); 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(); this.markSyncTime();
}, },
async overwrite() { async download() {
await this.sync(true); 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() { async check() {