diff --git a/src/views/components/AddTaskModal.jsx b/src/views/components/AddTaskModal.jsx index ef9a402..75dabc7 100644 --- a/src/views/components/AddTaskModal.jsx +++ b/src/views/components/AddTaskModal.jsx @@ -60,7 +60,7 @@ const getDefaultNotification = () => { return defaultNotification } -const TaskInput = ({ onChoreUpdate, isModalOpen, onClose }) => { +const TaskInput = ({ onChoreUpdate, isModalOpen, onClose, initialMode }) => { const { ResponsiveModal } = useResponsiveModal() const isMobile = useMediaQuery(theme => theme.breakpoints.down('sm')) const pickerEmptyDisplay = isMobile ? 'icon' : 'icon-text' @@ -136,6 +136,29 @@ const TaskInput = ({ onChoreUpdate, isModalOpen, onClose }) => { voiceInputService.isSupported().then(setVoiceAvailable) }, []) + // Quick-capture widget entry points (donetick://chores/add?mode=voice|scan) + // land here: open straight into the requested panel, once per modal open so + // backing out of the panel doesn't bounce the user right back into it. + const appliedInitialModeRef = useRef(false) + useEffect(() => { + if (!isModalOpen) { + appliedInitialModeRef.current = false + return + } + if (appliedInitialModeRef.current) return + + if (initialMode === 'voice') { + // isSupported() resolves async — wait for the answer before deciding. + if (!voiceAvailable) return + appliedInitialModeRef.current = true + setShowVoice(true) + } else if (initialMode === 'scan') { + appliedInitialModeRef.current = true + setScanAutoCapture(true) + setShowScan(true) + } + }, [isModalOpen, initialMode, voiceAvailable]) + // Priority colors const priorityColors = { 0: TASK_COLOR.NO_PRIORITY, @@ -817,18 +840,21 @@ const TaskInput = ({ onChoreUpdate, isModalOpen, onClose }) => { /> )} - + {/* Sub-panels (voice/scan) own their own confirm action */} + {!showScan && !showVoice && ( + + )} } > @@ -1177,7 +1203,6 @@ const TaskInput = ({ onChoreUpdate, isModalOpen, onClose }) => { userLabels={userLabels || []} members={circleMembers?.res || []} userProfile={userProfile} - onClose={() => setShowVoice(false)} onUseSingle={handleVoiceSingle} onCreateMany={handleVoiceCreateMany} /> diff --git a/src/views/components/ScanToTask/ScanPanel.jsx b/src/views/components/ScanToTask/ScanPanel.jsx index 4732193..be7aa02 100644 --- a/src/views/components/ScanToTask/ScanPanel.jsx +++ b/src/views/components/ScanToTask/ScanPanel.jsx @@ -171,9 +171,6 @@ const ScanPanel = ({ open, onTaskExtracted, onClose, initialImageUrl, autoCaptur /> - {isNativeScanner ? ( - )} diff --git a/src/views/components/VoiceToTask/VoicePanel.jsx b/src/views/components/VoiceToTask/VoicePanel.jsx index 97601d2..7d6bae3 100644 --- a/src/views/components/VoiceToTask/VoicePanel.jsx +++ b/src/views/components/VoiceToTask/VoicePanel.jsx @@ -411,7 +411,6 @@ const VoicePanel = ({ userLabels = [], members = [], userProfile, - onClose, onUseSingle, onCreateMany, }) => { @@ -426,7 +425,6 @@ const VoicePanel = ({ removeSegment, updateSegment, patchSegment, - reset, isNative, } = useVoiceToTask({ members, userLabels }) const [creating, setCreating] = useState(false) @@ -463,11 +461,6 @@ const VoicePanel = ({ const isListening = phase === 'listening' const showActions = segments.length > 0 && !isListening && !creating - const handleCancel = () => { - reset() - onClose() - } - const mergedTask = segment => ({ ...parseVoiceTask(segment.text, parseCtx), ...(segment.overrides || {}), @@ -644,62 +637,52 @@ const VoicePanel = ({ level='body-xs' sx={{ opacity: 0.5, px: 2, textAlign: 'center' }} > - Pause or say “also” between tasks · say - “scratch that” to remove the last one + Pause between tasks · say “scratch that” to + remove the last one )} - {/* ── Footer ── */} - - - - {creating && ( + {creating ? ( + ) : segments.length === 1 ? ( + + ) : ( + )} - {showActions && - (segments.length === 1 ? ( - - ) : ( - - ))} - + )} ) }