fix: keep edit modals open when a change is rejected - #18
Draft
dsnsgithub wants to merge 2 commits into
Draft
Conversation
Event and routine modals dismissed themselves regardless of whether the write succeeded, so a rejected change looked like it silently vanished. - Add/edit event: report which two events overlap instead of a generic message, and keep the modal open so times can be corrected in place. Editing also no longer pushes the rejected times into the event row, which previously left the list showing times that were never saved. - Add routine: a duplicate name (the pre-filled "New Routine!" being the common case) closed the modal without creating anything, so the routine never appeared in the list. Keep the modal open on failure and reset the form after a successful create. Deep copy the schedule before mutating it so the previous state object stays intact. - Dark mode: the root layout applied the stored scheme during render on every pass, remounting the tab navigator and bouncing the user out of the screen they were on. Apply it once in an effect, set Appearance from the toggle itself, and drive the status bar from the reactive value. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XN5J6bUa9anKhYUSfcnpiz
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
dsnsgithub
marked this pull request as draft
August 20, 2026 12:32
…ction-points-51n3yv
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Addresses three friction points reported from within the mobile app.
All three share one root cause on the event/routine side: the modals called
setModalVisible(false)unconditionally, so a rejected write looked identical to a silent failure — the sheet closed, the user's input was gone, and nothing had changed.Event overlaps and invalid times
AddEventModalandEventModalboth dismissed themselves right after calling their save function, whether or not validation passed. The user had to reopen the modal and re-enter the name and both times from scratch, guided only by "This event overlaps with another event or has an invalid start/end time" — which never said which event it collided with.EventModalwas worse: it calledprops.setStartTime/props.setEndTimebefore attempting the save, so a rejected edit left the event row in the routine list displaying times that were never written to storage.areEventsValid/createCustomTimepair in both modals is replaced by a sharedfindEventConflictinsrc/utils/eventValidation.ts, which returns the offending pair ("Lunch" overlaps with "5th Period"…) instead of a bare boolean.EventModalapplies the new times to the parent only after the save is accepted.The shared helper also drops the old
events.length <= 1early return, so a lone event's start/end is checked like any other. Both modals already blockedstart >= endat the picker and on Finish, so nothing that used to save stops saving.Routine missing after creation
createNewRoutinerejects a duplicate name, and the pre-filled name is always"New Routine!"— so creating a second routine without renaming it hit the duplicate branch, the modal closed anyway, and no routine appeared in the list.createNewRoutinedeep copies the schedule before mutating. It previously used{ ...schedule }, which shares theroutinesobject with the existing state, so the pre-update object was mutated in place. The other writers here (addEvent,modifyEventTimes) already deep copy.Dark mode toggle
app/_layout.tsxcalledAppearance.setColorScheme()andsetColorScheme()in the render body, on every render. That re-entered render from render and remountedNativeTabs, which resets navigation state — the toggle would bounce the user out of the screen they were on, and the re-applied stored value could land back on the scheme they started from.Appearanceand the nativewind scheme, so nothing re-reads a stale value mid-flight.StatusBarreads the reactivecolorSchemeinstead ofstorage.getString(...), so it flips with the rest of the UI.Not addressed
The countdown-timer engagement note is an observation rather than a defect, so nothing changed there.
Testing
bun run lint,bun run format, andtsc --noEmitinapps/mobileall pass locally, and all CI checks are green on this branch.The meaningful coverage comes from
test-ios, which builds the app, boots an iPhone 17 Pro simulator, and runs the whole Maestro suite. Two of those flows exercise this diff directly:darkMode.yamltoggles dark mode and then walks Settings → General → Back → Routines → Back — the navigation path the render-phasesetColorScheme()was resetting.editRoutine.yamlopens an event, renames it, and saves through theEventModalFinish button that now gates on the save succeeding.Worth flagging for reviewers:
test-androidonly builds the Android simulator app, it does not run Maestro. So the dark mode fix is verified on iOS but not on Android, whereNativeTabsandAppearanceare backed by a different native implementation. The manual repro for that one is: toggle dark mode from a nested settings screen and confirm you stay put and the theme actually flips.🤖 Generated with Claude Code
https://claude.ai/code/session_01XN5J6bUa9anKhYUSfcnpiz