fix(calendar): refuse to change an event's kind on update (#65) #66
No reviewers
Labels
No labels
bug
discussion
enhancement
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
edufeed/edufeed-app!66
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/issue-65-block-allday-toggle"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #65 — the half of #62 that was not a cache bug.
The bug, as measured
TestOER reproduced it in a browser at
b82bfe9c, twice. Create a timed event, toggle All Day, save:Two live events at one d-tag, on both relays, the original byte-identical. The page stays on the original naddr and renders the pre-edit title, with no error.
/calendarlists both.updateEventtookkind: eventData.kind || existingEvent.kind, andconvertFormDataToEventsets 31922 for date-based and 31923 for time-based. A replaceable event is addressed by(kind, pubkey, d-tag), so the new kind is a new coordinate — nothing is replaced.That is the same user-visible symptom as #62 ("my edit did nothing"), which is why it needed splitting out: no cache fix can reach it, because the edit genuinely went somewhere else.
Why refusing, and not something cleverer
Keeping the kind is not legal. NIP-52 requires
startto beYYYY-MM-DDon 31922 and a unix timestamp on 31923, plus a requiredDday-granularity tag on 31923. There is no way to express an all-day event as a 31923, so the kind change is forced by the spec — this cannot be fixed by makingupdateEventkeep the existing kind.Delete-and-recreate cannot be done correctly by this client. NIP-52 calendars (kind 31924) reference their events by
a=<kind>:<pubkey>:<d>, and that list lives on the calendar event, signed by its owner — who need not be the person editing. A correct migration would have to rewriteatags in events the editing user cannot sign. RSVPs (31925) are at least authored by their own users, but the NIP states it "is intentionally not defining what happens if a calendar event changes after an RSVP is submitted", so there is no spec-blessed migration to follow either.Refusing costs nothing that works. The original event survives byte-identical today, so the toggle has never produced a working edit. Blocking it does not remove a working feature; it stops the app manufacturing a second event.
What changed
updateEventrefuses a kind change — before signing, before publishing, and beforecalendarStore.setEvents. That ordering is deliberate:updateEventoptimistically rewrites the store first, so a guard placed later would still show the user a phantom edit that no relay has.The type selector is disabled in edit mode, with the reason as both a
titleattribute and visible helper text (en + de). The UI lock alone would not be enough — the action layer is where the fork happens.Tests
Five cases in
src/lib/__tests__/calendar-update-event-kind-lock.test.js: both directions refused, nothing published,calendarStoreuntouched, and an ordinary same-kind edit still working.Negative control, actually run: with the guard reverted, 4 of 5 fail and the fifth — the same-kind edit — passes. That fifth is the control that makes the other four evidence about the guard rather than about a broken
updateEvent.Verification at
2df1c8bd.envexported andnode_modules/.binon PATH.pnpm check0 errors (4 warnings, all pre-existing in unrelated test fixtures).pnpm lintclean, exit 0.2df1c8bd;git status --porcelainis empty, so the bytes tested are the bytes committed.I did not run the browser. The acceptance check is that the All Day button is disabled in edit mode and that a query at the d-tag after an attempted toggle returns exactly one event.
Note for anyone testing: the publish is not visible on the e2e relays for ~4-8s, and seeded calendar events live on 17002 only — a 17003 query looks empty on a perfectly healthy relay.
🤖 Generated with Claude Code