M6 1908a: recurring reminders + complete/snooze (backend, no web-push)
Per operator: skip Web Push; build the rest of reminder delivery. This is
the client-agnostic half — the model + logic that foreground/native
delivery drives.
- notes.recurrence (migration 0022): daily/weekly/monthly/yearly or null.
update_note accepts it (cleared when the reminder is cleared); rides
export/import + sync push. Serialized on the note.
- Pure next_occurrence(remind_at, recurrence, after): the next fire strictly
after `after`, rolling past missed occurrences; _add_months clamps the day
to the target month (Jan 31 → Feb 28).
- POST /api/notes/<id>/reminder/complete — a recurring reminder advances to
its next occurrence; a one-off clears. POST .../reminder/snooze {minutes}
→ remind_at = now + minutes (1 min .. 30 days).
No VAPID / push-subscription / service-worker — foreground + native delivery
land in the UI commit and the native clients.
Tests (DB-free): normalize_recurrence; next_occurrence (daily/weekly/
monthly-clamp/skip-missed/yearly/none); complete + snooze auth-guards.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
This commit is contained in:
@@ -53,8 +53,11 @@ class Note(Base):
|
||||
archived: Mapped[bool] = mapped_column(Boolean(), nullable=False, server_default=func.false())
|
||||
# Soft delete: non-null => in Trash. Restore sets it back to null.
|
||||
deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
# Optional reminder time (surfaced in the Reminders view; no push in M3).
|
||||
# Optional reminder time (surfaced in the Reminders view + foreground delivery).
|
||||
remind_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
# Optional recurrence for the reminder: daily | weekly | monthly | yearly (else null).
|
||||
# On "complete", a recurring reminder advances remind_at to its next occurrence.
|
||||
recurrence: Mapped[str | None] = mapped_column(Text(), nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()
|
||||
@@ -80,6 +83,7 @@ class Note(Base):
|
||||
"archived": self.archived,
|
||||
"trashed": self.deleted_at is not None,
|
||||
"remind_at": self.remind_at.isoformat() if self.remind_at else None,
|
||||
"recurrence": self.recurrence,
|
||||
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user