Skip to content

Commit

Permalink
set up an event listener for outputs within useCell
Browse files Browse the repository at this point in the history
  • Loading branch information
rgbkrk committed Jan 15, 2024
1 parent 8b57149 commit b3f50f0
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 49 deletions.
5 changes: 4 additions & 1 deletion src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ impl AppState {

let finished_output = &output_handler.lock().await.output;

window.emit("output", finished_output.clone()).unwrap();
window.emit(
format!("cell-outputs-{}", cell_id).as_str(),
Some(finished_output.clone()),
).unwrap();
return true;
}
}
Expand Down
34 changes: 22 additions & 12 deletions src/components/Cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ 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, outputs } =
useCell(cellId);

let actionIcon = "▶";
let showExecutionCountAs = executionCount === null ? " " : executionCount;

switch (cellState) {
switch (executionState) {
case "idle":
// Let them try again
case "errored":
Expand All @@ -24,16 +25,25 @@ const Cell = ({ cellId }: { cellId: string }) => {
}

return (
<div className="bg-gray-100 p-4 rounded flex items-start">
<Button
variant="ghost"
className="font-mono text-sm group w-14 h-full pt-0 pb-0 m-0"
onClick={executeCell}
>
[<div className="group-hover:block hidden">{actionIcon}</div>
<span className="group-hover:hidden">{showExecutionCountAs}</span>]
</Button>
<Editor cellId={cellId} className="mr-2 pt-0 pb-0 text-sm" language="python"/>
<div>
<div className="bg-gray-100 p-4 rounded flex items-start">
<Button
variant="ghost"
className="font-mono text-sm group w-14 h-full pt-0 pb-0 m-0"
onClick={executeCell}
>
[<div className="group-hover:block hidden">{actionIcon}</div>
<span className="group-hover:hidden">{showExecutionCountAs}</span>]
</Button>
<Editor
cellId={cellId}
className="mr-2 pt-0 pb-0 text-sm"
language="python"
/>
</div>
{outputs && outputs.length > 0 ? (
<pre>{JSON.stringify(outputs, null, 2)}</pre>
) : null}
</div>
);
};
Expand Down
96 changes: 66 additions & 30 deletions src/hooks/useCell.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,82 @@
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<string>("");

// 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' } | {type: "setOutputs", outputs: []}

const [executionSubmitted, setExecutionSubmitted] = useState<boolean>(false);
type CellState = {
executionState: ExecutionState;
outputs: [];
}

let cellState: CellStates = "idle";
const initialState: CellState = {
executionState: ExecutionState.Idle,
outputs: []
};

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 };
case 'setOutputs':
return {...state, outputs: action.outputs }
default:
return state;
}
}

export function useCell(cellId: string) {
const [content, setContent] = useState<string>("");
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(() => {
const setupListener = async () => {
const unlisten = await listen(`cell-outputs-${cellId}`, (event) => {
// Q(Kyle): Do we need to check if the event.windowLabel matches ours
const outputs = event.payload as [];

// TODO(kyle): Listen for events from the backend to update the cell content
dispatch({type: "setOutputs", outputs})

});

return () => {
unlisten();
};
};

setupListener();
}, [cellId]);

return { content, executeCell, updateCell, cellState: cellState as CellStates, executionCount };
return { ...state, content, executeCell, updateCell, executionCount: null};
}
6 changes: 0 additions & 6 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<React.StrictMode>
Expand Down

0 comments on commit b3f50f0

Please sign in to comment.