diff --git a/web/src/App.tsx b/web/src/App.tsx index 0a7fd503..874fa906 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,8 +1,8 @@ import { type KeyboardEvent, type ReactNode, useCallback, useEffect, useRef, useState, useTransition } from 'react'; -import { fend } from './lib/fend'; import { useCurrentInput } from './hooks/useCurrentInput'; import NewTabLink from './components/NewTabLink'; import PendingOutput from './components/PendingOutput'; +import { useFend } from './hooks/useFend'; const examples = ` > 5'10" to cm @@ -36,8 +36,8 @@ const exampleContent = ( export default function App({ widget = false }: { widget?: boolean }) { const [output, setOutput] = useState(widget ? <> : exampleContent); - const { currentInput, submit, onInput, upArrow, downArrow } = useCurrentInput(); - const [variables, setVariables] = useState(''); + const { evaluate, evaluateHint } = useFend(); + const { currentInput, submit, onInput, upArrow, downArrow, hint } = useCurrentInput(evaluateHint); const inputText = useRef(null); const pendingOutput = useRef(null); const focus = useCallback(() => { @@ -86,16 +86,12 @@ export default function App({ widget = false }: { widget?: boolean }) { startTransition(async () => { const request =

{`> ${currentInput}`}

; submit(); - const fendResult = await fend(currentInput, 1000000000, variables); + const fendResult = await evaluate(currentInput); if (!fendResult.ok && fendResult.message === 'cancelled') { return; } onInput(''); - console.log(fendResult); const result =

{fendResult.ok ? fendResult.result : fendResult.message}

; - if (fendResult.ok && fendResult.variables.length > 0) { - setVariables(fendResult.variables); - } setOutput(o => ( <> {o} @@ -106,7 +102,7 @@ export default function App({ widget = false }: { widget?: boolean }) { pendingOutput.current?.scrollIntoView({ behavior: 'smooth' }); }); }, - [currentInput, submit, variables, onInput, downArrow, upArrow], + [currentInput, submit, onInput, downArrow, upArrow, evaluate], ); useEffect(() => { document.addEventListener('click', focus); @@ -139,7 +135,7 @@ export default function App({ widget = false }: { widget?: boolean }) { autoFocus /> - + ); diff --git a/web/src/components/PendingOutput.tsx b/web/src/components/PendingOutput.tsx index 2f2de24e..72814dbf 100644 --- a/web/src/components/PendingOutput.tsx +++ b/web/src/components/PendingOutput.tsx @@ -1,27 +1,13 @@ -import { startTransition, useEffect, useState, type Ref } from 'react'; +import type { Ref } from 'react'; import { ThreeDotsScale } from 'react-svg-spinners'; -import { fend } from '../lib/fend'; type Props = { ref: Ref; - currentInput: string; - variables: string; + hint: string; isPending: boolean; }; -export default function PendingOutput({ ref, currentInput, variables, isPending }: Props) { - const [hint, setHint] = useState(''); - useEffect(() => { - startTransition(async () => { - const result = await fend(currentInput, 100, variables); - if (!result.ok) { - setHint(''); - } else { - setHint(result.result); - } - }); - }, [currentInput, variables]); - +export default function PendingOutput({ ref, hint, isPending }: Props) { return (

{hint || (isPending ? : <> )} diff --git a/web/src/hooks/useCurrentInput.ts b/web/src/hooks/useCurrentInput.ts index 7fcda4e1..902c09b4 100644 --- a/web/src/hooks/useCurrentInput.ts +++ b/web/src/hooks/useCurrentInput.ts @@ -1,10 +1,21 @@ -import { FormEvent, useCallback, useState } from 'react'; +import { FormEvent, startTransition, useCallback, useState } from 'react'; import { useHistory } from './useHistory'; -export function useCurrentInput() { +export function useCurrentInput(evaluateHint: (input: string) => Promise) { const { history, addToHistory } = useHistory(); - const [currentInput, setCurrentInput] = useState(''); + const [currentInput, setCurrentInputInternal] = useState(''); const [navigation, setNavigation] = useState(0); + const [hint, setHint] = useState(''); + + const setCurrentInput = useCallback( + (value: string) => { + setCurrentInputInternal(value); + startTransition(async () => { + setHint(await evaluateHint(value)); + }); + }, + [evaluateHint], + ); const navigate = useCallback( (direction: 'up' | 'down') => { @@ -27,13 +38,16 @@ export function useCurrentInput() { return newValue; }); }, - [history], + [history, setCurrentInput], ); - const onInput = useCallback((e: string | FormEvent) => { - setNavigation(0); - setCurrentInput(typeof e === 'string' ? e : e.currentTarget.value); - }, []); + const onInput = useCallback( + (e: string | FormEvent) => { + setNavigation(0); + setCurrentInput(typeof e === 'string' ? e : e.currentTarget.value); + }, + [setCurrentInput], + ); const upArrow = useCallback(() => { if (currentInput.trim().length !== 0 && navigation === 0) { @@ -52,5 +66,5 @@ export function useCurrentInput() { setNavigation(0); }, [currentInput, addToHistory]); - return { currentInput, submit, onInput, downArrow, upArrow }; + return { currentInput, submit, onInput, downArrow, upArrow, hint }; } diff --git a/web/src/hooks/useFend.ts b/web/src/hooks/useFend.ts new file mode 100644 index 00000000..37b27d85 --- /dev/null +++ b/web/src/hooks/useFend.ts @@ -0,0 +1,32 @@ +import { useCallback, useState } from 'react'; +import { fend } from '../lib/fend'; + +export function useFend() { + const [variables, setVariables] = useState(''); + + const evaluate = useCallback( + async (input: string) => { + const result = await fend(input, 1000000000, variables); + console.log(result); + if (result.ok && result.variables.length > 0) { + setVariables(result.variables); + } + return result; + }, + [variables], + ); + + const evaluateHint = useCallback( + async (input: string) => { + const result = await fend(input, 100, variables); + if (!result.ok) { + return ''; + } else { + return result.result; + } + }, + [variables], + ); + + return { evaluate, evaluateHint }; +}