diff --git a/app/components/chat/LoadProblemButton.tsx b/app/components/chat/LoadProblemButton.tsx index a15e56726..7372d414b 100644 --- a/app/components/chat/LoadProblemButton.tsx +++ b/app/components/chat/LoadProblemButton.tsx @@ -12,6 +12,58 @@ interface LoadProblemButtonProps { importChat?: (description: string, messages: Message[]) => Promise; } +export async function loadProblem(problemId: string) { + let problem: BoltProblem | null = null; + try { + const rv = await sendCommandDedicatedClient({ + method: "Recording.globalExperimentalCommand", + params: { + name: "fetchBoltProblem", + params: { problemId }, + }, + }); + console.log("FetchProblemRval", rv); + problem = (rv as any).rval.problem; + } catch (error) { + console.error("Error fetching problem", error); + toast.error("Failed to fetch problem"); + } + + if (!problem) { + return; + } + + console.log("Problem", problem); + + const zip = new JSZip(); + await zip.loadAsync(problem.prompt.content, { base64: true }); + + const fileArtifacts: FileArtifact[] = []; + for (const [key, object] of Object.entries(zip.files)) { + if (object.dir) continue; + fileArtifacts.push({ + content: await object.async('text'), + path: key, + }); + } + + try { + const messages = await createChatFromFolder(fileArtifacts, [], "problem"); + + logStore.logSystem('Problem loaded successfully', { + problemId, + textFileCount: fileArtifacts.length, + }); + toast.success('Problem loaded successfully'); + + return messages; + } catch (error) { + logStore.logError('Failed to load problem', error); + console.error('Failed to load problem:', error); + toast.error('Failed to load problem'); + } +} + export const LoadProblemButton: React.FC = ({ className, importChat }) => { const [isLoading, setIsLoading] = useState(false); const [isInputOpen, setIsInputOpen] = useState(false); @@ -22,59 +74,13 @@ export const LoadProblemButton: React.FC = ({ className, const problemId = (document.getElementById('problem-input') as HTMLInputElement)?.value; - let problem: BoltProblem | null = null; - try { - const rv = await sendCommandDedicatedClient({ - method: "Recording.globalExperimentalCommand", - params: { - name: "fetchBoltProblem", - params: { problemId }, - }, - }); - console.log("FetchProblemRval", rv); - problem = (rv as any).rval.problem; - } catch (error) { - console.error("Error fetching problem", error); - toast.error("Failed to fetch problem"); - } + const messages = await loadProblem(problemId); - if (!problem) { - return; + if (importChat) { + await importChat("Imported problem", messages ?? []); } - console.log("Problem", problem); - - const zip = new JSZip(); - await zip.loadAsync(problem.prompt.content, { base64: true }); - - const fileArtifacts: FileArtifact[] = []; - for (const [key, object] of Object.entries(zip.files)) { - if (object.dir) continue; - fileArtifacts.push({ - content: await object.async('text'), - path: key, - }); - } - - try { - const messages = await createChatFromFolder(fileArtifacts, [], "problem"); - - if (importChat) { - await importChat("Imported problem", [...messages]); - } - - logStore.logSystem('Problem loaded successfully', { - problemId, - textFileCount: fileArtifacts.length, - }); - toast.success('Problem loaded successfully'); - } catch (error) { - logStore.logError('Failed to load problem', error); - console.error('Failed to load problem:', error); - toast.error('Failed to load problem'); - } finally { - setIsLoading(false); - } + setIsLoading(false); }; return ( diff --git a/app/lib/persistence/useChatHistory.ts b/app/lib/persistence/useChatHistory.ts index b5d31cfe1..dfaf9fa29 100644 --- a/app/lib/persistence/useChatHistory.ts +++ b/app/lib/persistence/useChatHistory.ts @@ -14,6 +14,7 @@ import { duplicateChat, createChatFromMessages, } from './db'; +import { loadProblem } from '~/components/chat/LoadProblemButton'; export interface ChatHistoryItem { id: string; @@ -32,7 +33,7 @@ export const description = atom(undefined); export function useChatHistory() { const navigate = useNavigate(); - const { id: mixedId } = useLoaderData<{ id?: string }>() ?? {}; + const { id: mixedId, problemId } = useLoaderData<{ id?: string, problemId?: string }>() ?? {}; const [searchParams] = useSearchParams(); const [initialMessages, setInitialMessages] = useState([]); @@ -75,11 +76,13 @@ export function useChatHistory() { logStore.logError('Failed to load chat messages', error); toast.error(error.message); }); + } else if (problemId) { + loadProblem(problemId).then(() => setReady(true)); } }, []); return { - ready: !mixedId || ready, + ready: ready || (!mixedId && !problemId), initialMessages, storeMessageHistory: async (messages: Message[]) => { if (!db || messages.length === 0) { diff --git a/app/routes/problem.$id.tsx b/app/routes/problem.$id.tsx new file mode 100644 index 000000000..e0ccf3d82 --- /dev/null +++ b/app/routes/problem.$id.tsx @@ -0,0 +1,8 @@ +import { json, type LoaderFunctionArgs } from '@remix-run/cloudflare'; +import { default as IndexRoute } from './_index'; + +export async function loader(args: LoaderFunctionArgs) { + return json({ problemId: args.params.id }); +} + +export default IndexRoute; diff --git a/app/routes/problems.tsx b/app/routes/problems.tsx index 7599d79d6..369071484 100644 --- a/app/routes/problems.tsx +++ b/app/routes/problems.tsx @@ -9,7 +9,7 @@ import { useEffect } from 'react'; import { useState } from 'react'; interface BoltProblemDescription { - id: string; + problemId: string; title: string; description: string; timestamp: number; @@ -87,20 +87,21 @@ function ProblemsPage() {
) : problems.length === 0 ? ( -
No problems found
+
No problems found
) : (
{problems.map((problem) => ( -

{problem.title}

-

{problem.description}

-

- Time: {problem.timestamp}ms +

{problem.description}

+

+ Time: {new Date(problem.timestamp).toLocaleString()}

-
+ ))}
)}