Skip to content

Commit e553d52

Browse files
committed
add storage support
1 parent f379cac commit e553d52

File tree

2 files changed

+175
-105
lines changed

2 files changed

+175
-105
lines changed

client/packages/lowcoder/src/comps/comps/chatComp/components/ChatWithThreads.tsx

Lines changed: 36 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,27 @@ import { useThreadContext, MyMessage, ThreadProvider } from "./context/ThreadCon
1010
import { Thread } from "./assistant-ui/thread";
1111
import { ThreadList } from "./assistant-ui/thread-list";
1212
import { chatStorage, ThreadData as StoredThreadData } from "../utils/chatStorage";
13+
import { useChatStorage } from "../hooks/useChatStorage";
14+
import styled from "styled-components";
15+
16+
17+
18+
19+
const ChatContainer = styled.div`
20+
display: flex;
21+
height: 500px;
22+
23+
.aui-thread-list-root {
24+
width: 250px;
25+
background-color: #333;
26+
}
27+
28+
.aui-thread-root {
29+
flex: 1;
30+
background-color: #f0f0f0;
31+
}
32+
33+
`;
1334

1435
// Define thread data interfaces to match ExternalStoreThreadData requirements
1536
interface RegularThreadData {
@@ -45,109 +66,13 @@ function ChatWithThreads() {
4566
const [threadList, setThreadList] = useState<ThreadData[]>([
4667
{ threadId: "default", status: "regular", title: "New Chat" } as RegularThreadData,
4768
]);
48-
const [isInitialized, setIsInitialized] = useState(false);
49-
50-
// Load data from persistent storage on component mount
51-
useEffect(() => {
52-
const loadData = async () => {
53-
try {
54-
await chatStorage.initialize();
55-
56-
// Load all threads from storage
57-
const storedThreads = await chatStorage.getAllThreads();
58-
if (storedThreads.length > 0) {
59-
// Convert stored threads to UI format
60-
const uiThreads: ThreadData[] = storedThreads.map(stored => ({
61-
threadId: stored.threadId,
62-
status: stored.status as "regular" | "archived",
63-
title: stored.title,
64-
}));
65-
setThreadList(uiThreads);
66-
67-
// Load messages for each thread
68-
const threadMessages = new Map<string, MyMessage[]>();
69-
for (const thread of storedThreads) {
70-
const messages = await chatStorage.getMessages(thread.threadId);
71-
threadMessages.set(thread.threadId, messages);
72-
}
73-
74-
// Ensure default thread exists
75-
if (!threadMessages.has("default")) {
76-
threadMessages.set("default", []);
77-
}
78-
79-
setThreads(threadMessages);
80-
81-
// Set current thread to the most recently updated one
82-
const latestThread = storedThreads.sort((a, b) => b.updatedAt - a.updatedAt)[0];
83-
if (latestThread) {
84-
setCurrentThreadId(latestThread.threadId);
85-
}
86-
} else {
87-
// Initialize with default thread
88-
const defaultThread: StoredThreadData = {
89-
threadId: "default",
90-
status: "regular",
91-
title: "New Chat",
92-
createdAt: Date.now(),
93-
updatedAt: Date.now(),
94-
};
95-
await chatStorage.saveThread(defaultThread);
96-
}
97-
98-
setIsInitialized(true);
99-
} catch (error) {
100-
console.error("Failed to load chat data:", error);
101-
setIsInitialized(true); // Continue with default state
102-
}
103-
};
104-
105-
loadData();
106-
}, [setCurrentThreadId, setThreads]);
107-
108-
// Save thread data whenever threadList changes
109-
useEffect(() => {
110-
if (!isInitialized) return;
111-
112-
const saveThreads = async () => {
113-
try {
114-
for (const thread of threadList) {
115-
const storedThread: StoredThreadData = {
116-
threadId: thread.threadId,
117-
status: thread.status,
118-
title: thread.title,
119-
createdAt: Date.now(), // In real app, preserve original createdAt
120-
updatedAt: Date.now(),
121-
};
122-
await chatStorage.saveThread(storedThread);
123-
}
124-
} catch (error) {
125-
console.error("Failed to save threads:", error);
126-
}
127-
};
128-
129-
saveThreads();
130-
}, [threadList, isInitialized]);
131-
132-
// Save messages whenever threads change
133-
useEffect(() => {
134-
if (!isInitialized) return;
135-
136-
const saveMessages = async () => {
137-
try {
138-
for (const [threadId, messages] of threads.entries()) {
139-
await chatStorage.saveMessages(messages, threadId);
140-
}
141-
} catch (error) {
142-
console.error("Failed to save messages:", error);
143-
}
144-
};
145-
146-
saveMessages();
147-
}, [threads, isInitialized]);
148-
149-
150-
69+
const { isInitialized } = useChatStorage({
70+
threadList,
71+
threads,
72+
setThreadList,
73+
setThreads,
74+
setCurrentThreadId,
75+
});
15176
// Get messages for current thread
15277
const currentMessages = threads.get(currentThreadId) || [];
15378

@@ -349,10 +274,16 @@ function ChatWithThreads() {
349274
},
350275
});
351276

