import { type Result } from '@scrabble-solver/types'; import classNames from 'classnames'; import { type FunctionComponent, memo, type SyntheticEvent, useEffect, useMemo } from 'react'; import { useDispatch } from 'react-redux'; import { useAppLayout } from '@/app-layout'; import { useIsTouchDevice } from '@/hooks'; import { resultsSlice, selectAreResultsOutdated, selectProcessedResults, selectResultCandidate, selectSolveError, solveSlice, useTranslate, useTypedSelector, } from '@/state'; import { Alert } from '../Alert'; import { Board } from '../Board'; import { Dictionary } from '../Dictionary'; import { DictionaryInput } from '../DictionaryInput'; import { Rack } from '../Rack'; import { Results } from '../Results'; import { ResultCandidatePicker } from './components'; import styles from './Solver.module.scss'; interface Props { className?: string; onShowResults: () => void; } const SolverBase: FunctionComponent = ({ className, onShowResults }) => { const dispatch = useDispatch(); const translate = useTranslate(); const isTouchDevice = useIsTouchDevice(); const { maxControlsWidth, showCompactControls, tileSize } = useAppLayout(); const error = useTypedSelector(selectSolveError); const isOutdated = useTypedSelector(selectAreResultsOutdated); const resultCandidate = useTypedSelector(selectResultCandidate); const results = useTypedSelector(selectProcessedResults); const [bestResult] = results || []; const touchCallbacks = useMemo( () => ({ onClick: (result: Result) => { const isSelected = result === resultCandidate; if (isSelected) { dispatch(resultsSlice.actions.applyResult(result)); } else { dispatch(resultsSlice.actions.changeResultCandidate(result)); } }, }), [dispatch, resultCandidate], ); const mouseCallbacks = useMemo( () => ({ onBlur: () => { dispatch(resultsSlice.actions.changeResultCandidate(null)); }, onClick: (result: Result) => { dispatch(resultsSlice.actions.applyResult(result)); }, onFocus: (result: Result) => { dispatch(resultsSlice.actions.changeResultCandidate(result)); }, onMouseEnter: (result: Result) => { dispatch(resultsSlice.actions.changeResultCandidate(result)); }, onMouseLeave: () => { dispatch(resultsSlice.actions.changeResultCandidate(null)); }, }), [dispatch], ); const callbacks = isTouchDevice ? touchCallbacks : mouseCallbacks; const handleSubmit = (event: SyntheticEvent) => { event.preventDefault(); dispatch(solveSlice.actions.submit()); }; useEffect(() => { if (showCompactControls && bestResult && !isOutdated) { dispatch(resultsSlice.actions.changeResultCandidate(bestResult)); } }, [bestResult, dispatch, showCompactControls, isOutdated]); return (
{showCompactControls && (
{error && ( {error.message} )} {results && results.length === 0 && !isOutdated && ( {translate('results.empty-state.no-results')} )}
)}
); }; export const Solver = memo(SolverBase);