Compare commits

..

4 Commits

Author SHA1 Message Date
lyf
ebe617b733 fix max_completions_tokens 2024-10-16 16:24:02 +08:00
Lloyd Zhou
c139038e01 Merge pull request #5639 from code-october/fix/auth-ui
优化访问码输入框
2024-10-11 19:11:35 +08:00
code-october
4a7fd3a380 优化首页 api 输入框 2024-10-11 10:36:11 +00:00
code-october
c98dc31cdf 优化访问码输入框 2024-10-11 09:03:20 +00:00
14 changed files with 53 additions and 261 deletions

View File

@@ -19,26 +19,26 @@ jobs:
with: with:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- -
name: Extract metadata (tags, labels) for Docker name: Extract metadata (tags, labels) for Docker
id: meta id: meta
uses: docker/metadata-action@v4 uses: docker/metadata-action@v4
with: with:
images: ${{ secrets.DOCKER_USERNAME }}/chatgpt-next-web images: yidadaa/chatgpt-next-web
tags: | tags: |
type=raw,value=latest type=raw,value=latest
type=ref,event=tag type=ref,event=tag
- -
name: Set up QEMU name: Set up QEMU
uses: docker/setup-qemu-action@v2 uses: docker/setup-qemu-action@v2
- -
name: Set up Docker Buildx name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v2
- -
name: Build and push Docker image name: Build and push Docker image
uses: docker/build-push-action@v4 uses: docker/build-push-action@v4
with: with:
@@ -49,4 +49,4 @@ jobs:
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha cache-from: type=gha
cache-to: type=gha,mode=max cache-to: type=gha,mode=max

View File

@@ -63,7 +63,7 @@ export interface RequestPayload {
presence_penalty: number; presence_penalty: number;
frequency_penalty: number; frequency_penalty: number;
top_p: number; top_p: number;
max_tokens?: number; max_completions_tokens?: number;
} }
export interface DalleRequestPayload { export interface DalleRequestPayload {
@@ -228,13 +228,16 @@ export class ChatGPTApi implements LLMApi {
presence_penalty: !isO1 ? modelConfig.presence_penalty : 0, presence_penalty: !isO1 ? modelConfig.presence_penalty : 0,
frequency_penalty: !isO1 ? modelConfig.frequency_penalty : 0, frequency_penalty: !isO1 ? modelConfig.frequency_penalty : 0,
top_p: !isO1 ? modelConfig.top_p : 1, top_p: !isO1 ? modelConfig.top_p : 1,
// max_tokens: Math.max(modelConfig.max_tokens, 1024), // max_completions_tokens: Math.max(modelConfig.max_completions_tokens, 1024),
// Please do not ask me why not send max_tokens, no reason, this param is just shit, I dont want to explain anymore. // Please do not ask me why not send max_completions_tokens, no reason, this param is just shit, I dont want to explain anymore.
}; };
// add max_tokens to vision model // add max_completions_tokens to vision model
if (visionModel) { if (visionModel) {
requestPayload["max_tokens"] = Math.max(modelConfig.max_tokens, 4000); requestPayload["max_completions_tokens"] = Math.max(
modelConfig.max_completions_tokens,
4000,
);
} }
} }

View File