277+
if (!isInitialized) {
278+
return <div>Loading...</div>;
279+
}
280+
352281
return (
353282
<AssistantRuntimeProvider runtime={runtime}>
354-
<ThreadList />
355-
<Thread />
283+
<ChatContainer>
284+
<ThreadList />
285+
<Thread />
286+
</ChatContainer>
356287
</AssistantRuntimeProvider>
357288
);
358289
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import { useEffect, useState } from "react";
2+
import { chatStorage, ThreadData as StoredThreadData } from "../utils/chatStorage";
3+
import { MyMessage } from "../components/context/ThreadContext";
4+
5+
// Thread data interfaces (matching ChatWithThreads)
6+
interface RegularThreadData {
7+
threadId: string;
8+
status: "regular";
9+
title: string;
10+
}
11+
12+
interface ArchivedThreadData {
13+
threadId: string;
14+
status: "archived";
15+
title: string;
16+
}
17+
18+
type ThreadData = RegularThreadData | ArchivedThreadData;
19+
20+
interface UseChatStorageParams {
21+
threadList: ThreadData[];
22+
threads: Map<string, MyMessage[]>;
23+
setThreadList: React.Dispatch<React.SetStateAction<ThreadData[]>>;
24+
setThreads: React.Dispatch<React.SetStateAction<Map<string, MyMessage[]>>>;
25+
setCurrentThreadId: (id: string) => void;
26+
}
27+
28+
export function useChatStorage({
29+
threadList,
30+
threads,
31+
setThreadList,
32+
setThreads,
33+
setCurrentThreadId,
34+
}: UseChatStorageParams) {
35+
const [isInitialized, setIsInitialized] = useState(false);
36+
37+
// Load data from persistent storage on component mount
38+
useEffect(() => {
39+
const loadData = async () => {
40+
try {
41+
await chatStorage.initialize();
42+
43+
// Load all threads from storage
44+
const storedThreads = await chatStorage.getAllThreads();
45+
if (storedThreads.length > 0) {
46+
// Convert stored threads to UI format
47+
const uiThreads: ThreadData[] = storedThreads.map(stored => ({
48+
threadId: stored.threadId,
49+
status: stored.status as "regular" | "archived",
50+
title: stored.title,
51+
}));
52+
setThreadList(uiThreads);
53+
54+
// Load messages for each thread
55+
const threadMessages = new Map<string, MyMessage[]>();
56+
for (const thread of storedThreads) {
57+
const messages = await chatStorage.getMessages(thread.threadId);
58+
threadMessages.set(thread.threadId, messages);
59+
}
60+
61+
// Ensure default thread exists
62+
if (!threadMessages.has("default")) {
63+
threadMessages.set("default", []);
64+
}
65+
66+
setThreads(threadMessages);
67+
68+
// Set current thread to the most recently updated one
69+
const latestThread = storedThreads.sort((a, b) => b.updatedAt - a.updatedAt)[0];
70+
if (latestThread) {
71+
setCurrentThreadId(latestThread.threadId);
72+
}
73+
} else {
74+
// Initialize with default thread
75+
const defaultThread: StoredThreadData = {
76+
threadId: "default",
77+
status: "regular",
78+
title: "New Chat",
79+
createdAt: Date.now(),
80+
updatedAt: Date.now(),
81+
};
82+
await chatStorage.saveThread(defaultThread);
83+
}
84+
85+
setIsInitialized(true);
86+
} catch (error) {
87+
console.error("Failed to load chat data:", error);
88+
setIsInitialized(true); // Continue with default state
89+
}
90+
};
91+
92+
loadData();
93+
}, [setCurrentThreadId, setThreads, setThreadList]);
94+
95+
// Save thread data whenever threadList changes
96+
useEffect(() => {
97+
if (!isInitialized) return;
98+
99+
const saveThreads = async () => {
100+
try {
101+
for (const thread of threadList) {
102+
const storedThread: StoredThreadData = {
103+
threadId: thread.threadId,
104+
status: thread.status,
105+
title: thread.title,
106+
createdAt: Date.now(), // In real app, preserve original createdAt
107+
updatedAt: Date.now(),
108+
};
109+
await chatStorage.saveThread(storedThread);
110+
}
111+
} catch (error) {
112+
console.error("Failed to save threads:", error);
113+
}
114+
};
115+
116+
saveThreads();
117+
}, [threadList, isInitialized]);
118+
119+
// Save messages whenever threads change
120+
useEffect(() => {
121+
if (!isInitialized) return;
122+
123+
const saveMessages = async () => {
124+
try {
125+
for (const [threadId, messages] of threads.entries()) {
126+
await chatStorage.saveMessages(messages, threadId);
127+
}
128+
} catch (error) {
129+
console.error("Failed to save messages:", error);
130+
}
131+
};
132+
133+
saveMessages();
134+
}, [threads, isInitialized]);
135+
136+
return {
137+
isInitialized,
138+
};
139+
}

0 commit comments

Comments
 (0)