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
}
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 }) => {
/>
)}
</Button>
<Button
size='lg'
variant='solid'
color='primary'
disabled={!taskTitle.trim()}
onClick={createChore}
>
Create
{showKeyboardShortcuts && (
<KeyboardShortcutHint shortcut='Enter' sx={{ ml: 1 }} />
)}
</Button>
{/* Sub-panels (voice/scan) own their own confirm action */}
{!showScan && !showVoice && (
<Button
size='lg'
variant='solid'
color='primary'
disabled={!taskTitle.trim()}
onClick={createChore}
>
Create
{showKeyboardShortcuts && (
<KeyboardShortcutHint shortcut='Enter' sx={{ ml: 1 }} />
)}
</Button>
)}
</Box>
}
>
@@ -1177,7 +1203,6 @@ const TaskInput = ({ onChoreUpdate, isModalOpen, onClose }) => {
userLabels={userLabels || []}
members={circleMembers?.res || []}
userProfile={userProfile}
onClose={() => setShowVoice(false)}
onUseSingle={handleVoiceSingle}
onCreateMany={handleVoiceCreateMany}
/>

View File

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

View File

@@ -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 &ldquo;also&rdquo; between tasks &middot; say
&ldquo;scratch that&rdquo; to remove the last one
Pause between tasks &middot; say &ldquo;scratch that&rdquo; to
remove the last one
</Typography>
</Box>
)}
{/* ── Footer ── */}
<Box
sx={{
px: 1.5,
py: 1,
display: 'flex',
alignItems: 'center',
gap: 1,
borderTop: '1px solid',
borderColor: 'divider',
}}
>
<Button
size='sm'
variant='plain'
color='neutral'
onClick={handleCancel}
{/* ── Footer — dismissing is the modal's Cancel; this owns confirm only ── */}
{(creating || showActions) && (
<Box
sx={{
px: 1.5,
py: 1,
display: 'flex',
justifyContent: 'flex-end',
gap: 1,
borderTop: '1px solid',
borderColor: 'divider',
}}
>
Cancel
</Button>
<Box sx={{ ml: 'auto', display: 'flex', gap: 1 }}>
{creating && (
{creating ? (
<Button size='sm' variant='solid' color='primary' loading>
Creating
</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>
)
}