@@ -35,7 +35,6 @@ export function useCommand(commands: Commands = {}) {
interface ChatCommands { interface ChatCommands {
new?: Command; new?: Command;
newm?: Command; newm?: Command;
copy?: Command;
next?: Command; next?: Command;
prev?: Command; prev?: Command;
clear?: Command; clear?: Command;

View File

@@ -11,6 +11,7 @@ import Logo from "../icons/logo.svg";
import { useMobileScreen } from "@/app/utils"; import { useMobileScreen } from "@/app/utils";
import BotIcon from "../icons/bot.svg"; import BotIcon from "../icons/bot.svg";
import { getClientConfig } from "../config/client"; import { getClientConfig } from "../config/client";
import { PasswordInput } from "./ui-lib";
import LeftIcon from "@/app/icons/left.svg"; import LeftIcon from "@/app/icons/left.svg";
import { safeLocalStorage } from "@/app/utils"; import { safeLocalStorage } from "@/app/utils";
import { import {
@@ -60,36 +61,43 @@ export function AuthPage() {
<div className={styles["auth-title"]}>{Locale.Auth.Title}</div> <div className={styles["auth-title"]}>{Locale.Auth.Title}</div>
<div className={styles["auth-tips"]}>{Locale.Auth.Tips}</div> <div className={styles["auth-tips"]}>{Locale.Auth.Tips}</div>
<input <PasswordInput
className={styles["auth-input"]} style={{ marginTop: "3vh", marginBottom: "3vh" }}
type="password" aria={Locale.Settings.ShowPassword}
placeholder={Locale.Auth.Input} aria-label={Locale.Auth.Input}
value={accessStore.accessCode} value={accessStore.accessCode}
type="text"
placeholder={Locale.Auth.Input}
onChange={(e) => { onChange={(e) => {
accessStore.update( accessStore.update(
(access) => (access.accessCode = e.currentTarget.value), (access) => (access.accessCode = e.currentTarget.value),
); );
}} }}
/> />
{!accessStore.hideUserApiKey ? ( {!accessStore.hideUserApiKey ? (
<> <>
<div className={styles["auth-tips"]}>{Locale.Auth.SubTips}</div> <div className={styles["auth-tips"]}>{Locale.Auth.SubTips}</div>
<input <PasswordInput
className={styles["auth-input"]} style={{ marginTop: "3vh", marginBottom: "3vh" }}
type="password" aria={Locale.Settings.ShowPassword}
placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder} aria-label={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
value={accessStore.openaiApiKey} value={accessStore.openaiApiKey}
type="text"
placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
onChange={(e) => { onChange={(e) => {
accessStore.update( accessStore.update(
(access) => (access.openaiApiKey = e.currentTarget.value), (access) => (access.openaiApiKey = e.currentTarget.value),
); );
}} }}
/> />
<input <PasswordInput
className={styles["auth-input-second"]} style={{ marginTop: "3vh", marginBottom: "3vh" }}
type="password" aria={Locale.Settings.ShowPassword}
placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder} aria-label={Locale.Settings.Access.Google.ApiKey.Placeholder}
value={accessStore.googleApiKey} value={accessStore.googleApiKey}
type="text"
placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
onChange={(e) => { onChange={(e) => {
accessStore.update( accessStore.update(
(access) => (access.googleApiKey = e.currentTarget.value), (access) => (access.googleApiKey = e.currentTarget.value),

View File

@@ -70,7 +70,6 @@ import {
getMessageImages, getMessageImages,
isVisionModel, isVisionModel,
isDalle3, isDalle3,
removeOutdatedEntries,
showPlugins, showPlugins,
safeLocalStorage, safeLocalStorage,
} from "../utils"; } from "../utils";
@@ -985,7 +984,6 @@ function _Chat() {
const chatCommands = useChatCommand({ const chatCommands = useChatCommand({
new: () => chatStore.newSession(), new: () => chatStore.newSession(),
newm: () => navigate(Path.NewChat), newm: () => navigate(Path.NewChat),
copy: () => chatStore.copySession(),
prev: () => chatStore.nextSession(-1), prev: () => chatStore.nextSession(-1),
next: () => chatStore.nextSession(1), next: () => chatStore.nextSession(1),
clear: () => clear: () =>
@@ -1117,20 +1115,10 @@ function _Chat() {
}; };
const deleteMessage = (msgId?: string) => { const deleteMessage = (msgId?: string) => {
chatStore.updateCurrentSession((session) => { chatStore.updateCurrentSession(
session.deletedMessageIds && (session) =>
removeOutdatedEntries(session.deletedMessageIds); (session.messages = session.messages.filter((m) => m.id !== msgId)),
session.messages = session.messages.filter((m) => { );
if (m.id !== msgId) {
return true;
}
if (!session.deletedMessageIds) {
session.deletedMessageIds = {} as Record<string, number>;
}
session.deletedMessageIds[m.id] = Date.now();
return false;
});
});
}; };
const onDelete = (msgId: string) => { const onDelete = (msgId: string) => {

View File

@@ -360,21 +360,6 @@ function SyncConfigModal(props: { onClose?: () => void }) {
</select> </select>
</ListItem> </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 <ListItem
title={Locale.Settings.Sync.Config.Proxy.Title} title={Locale.Settings.Sync.Config.Proxy.Title}
subTitle={Locale.Settings.Sync.Config.Proxy.SubTitle} subTitle={Locale.Settings.Sync.Config.Proxy.SubTitle}

View File

@@ -62,7 +62,6 @@ const cn = {
Commands: { Commands: {
new: "新建聊天", new: "新建聊天",
newm: "从面具新建聊天", newm: "从面具新建聊天",
copy: "复制当前聊天",
next: "下一个聊天", next: "下一个聊天",
prev: "上一个聊天", prev: "上一个聊天",
clear: "清除上下文", clear: "清除上下文",
@@ -233,10 +232,6 @@ const cn = {
Title: "同步类型", Title: "同步类型",
SubTitle: "选择喜爱的同步服务器", SubTitle: "选择喜爱的同步服务器",
}, },
EnableAutoSync: {
Title: "自动同步设置",
SubTitle: "在回复完成或删除消息后自动同步数据",
},
Proxy: { Proxy: {
Title: "启用代理", Title: "启用代理",
SubTitle: "在浏览器中同步时,必须启用代理以避免跨域限制", SubTitle: "在浏览器中同步时,必须启用代理以避免跨域限制",

View File

@@ -63,7 +63,6 @@ const en: LocaleType = {
Commands: { Commands: {
new: "Start a new chat", new: "Start a new chat",
newm: "Start a new chat with mask", newm: "Start a new chat with mask",
copy: "Copy the current Chat",
next: "Next Chat", next: "Next Chat",
prev: "Previous Chat", prev: "Previous Chat",
clear: "Clear Context", clear: "Clear Context",
@@ -235,11 +234,6 @@ const en: LocaleType = {
Title: "Sync Type", Title: "Sync Type",
SubTitle: "Choose your favorite sync service", SubTitle: "Choose your favorite sync service",
}, },
EnableAutoSync: {
Title: "Auto Sync Settings",
SubTitle:
"Automatically synchronize data after replying or deleting messages",
},
Proxy: { Proxy: {
Title: "Enable CORS Proxy", Title: "Enable CORS Proxy",
SubTitle: "Enable a proxy to avoid cross-origin restrictions", SubTitle: "Enable a proxy to avoid cross-origin restrictions",

View File

@@ -211,7 +211,7 @@ export const useAccessStore = createPersistStore(
}) })
.then((res: DangerConfig) => { .then((res: DangerConfig) => {
console.log("[Config] got config from server", res); console.log("[Config] got config from server", res);
set(() => ({ lastUpdateTime: Date.now(), ...res })); set(() => ({ ...res }));
}) })
.catch(() => { .catch(() => {
console.error("[Config] failed to fetch config"); console.error("[Config] failed to fetch config");

View File

@@ -1,8 +1,4 @@
import { import { getMessageTextContent, trimTopic } from "../utils";
getMessageTextContent,
trimTopic,
removeOutdatedEntries,
} from "../utils";
import { indexedDBStorage } from "@/app/utils/indexedDB-storage"; import { indexedDBStorage } from "@/app/utils/indexedDB-storage";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
@@ -33,7 +29,6 @@ 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();
@@ -85,7 +80,6 @@ export interface ChatSession {
lastUpdate: number; lastUpdate: number;
lastSummarizeIndex: number; lastSummarizeIndex: number;
clearContextIndex?: number; clearContextIndex?: number;
deletedMessageIds?: Record<string, number>;
mask: Mask; mask: Mask;
} }
@@ -109,7 +103,6 @@ function createEmptySession(): ChatSession {
}, },
lastUpdate: Date.now(), lastUpdate: Date.now(),
lastSummarizeIndex: 0, lastSummarizeIndex: 0,
deletedMessageIds: {},
mask: createEmptyMask(), mask: createEmptyMask(),
}; };
@@ -195,19 +188,9 @@ function fillTemplateWith(input: string, modelConfig: ModelConfig) {
return output; 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 = { const DEFAULT_CHAT_STATE = {
sessions: [createEmptySession()], sessions: [createEmptySession()],
currentSessionIndex: 0, currentSessionIndex: 0,
deletedSessionIds: {} as Record<string, number>,
lastInput: "", lastInput: "",
}; };
@@ -257,28 +240,6 @@ export const useChatStore = createPersistStore(
}); });
}, },
copySession() {
set((state) => {
const { sessions, currentSessionIndex } = state;
const emptySession = createEmptySession();
// copy the session
const curSession = JSON.parse(
JSON.stringify(sessions[currentSessionIndex]),
);
curSession.id = emptySession.id;
curSession.lastUpdate = emptySession.lastUpdate;
const newSessions = [...sessions];
newSessions.splice(0, 0, curSession);
return {
currentSessionIndex: 0,
sessions: newSessions,
};
});
},
moveSession(from: number, to: number) { moveSession(from: number, to: number) {
set((state) => { set((state) => {
const { sessions, currentSessionIndex: oldIndex } = state; const { sessions, currentSessionIndex: oldIndex } = state;
@@ -341,18 +302,7 @@ export const useChatStore = createPersistStore(
if (!deletedSession) return; if (!deletedSession) return;
const sessions = get().sessions.slice(); const sessions = get().sessions.slice();
const deletedSessionIds = { ...get().deletedSessionIds }; sessions.splice(index, 1);
removeOutdatedEntries(deletedSessionIds);
const hasDelSessions = sessions.splice(index, 1);
if (hasDelSessions?.length) {
hasDelSessions.forEach((session) => {
if (session.messages.length > 0) {
deletedSessionIds[session.id] = Date.now();
}
});
}
const currentIndex = get().currentSessionIndex; const currentIndex = get().currentSessionIndex;
let nextIndex = Math.min( let nextIndex = Math.min(
@@ -369,24 +319,19 @@ export const useChatStore = createPersistStore(
const restoreState = { const restoreState = {
currentSessionIndex: get().currentSessionIndex, currentSessionIndex: get().currentSessionIndex,
sessions: get().sessions.slice(), sessions: get().sessions.slice(),
deletedSessionIds: get().deletedSessionIds,
}; };
set(() => ({ set(() => ({
currentSessionIndex: nextIndex, currentSessionIndex: nextIndex,
sessions, sessions,
deletedSessionIds,
})); }));
noticeCloudSync();
showToast( showToast(
Locale.Home.DeleteToast, Locale.Home.DeleteToast,
{ {
text: Locale.Home.Revert, text: Locale.Home.Revert,
onClick() { onClick() {
set(() => restoreState); set(() => restoreState);
noticeCloudSync();
}, },
}, },
5000, 5000,
@@ -407,24 +352,6 @@ export const useChatStore = createPersistStore(
return session; return session;
}, },
sortSessions() {
const currentSession = get().currentSession();
const sessions = get().sessions.slice();
sessions.sort(
(a, b) =>
new Date(b.lastUpdate).getTime() - new Date(a.lastUpdate).getTime(),
);
const currentSessionIndex = sessions.findIndex((session) => {
return session && currentSession && session.id === currentSession.id;
});
set((state) => ({
currentSessionIndex,
sessions,
}));
},
onNewMessage(message: ChatMessage) { onNewMessage(message: ChatMessage) {
get().updateCurrentSession((session) => { get().updateCurrentSession((session) => {
session.messages = session.messages.concat(); session.messages = session.messages.concat();
@@ -432,8 +359,6 @@ export const useChatStore = createPersistStore(
}); });
get().updateStat(message); get().updateStat(message);
get().summarizeSession(); get().summarizeSession();
get().sortSessions();
noticeCloudSync();
}, },
async onUserInput(content: string, attachImages?: string[]) { async onUserInput(content: string, attachImages?: string[]) {

View File

@@ -65,7 +65,7 @@ export const DEFAULT_CONFIG = {
providerName: "OpenAI" as ServiceProvider, providerName: "OpenAI" as ServiceProvider,
temperature: 0.5, temperature: 0.5,
top_p: 1, top_p: 1,
max_tokens: 4000, max_completions_tokens: 4000,
presence_penalty: 0, presence_penalty: 0,
frequency_penalty: 0, frequency_penalty: 0,
sendMemory: true, sendMemory: true,
@@ -127,7 +127,7 @@ export const ModalConfigValidator = {
model(x: string) { model(x: string) {
return x as ModelType; return x as ModelType;
}, },
max_tokens(x: number) { max_completions_tokens(x: number) {
return limitNumber(x, 0, 512000, 1024); return limitNumber(x, 0, 512000, 1024);
}, },
presence_penalty(x: number) { presence_penalty(x: number) {

View File

@@ -24,7 +24,6 @@ export type SyncStore = GetStoreState<typeof useSyncStore>;
const DEFAULT_SYNC_STATE = { const DEFAULT_SYNC_STATE = {
provider: ProviderType.WebDAV, provider: ProviderType.WebDAV,
enableAutoSync: true,
useProxy: true, useProxy: true,
proxyUrl: ApiPath.Cors as string, proxyUrl: ApiPath.Cors as string,
@@ -44,8 +43,6 @@ const DEFAULT_SYNC_STATE = {
lastProvider: "", lastProvider: "",
}; };
let lastSyncTime = 0;
export const useSyncStore = createPersistStore( export const useSyncStore = createPersistStore(
DEFAULT_SYNC_STATE, DEFAULT_SYNC_STATE,
(set, get) => ({ (set, get) => ({
@@ -92,16 +89,6 @@ export const useSyncStore = createPersistStore(
}, },
async sync() { async sync() {
if (lastSyncTime && lastSyncTime >= Date.now() - 800) {
return;
}
lastSyncTime = Date.now();
const enableAutoSync = get().enableAutoSync;
if (!enableAutoSync) {
return;
}
const localState = getLocalAppState(); const localState = getLocalAppState();
const provider = get().provider; const provider = get().provider;
const config = get()[provider]; const config = get()[provider];
@@ -116,7 +103,9 @@ export const useSyncStore = createPersistStore(
); );
return; return;
} else { } else {
const parsedRemoteState = JSON.parse(remoteState) as AppState; const parsedRemoteState = JSON.parse(
await client.get(config.username),
) as AppState;
mergeAppState(localState, parsedRemoteState); mergeAppState(localState, parsedRemoteState);
setLocalAppState(localState); setLocalAppState(localState);
} }
@@ -134,14 +123,6 @@ export const useSyncStore = createPersistStore(
const client = this.getClient(); const client = this.getClient();
return await client.check(); return await client.check();
}, },
async autoSync() {
const { lastSyncTime, provider } = get();
const syncStore = useSyncStore.getState();
if (lastSyncTime && syncStore.cloudSync()) {
syncStore.sync();
}
},
}), }),
{ {
name: StoreKey.Sync, name: StoreKey.Sync,

View File

@@ -274,19 +274,6 @@ export function isDalle3(model: string) {
return "dall-e-3" === model; return "dall-e-3" === model;
} }
export function removeOutdatedEntries(
timeMap: Record<string, number>,
): Record<string, number> {
const oneMonthAgo = Date.now() - 30 * 24 * 60 * 60 * 1000;
// Delete data from a month ago
Object.keys(timeMap).forEach((id) => {
if (timeMap[id] < oneMonthAgo) {
delete timeMap[id];
}
});
return timeMap;
}
export function showPlugins(provider: ServiceProvider, model: string) { export function showPlugins(provider: ServiceProvider, model: string) {
if ( if (
provider == ServiceProvider.OpenAI || provider == ServiceProvider.OpenAI ||

View File

@@ -8,7 +8,6 @@ import { useMaskStore } from "../store/mask";
import { usePromptStore } from "../store/prompt"; import { usePromptStore } from "../store/prompt";
import { StoreKey } from "../constant"; import { StoreKey } from "../constant";
import { merge } from "./merge"; import { merge } from "./merge";
import { removeOutdatedEntries } from "@/app/utils";
type NonFunctionKeys<T> = { type NonFunctionKeys<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? never : K; [K in keyof T]: T[K] extends (...args: any[]) => any ? never : K;
@@ -66,10 +65,7 @@ type StateMerger = {
const MergeStates: StateMerger = { const MergeStates: StateMerger = {
[StoreKey.Chat]: (localState, remoteState) => { [StoreKey.Chat]: (localState, remoteState) => {
// merge sessions // merge sessions
const currentSession = useChatStore.getState().currentSession();
const localSessions: Record<string, ChatSession> = {}; const localSessions: Record<string, ChatSession> = {};
const localDeletedSessionIds = localState.deletedSessionIds || {};
localState.sessions.forEach((s) => (localSessions[s.id] = s)); localState.sessions.forEach((s) => (localSessions[s.id] = s));
remoteState.sessions.forEach((remoteSession) => { remoteState.sessions.forEach((remoteSession) => {
@@ -79,98 +75,29 @@ const MergeStates: StateMerger = {
const localSession = localSessions[remoteSession.id]; const localSession = localSessions[remoteSession.id];
if (!localSession) { if (!localSession) {
// if remote session is new, just merge it // if remote session is new, just merge it
if ( localState.sessions.push(remoteSession);
(localDeletedSessionIds[remoteSession.id] || -1) <
remoteSession.lastUpdate
) {
localState.sessions.push(remoteSession);
}
} else { } else {
// if both have the same session id, merge the messages // if both have the same session id, merge the messages
const localMessageIds = new Set(localSession.messages.map((v) => v.id)); const localMessageIds = new Set(localSession.messages.map((v) => v.id));
const localDeletedMessageIds = localSession.deletedMessageIds || {};
remoteSession.messages.forEach((m) => { remoteSession.messages.forEach((m) => {
if (!localMessageIds.has(m.id)) { if (!localMessageIds.has(m.id)) {
if ( localSession.messages.push(m);
!localDeletedMessageIds[m.id] ||
new Date(localDeletedMessageIds[m.id]).toLocaleString() < m.date
) {
localSession.messages.push(m);
}
} }
}); });
const remoteDeletedMessageIds = remoteSession.deletedMessageIds || {};
localSession.messages = localSession.messages.filter((localMessage) => {
return (
!remoteDeletedMessageIds[localMessage.id] ||
new Date(localDeletedMessageIds[localMessage.id]).toLocaleString() <
localMessage.date
);
});
// sort local messages with date field in asc order // sort local messages with date field in asc order
localSession.messages.sort( localSession.messages.sort(
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(), (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(),
); );
localSession.lastUpdate = Math.max(
remoteSession.lastUpdate,
localSession.lastUpdate,
);
const deletedMessageIds = {
...remoteDeletedMessageIds,
...localDeletedMessageIds,
};
removeOutdatedEntries(deletedMessageIds);
localSession.deletedMessageIds = deletedMessageIds;
} }
}); });
const remoteDeletedSessionIds = remoteState.deletedSessionIds || {};
const finalIds: Record<string, any> = {};
localState.sessions = localState.sessions.filter((localSession) => {
// 去除掉重复的会话
if (finalIds[localSession.id]) {
return false;
}
finalIds[localSession.id] = true;
// 去除掉非首个空会话,避免多个空会话在中间,不方便管理
if (
localSession.messages.length === 0 &&
localSession != localState.sessions[0]
) {
return false;
}
// 去除云端删除并且删除时间小于本地修改时间的会话
return (
(remoteDeletedSessionIds[localSession.id] || -1) <=
localSession.lastUpdate
);
});
// sort local sessions with date field in desc order // sort local sessions with date field in desc order
localState.sessions.sort( localState.sessions.sort(
(a, b) => (a, b) =>
new Date(b.lastUpdate).getTime() - new Date(a.lastUpdate).getTime(), new Date(b.lastUpdate).getTime() - new Date(a.lastUpdate).getTime(),
); );
const deletedSessionIds = {
...remoteDeletedSessionIds,
...localDeletedSessionIds,
};
removeOutdatedEntries(deletedSessionIds);
localState.deletedSessionIds = deletedSessionIds;
localState.currentSessionIndex = localState.sessions.findIndex(
(session) => {
return session && currentSession && session.id === currentSession.id;
},
);
return localState; return localState;
}, },
[StoreKey.Prompt]: (localState, remoteState) => { [StoreKey.Prompt]: (localState, remoteState) => {
@@ -226,9 +153,9 @@ export function mergeWithUpdate<T extends { lastUpdateTime?: number }>(
remoteState: T, remoteState: T,
) { ) {
const localUpdateTime = localState.lastUpdateTime ?? 0; const localUpdateTime = localState.lastUpdateTime ?? 0;
const remoteUpdateTime = remoteState.lastUpdateTime ?? 1; const remoteUpdateTime = localState.lastUpdateTime ?? 1;
if (localUpdateTime >= remoteUpdateTime) { if (localUpdateTime < remoteUpdateTime) {
merge(remoteState, localState); merge(remoteState, localState);
return { ...remoteState }; return { ...remoteState };
} else { } else {