Skip to content

Commit

Permalink
feat: The cloud synchronization feature is enhanced to support the sy…
Browse files Browse the repository at this point in the history
…nchronization of deleted conversations and deleted messages
  • Loading branch information
李超 committed Aug 3, 2024
1 parent 5065091 commit 22c7959
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 8 deletions.
19 changes: 15 additions & 4 deletions app/components/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
getMessageTextContent,
getMessageImages,
isVisionModel,
removeOutdatedEntries,
} from "../utils";

import { uploadImage as uploadImageRemote } from "@/app/utils/chat";
Expand Down Expand Up @@ -923,10 +924,20 @@ function _Chat() {
};

const deleteMessage = (msgId?: string) => {
chatStore.updateCurrentSession(
(session) =>
(session.messages = session.messages.filter((m) => m.id !== msgId)),
);
chatStore.updateCurrentSession((session) => {
session.deletedMessageIds &&
removeOutdatedEntries(session.deletedMessageIds);
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) => {
Expand Down
24 changes: 22 additions & 2 deletions app/store/chat.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { trimTopic, getMessageTextContent } from "../utils";
import {
trimTopic,
getMessageTextContent,
removeOutdatedEntries,
} from "../utils";

import Locale, { getLang } from "../locales";
import { showToast } from "../components/ui-lib";
Expand Down Expand Up @@ -61,6 +65,7 @@ export interface ChatSession {
lastUpdate: number;
lastSummarizeIndex: number;
clearContextIndex?: number;
deletedMessageIds?: Record<string, number>;

mask: Mask;
}
Expand All @@ -84,6 +89,7 @@ function createEmptySession(): ChatSession {
},
lastUpdate: Date.now(),
lastSummarizeIndex: 0,
deletedMessageIds: {},

mask: createEmptyMask(),
};
Expand Down Expand Up @@ -164,6 +170,7 @@ function fillTemplateWith(input: string, modelConfig: ModelConfig) {
const DEFAULT_CHAT_STATE = {
sessions: [createEmptySession()],
currentSessionIndex: 0,
deletedSessionIds: {} as Record<string, number>,
};

export const useChatStore = createPersistStore(
Expand Down Expand Up @@ -252,7 +259,18 @@ export const useChatStore = createPersistStore(
if (!deletedSession) return;

const sessions = get().sessions.slice();
sessions.splice(index, 1);
const deletedSessionIds = { ...get().deletedSessionIds };

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;
let nextIndex = Math.min(
Expand All @@ -269,11 +287,13 @@ export const useChatStore = createPersistStore(
const restoreState = {
currentSessionIndex: get().currentSessionIndex,
sessions: get().sessions.slice(),
deletedSessionIds: get().deletedSessionIds,
};

set(() => ({
currentSessionIndex: nextIndex,
sessions,
deletedSessionIds,
}));

showToast(
Expand Down
13 changes: 13 additions & 0 deletions app/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,16 @@ export function isVisionModel(model: string) {
visionKeywords.some((keyword) => model.includes(keyword)) || isGpt4Turbo
);
}

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;
}
48 changes: 46 additions & 2 deletions app/utils/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useMaskStore } from "../store/mask";
import { usePromptStore } from "../store/prompt";
import { StoreKey } from "../constant";
import { merge } from "./merge";
import { removeOutdatedEntries } from "@/app/utils";

type NonFunctionKeys<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? never : K;
Expand Down Expand Up @@ -66,6 +67,7 @@ const MergeStates: StateMerger = {
[StoreKey.Chat]: (localState, remoteState) => {
// merge sessions
const localSessions: Record<string, ChatSession> = {};
const localDeletedSessionIds = localState.deletedSessionIds || {};
localState.sessions.forEach((s) => (localSessions[s.id] = s));

remoteState.sessions.forEach((remoteSession) => {
Expand All @@ -75,29 +77,71 @@ const MergeStates: StateMerger = {
const localSession = localSessions[remoteSession.id];
if (!localSession) {
// if remote session is new, just merge it
localState.sessions.push(remoteSession);
if (
(localDeletedSessionIds[remoteSession.id] || -1) <
remoteSession.lastUpdate
) {
localState.sessions.push(remoteSession);
}
} else {
// if both have the same session id, merge the messages
const localMessageIds = new Set(localSession.messages.map((v) => v.id));
const localDeletedMessageIds = localSession.deletedMessageIds || {};
remoteSession.messages.forEach((m) => {
if (!localMessageIds.has(m.id)) {
localSession.messages.push(m);
if (
!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
localSession.messages.sort(
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(),
);

const deletedMessageIds = {
...remoteDeletedMessageIds,
...localDeletedMessageIds,
};
removeOutdatedEntries(deletedMessageIds);
localSession.deletedMessageIds = deletedMessageIds;
}
});

const remoteDeletedSessionIds = remoteState.deletedSessionIds || {};
localState.sessions = localState.sessions.filter((localSession) => {
return (
(remoteDeletedSessionIds[localSession.id] || -1) <=
localSession.lastUpdate
);
});

// sort local sessions with date field in desc order
localState.sessions.sort(
(a, b) =>
new Date(b.lastUpdate).getTime() - new Date(a.lastUpdate).getTime(),
);

const deletedSessionIds = {
...remoteDeletedSessionIds,
...localDeletedSessionIds,
};
removeOutdatedEntries(deletedSessionIds);
localState.deletedSessionIds = deletedSessionIds;

return localState;
},
[StoreKey.Prompt]: (localState, remoteState) => {
Expand Down

0 comments on commit 22c7959

Please sign in to comment.