Move cancel button to the modal

This commit is contained in:
Mo Tarbin
2026-07-28 02:02:24 -04:00
parent ea89f90edb
commit ce49e4afc6
3 changed files with 75 additions and 73 deletions

View File

@@ -60,7 +60,7 @@ const getDefaultNotification = () => {
return defaultNotification return defaultNotification
} }
const TaskInput = ({ onChoreUpdate, isModalOpen, onClose }) => { const TaskInput = ({ onChoreUpdate, isModalOpen, onClose, initialMode }) => {
const { ResponsiveModal } = useResponsiveModal() const { ResponsiveModal } = useResponsiveModal()
const isMobile = useMediaQuery(theme => theme.breakpoints.down('sm')) const isMobile = useMediaQuery(theme => theme.breakpoints.down('sm'))
const pickerEmptyDisplay = isMobile ? 'icon' : 'icon-text' const pickerEmptyDisplay = isMobile ? 'icon' : 'icon-text'
@@ -136,6 +136,29 @@ const TaskInput = ({ onChoreUpdate, isModalOpen, onClose }) => {
voiceInputService.isSupported().then(setVoiceAvailable) 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 // Priority colors
const priorityColors = { const priorityColors = {
0: TASK_COLOR.NO_PRIORITY, 0: TASK_COLOR.NO_PRIORITY,
@@ -817,18 +840,21 @@ const TaskInput = ({ onChoreUpdate, isModalOpen, onClose }) => {
/> />
)} )}
</Button> </Button>
<Button {/* Sub-panels (voice/scan) own their own confirm action */}
size='lg' {!showScan && !showVoice && (
variant='solid' <Button
color='primary' size='lg'
disabled={!taskTitle.trim()} variant='solid'
onClick={createChore} color='primary'
> disabled={!taskTitle.trim()}
Create onClick={createChore}
{showKeyboardShortcuts && ( >
<KeyboardShortcutHint shortcut='Enter' sx={{ ml: 1 }} /> Create
)} {showKeyboardShortcuts && (
</Button> <KeyboardShortcutHint shortcut='Enter' sx={{ ml: 1 }} />
)}
</Button>
)}
</Box> </Box>
} }
> >
@@ -1177,7 +1203,6 @@ const TaskInput = ({ onChoreUpdate, isModalOpen, onClose }) => {
userLabels={userLabels || []} userLabels={userLabels || []}
members={circleMembers?.res || []} members={circleMembers?.res || []}
userProfile={userProfile} userProfile={userProfile}
onClose={() => setShowVoice(false)}
onUseSingle={handleVoiceSingle} onUseSingle={handleVoiceSingle}
onCreateMany={handleVoiceCreateMany} onCreateMany={handleVoiceCreateMany}
/> />

View File

@@ -171,9 +171,6 @@ const ScanPanel = ({ open, onTaskExtracted, onClose, initialImageUrl, autoCaptur
/> />
<Box sx={{ ml: 'auto', display: 'flex', gap: 1 }}> <Box sx={{ ml: 'auto', display: 'flex', gap: 1 }}>
<Button size='sm' variant='plain' color='neutral' onClick={onClose}>
Cancel
</Button>
{isNativeScanner ? ( {isNativeScanner ? (
<Button <Button
size='sm' size='sm'
@@ -297,9 +294,6 @@ const ScanPanel = ({ open, onTaskExtracted, onClose, initialImageUrl, autoCaptur
> >
Retake Retake
</Button> </Button>
<Button size='sm' variant='plain' color='neutral' onClick={onClose}>
Cancel
</Button>
</Box> </Box>
</Box> </Box>
)} )}

View File

@@ -411,7 +411,6 @@ const VoicePanel = ({
userLabels = [], userLabels = [],
members = [], members = [],
userProfile, userProfile,
onClose,
onUseSingle, onUseSingle,
onCreateMany, onCreateMany,
}) => { }) => {
@@ -426,7 +425,6 @@ const VoicePanel = ({
removeSegment, removeSegment,
updateSegment, updateSegment,
patchSegment, patchSegment,
reset,
isNative, isNative,
} = useVoiceToTask({ members, userLabels }) } = useVoiceToTask({ members, userLabels })
const [creating, setCreating] = useState(false) const [creating, setCreating] = useState(false)
@@ -463,11 +461,6 @@ const VoicePanel = ({
const isListening = phase === 'listening' const isListening = phase === 'listening'
const showActions = segments.length > 0 && !isListening && !creating const showActions = segments.length > 0 && !isListening && !creating
const handleCancel = () => {
reset()
onClose()
}
const mergedTask = segment => ({ const mergedTask = segment => ({
...parseVoiceTask(segment.text, parseCtx), ...parseVoiceTask(segment.text, parseCtx),
...(segment.overrides || {}), ...(segment.overrides || {}),
@@ -644,62 +637,52 @@ const VoicePanel = ({
level='body-xs' level='body-xs'
sx={{ opacity: 0.5, px: 2, textAlign: 'center' }} sx={{ opacity: 0.5, px: 2, textAlign: 'center' }}
> >
Pause or say &ldquo;also&rdquo; between tasks &middot; say Pause between tasks &middot; say &ldquo;scratch that&rdquo; to
&ldquo;scratch that&rdquo; to remove the last one remove the last one
</Typography> </Typography>
</Box> </Box>
)} )}
{/* ── Footer ── */} {/* ── Footer — dismissing is the modal's Cancel; this owns confirm only ── */}
<Box {(creating || showActions) && (
sx={{ <Box
px: 1.5, sx={{
py: 1, px: 1.5,
display: 'flex', py: 1,
alignItems: 'center', display: 'flex',
gap: 1, justifyContent: 'flex-end',
borderTop: '1px solid', gap: 1,
borderColor: 'divider', borderTop: '1px solid',
}} borderColor: 'divider',
> }}
<Button
size='sm'
variant='plain'
color='neutral'
onClick={handleCancel}
> >
Cancel {creating ? (
</Button>
<Box sx={{ ml: 'auto', display: 'flex', gap: 1 }}>
{creating && (
<Button size='sm' variant='solid' color='primary' loading> <Button size='sm' variant='solid' color='primary' loading>
Creating Creating
</Button> </Button>
) : segments.length === 1 ? (
<Button
size='sm'
variant='solid'
color='primary'
onClick={() =>
onUseSingle(segments[0].text, segments[0].overrides || {})
}
>
Use Task
</Button>
) : (
<Button
size='sm'
variant='solid'
color='primary'
onClick={handleCreateAll}
>
Create {segments.length} Tasks
</Button>
)} )}
{showActions &&
(segments.length === 1 ? (
<Button
size='sm'
variant='solid'
color='primary'
onClick={() =>
onUseSingle(segments[0].text, segments[0].overrides || {})
}
>
Use Task
</Button>
) : (
<Button
size='sm'
variant='solid'
color='primary'
onClick={handleCreateAll}
>
Create {segments.length} Tasks
</Button>
))}
</Box> </Box>
</Box> )}
</Box> </Box>
) )
} }