feat: Add automatic data synchronization settings and implementation, enabling auto-sync after completing replies or deleting conversations
This commit is contained in:
parent
621b1480c2
commit
eae593d660
|
@ -357,6 +357,21 @@ function SyncConfigModal(props: { onClose?: () => void }) {
|
|||
</select>
|
||||
</ListItem>
|
||||
|
||||
<ListItem
|
||||
title={Locale.Settings.Sync.Config.EnableAutoSync.Title}
|
||||
subTitle={Locale.Settings.Sync.Config.EnableAutoSync.SubTitle}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={syncStore.enableAutoSync}
|
||||
onChange={(e) => {
|
||||
syncStore.update(
|
||||
(config) => (config.enableAutoSync = e.currentTarget.checked),
|
||||
);
|
||||
}}
|
||||
></input>
|
||||
</ListItem>
|
||||
|
||||
<ListItem
|
||||
title={Locale.Settings.Sync.Config.Proxy.Title}
|
||||
subTitle={Locale.Settings.Sync.Config.Proxy.SubTitle}
|
||||
|
|
|
@ -206,6 +206,10 @@ const cn = {
|
|||
Title: "同步类型",
|
||||
SubTitle: "选择喜爱的同步服务器",
|
||||
},
|
||||
EnableAutoSync: {
|
||||
Title: "自动同步设置",
|
||||
SubTitle: "在回复完成或删除消息后自动同步数据",
|
||||
},
|
||||
Proxy: {
|
||||
Title: "启用代理",
|
||||
SubTitle: "在浏览器中同步时,必须启用代理以避免跨域限制",
|
||||
|
|
|
@ -209,6 +209,11 @@ const en: LocaleType = {
|
|||
Title: "Sync Type",
|
||||
SubTitle: "Choose your favorite sync service",
|
||||
},
|
||||
EnableAutoSync: {
|
||||
Title: "Auto Sync Settings",
|
||||
SubTitle:
|
||||
"Automatically synchronize data after replying or deleting messages",
|
||||
},
|
||||
Proxy: {
|
||||
Title: "Enable CORS Proxy",
|
||||
SubTitle: "Enable a proxy to avoid cross-origin restrictions",
|
||||
|
|
|
@ -30,6 +30,7 @@ import { nanoid } from "nanoid";
|
|||
import { createPersistStore } from "../utils/store";
|
||||
import { collectModelsWithDefaultModel } from "../utils/model";
|
||||
import { useAccessStore } from "./access";
|
||||
import { useSyncStore } from "./sync";
|
||||
import { isDalle3 } from "../utils";
|
||||
|
||||
export type ChatMessage = RequestMessage & {
|
||||
|
@ -168,6 +169,15 @@ function fillTemplateWith(input: string, modelConfig: ModelConfig) {
|
|||
return output;
|
||||
}
|
||||
|
||||
let cloudSyncTimer: any = null;
|
||||
function noticeCloudSync(): void {
|
||||
const syncStore = useSyncStore.getState();
|
||||
cloudSyncTimer && clearTimeout(cloudSyncTimer);
|
||||
cloudSyncTimer = setTimeout(() => {
|
||||
syncStore.autoSync();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
const DEFAULT_CHAT_STATE = {
|
||||
sessions: [createEmptySession()],
|
||||
currentSessionIndex: 0,
|
||||
|
@ -297,12 +307,15 @@ export const useChatStore = createPersistStore(
|
|||
deletedSessionIds,
|
||||
}));
|
||||
|
||||
noticeCloudSync();
|
||||
|
||||
showToast(
|
||||
Locale.Home.DeleteToast,
|
||||
{
|
||||
text: Locale.Home.Revert,
|
||||
onClick() {
|
||||
set(() => restoreState);
|
||||
noticeCloudSync();
|
||||
},
|
||||
},
|
||||
5000,
|
||||
|
@ -330,6 +343,7 @@ export const useChatStore = createPersistStore(
|
|||
});
|
||||
get().updateStat(message);
|
||||
get().summarizeSession();
|
||||
noticeCloudSync();
|
||||
},
|
||||
|
||||
async onUserInput(content: string, attachImages?: string[]) {
|
||||
|
|
|
@ -26,6 +26,7 @@ export type SyncStore = GetStoreState<typeof useSyncStore>;
|
|||
|
||||
const DEFAULT_SYNC_STATE = {
|
||||
provider: ProviderType.WebDAV,
|
||||
enableAutoSync: true,
|
||||
useProxy: true,
|
||||
proxyUrl: corsPath(ApiPath.Cors),
|
||||
|
||||
|
@ -91,6 +92,11 @@ export const useSyncStore = createPersistStore(
|
|||
},
|
||||
|
||||
async sync() {
|
||||
const enableAutoSync = get().enableAutoSync;
|
||||
if (!enableAutoSync) {
|
||||
return;
|
||||
}
|
||||
|
||||
const localState = getLocalAppState();
|
||||
const provider = get().provider;
|
||||
const config = get()[provider];
|
||||
|
@ -100,15 +106,17 @@ export const useSyncStore = createPersistStore(
|
|||
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
|
||||
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;
|
||||
|
@ -123,6 +131,14 @@ export const useSyncStore = createPersistStore(
|
|||
const client = this.getClient();
|
||||
return await client.check();
|
||||
},
|
||||
|
||||
async autoSync() {
|
||||
const { lastSyncTime, provider } = get();
|
||||
const syncStore = useSyncStore.getState();
|
||||
if (lastSyncTime && syncStore.cloudSync()) {
|
||||
syncStore.sync();
|
||||
}
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: StoreKey.Sync,
|
||||
|
|
|
@ -130,10 +130,10 @@ const MergeStates: StateMerger = {
|
|||
});
|
||||
|
||||
// sort local sessions with date field in desc order
|
||||
localState.sessions.sort(
|
||||
(a, b) =>
|
||||
new Date(b.lastUpdate).getTime() - new Date(a.lastUpdate).getTime(),
|
||||
);
|
||||
// localState.sessions.sort(
|
||||
// (a, b) =>
|
||||
// new Date(b.lastUpdate).getTime() - new Date(a.lastUpdate).getTime(),
|
||||
// );
|
||||
|
||||
const deletedSessionIds = {
|
||||
...remoteDeletedSessionIds,
|
||||
|
|
Loading…
Reference in New Issue