diff --git a/src/components/Cell.tsx b/src/components/Cell.tsx index 649eaac..7513e4b 100644 --- a/src/components/Cell.tsx +++ b/src/components/Cell.tsx @@ -4,12 +4,12 @@ import { useCell } from "@/hooks/useCell"; import { Editor } from "@/components//Editor"; const Cell = ({ cellId }: { cellId: string }) => { - const { executeCell, cellState, executionCount } = useCell(cellId); + const { executeCell, executionState, executionCount } = useCell(cellId); let actionIcon = "▶"; let showExecutionCountAs = executionCount === null ? " " : executionCount; - switch (cellState) { + switch (executionState) { case "idle": // Let them try again case "errored": diff --git a/src/hooks/useCell.ts b/src/hooks/useCell.ts index 396e00a..e29b8ab 100644 --- a/src/hooks/useCell.ts +++ b/src/hooks/useCell.ts @@ -1,46 +1,85 @@ -import { useState, useCallback } from "react"; +import { useState, useEffect, useCallback, useReducer } from "react"; import { invoke } from "@tauri-apps/api/tauri"; +import { listen } from '@tauri-apps/api/event' -type CellStates = "idle" | "queued" | "busy" | "errored" | "submitted"; - -export function useCell(cellId: string) { - const [content, setContent] = useState(""); - - // We also find out the execution count, whether the cell is queued, busy, or - // errored, and the output of the cell. We can use this to display the - // execution count, a spinner, or an error message. - - // Execution count is only set by the backend. - - const executionCount = null; // TODO: get from backend +enum ExecutionState { + Idle = "idle", + Queued = "queued", + Busy = "busy", + Errored = "errored", + Submitted = "submitted" +} - // Queued, busy, and errored are set by the backend. However, we have to - // have our own state for queued for before the backend has acknowledged the - // execution request. +type Action = { type: 'setSubmitted' } | { type: 'reset' }; - const [executionSubmitted, setExecutionSubmitted] = useState(false); +type CellState = { + executionState: ExecutionState; +} - let cellState: CellStates = "idle"; +const initialState: CellState = { + executionState: ExecutionState.Idle +}; - if (executionSubmitted) { - cellState = "submitted"; +function cellReducer(state: CellState, action: Action) { + switch (action.type) { + case 'setSubmitted': + return { ...state, executionState: ExecutionState.Submitted }; + case 'reset': + return { ...state, executionState: ExecutionState.Idle }; + // Handle other actions here + default: + return state; } +} +export function useCell(cellId: string) { + const [content, setContent] = useState(""); + const [state, dispatch] = useReducer(cellReducer, initialState); + const executeCell = useCallback(async () => { - setExecutionSubmitted(true); - await invoke("execute_cell", { cellId }); - setExecutionSubmitted(false); + dispatch({ type: 'setSubmitted' }); + try { + await invoke("execute_cell", { cellId }); + } catch (error) { + console.error(error); + // Handle error + } + dispatch({ type: 'reset' }); }, [cellId]); - const updateCell = useCallback( - async (newContent: string) => { + const updateCell = useCallback(async (newContent: string) => { + try { await invoke("update_cell", { cellId, newContent }); setContent(newContent); - }, - [cellId] - ); + } catch (error) { + console.error(error); + // Handle error + } + }, [cellId]); + + useEffect(() => { + let isMounted = true; - // TODO(kyle): Listen for events from the backend to update the cell content + const setupListener = async () => { + const unlisten = await listen(`cell-${cellId}`, (event) => { + if (isMounted) { + console.log(event); + // Update state based on event here + } + }); + + return () => { + isMounted = false; + unlisten(); + }; + }; + + setupListener(); + + return () => { + isMounted = false; + }; + }, [cellId]); - return { content, executeCell, updateCell, cellState: cellState as CellStates, executionCount }; + return { ...state, content, executeCell, updateCell, executionCount: null }; } diff --git a/src/main.tsx b/src/main.tsx index 374b3b8..e83bf95 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,13 +2,7 @@ import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App"; import "./index.css"; -import { emit, listen } from '@tauri-apps/api/event' -// listen to the `click` event and get a function to remove the event listener -// there's also a `once` function that subscribes to an event and automatically unsubscribes the listener on the first event -const unlisten = await listen('output', (event) => { - console.log(event) -}) ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(