Testing feedback:
- show-all / hide-all are now side by side (bordered), not two big stacked rows
- the busy quick-access strip becomes a single "Einstellungen" button that
opens the full menu (which already holds accounts/profile/groups/server/sync/
logout)
- extra horizontal + bottom padding so the footer button is not clipped by the
iPhone's rounded corners / home indicator
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the top-bar menu popup with an off-canvas left drawer, opened by the
hamburger or a narrow left-edge swipe (kept off the content area so month/week
swipe stay free). The drawer hosts the calendar visibility list, a view
switcher, group switching, and quick access to Settings/Accounts/Profile/
Groups/Server plus sync and logout.
- extract the filter list into reusable CalendarFilterContent (shared by the
drawer and the still-present CalendarFilterSheet); the sheet is now a thin
wrapper
- new CalendarDrawer view; CalendarHostView hosts it as a ZStack overlay with a
scrim and edge-swipe gesture, and presents drawer destinations as sheets
- opt-in surface colour: surfaceColor (device-local) tints the flat top bar;
empty keeps the translucent .bar material. Settings row with an Auto reset.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Generalise the two-tier SettingsSync into a server-driven flag map: each
syncable key has an account-wide flag (fetched from the server); pull applies
only on-flagged keys, push (read-modify-write) sends only on-flagged keys plus
the flag map. Per-row link icon toggles a setting's sync; a global switch flips
all. Enabling a flag adopts this device's current value as the shared one.
- AppSettings: fix bg_color mapping (was background_color), add cache_months,
month_view_paged, sync_flags
- SettingsView: uniform rows (sync icon | name | value), ContrastSelector and
the button pickers replaced by menu dropdowns, every colour gets a reset to a
canonical default, global "sync all" toggle; account settings (privacy,
group calendar, hide profile) kept in their own block; language and text/line
contrast kept device-local (iOS-specific)
- unify default line colour to #3A3A52 across views
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- one birthday calendar per user; BirthdaysImporter auto-creates/targets it
(no picker) and reconciles per-device (external_uid contact:<deviceId>:<id>)
so multiple devices don't clash
- Contacts birthday sync now runs on every "Sync with server" (syncFromServer),
not just app launch and the manual button
- report the device after each sync (POST /api/birthdays/sync-report) for the
web device list
- Accounts: Birthdays-from-Contacts section drops the target picker, keeps the
reminder picker; removed the birthday toggle from the generic new-calendar sheet
- New-birthday sheet targets the single calendar, offers activation if none
- exclude the birthday calendar from the group-visible ("shared calendar") picker
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- LocalCalendar/CalEvent gain is_birthday + birthday fields; API sends
rrule/external_uid/birth_year and can create/update birthday calendars
- BirthdaysImporter: mirrors Contacts birthdays into a chosen birthday
calendar, reconciling by external_uid (leaves manual entries untouched)
- AccountsView: "birthday calendar" toggle + notify-days when creating a
calendar, and a "Birthdays from Contacts" section (enable + target + sync now)
- FAB long-press menu -> "New birthday" opens a minimal name+date mask
(BirthdayEditorSheet) targeting birthday calendars, with year-unknown option
- Event bars render display_title (age) and a cake icon via EventLabel
- NSContactsUsageDescription added to both app build configs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New device-local setting "Monatsansicht seitenweise wischen" (@AppStorage, no
server sync). Off keeps the continuous vertical scroll feed; on switches the
month view to a paged TabView — one month per screen, six height-filling week
rows, swipe left/right to change month (bounded ±monthsBack/Ahead range to keep
the TabView light). WeekRow gains a fillHeight flag so the same row renders at a
fixed height (scroll) or fills the page evenly (paged); its inner cells/divider
now size to the actual geometry. Paged selection is two-way bound to the store's
current date so prev/next/today and the title stay in sync.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hiding or showing a calendar on the web sets enabled=false + sidebar_hidden
server-side, so the server stops returning that calendar's events entirely.
The app only reconciled these per-calendar flags when the filter sheet or the
accounts screen happened to be opened — never on launch, resume or the
periodic pull. So a calendar re-enabled on the web stayed invisible (still
banished locally, and its events weren't in the cache) until a manual sync or
relaunch — the "hide it on the web and the app won't bring it back" bug.
Add CalendarStore.reconcileCalendarVisibility(api:) which pulls the account
lists, updates the banished set from the server's sidebar_hidden flags, and
returns whether anything changed. CalendarHostView now runs it (via
syncFromServer) on launch, on scenePhase .active, in the periodic loop, and on
manual sync — forcing a refetch when the set changed so a re-enabled calendar's
events actually reappear without user intervention.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The server now reports per-calendar sync failures (e.g. expired
credentials) alongside an otherwise-successful events response, via an
"errors" array. Previously such failures were silently swallowed, so a
calendar could appear enabled while showing zero events with no
explanation.
- Decode the new "errors" field into SyncError (source/name/message),
following the existing JSONSerialization + from(json:) convention.
- CalendarStore.syncErrors captures these on successful personal
fetches (loadEvents, prefetchBackground); left untouched on a hard
fetch failure so it isn't conflated with lastError. Group overlay
fetches never populate it (the endpoint doesn't apply there).
- CalendarHostView shows a second red warning banner alongside the
existing lastError banner, reusing the same visual style.
- Reminder editor: presets + custom number+unit (minutes/hours/days/weeks)
- Grey out + footer hint when the selected calendar's reminders are muted;
reminders are kept, scheduler already skips them
- New synced setting defaultEventDurationMinutes for new events
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- DayPreviewAllDayBar: use custom ChevronBarShape (polygon clip) instead of
chevron SF Symbols; bars have tapered pointed ends for continuation
- MonthView rowHeight: increase bottom padding from +4 to +16 so the
overflow "+N" count no longer overlaps the last event bar lane
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CalendarDayWidget: add 6pt top padding to header so content isn't flush
against the widget edge
- ThisWeekWidget: increase per-day event cap from 6 to 8 to prevent "+1"
overflow when there is vertical space available
- MonthView: add DayContextPreviewView to the long-press context menu using
.contextMenu(menuItems:preview:); shows all-day events as colored bars
with chevron continuation arrows, timed events as dot+time+title rows
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
C1 — Localization: route the remaining hardcoded German strings through
L10n (LoginView, ServerSetupView, SettingsView email, EventDetailSheet) so
"System Default" + English device language shows fully English text.
C2 — Per-calendar reminders: parse the new reminders_enabled flag on every
calendar type; CalendarStore persists a reminderDisabledKeys set and passes
it to NotificationScheduler, which skips events of muted calendars (default
and per-event reminders). Filter sheet gains a per-calendar reminder toggle
(leading swipe + bell.slash indicator), reconciled from the server and
synced back via PUT.
C3 — Widgets:
- Shared WidgetTime.range helper; Today / Today & Tomorrow / Three Days /
Up Next now show start–end instead of only the start time.
- This Week: show up to 6 events per day (was 3) to use the height.
- Two Weeks: mini event-title pills instead of bare dots.
- Two Months: weeks expand to fill the column (no more empty lower third).
- Day & Events: smaller header/strip/rows so content stops clipping.
- Next 5 days → Next 7 days (range + labels), higher row cap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Left side now holds only the prev/next chevrons; "Heute" moved next to the
burger menu on the right (both flat and Liquid Glass bars), so the month title
is centered again instead of pushed right by a heavy left cluster.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The top bar now shows only nav + title + a single burger. Tapping it opens a
compact menu: View (with a fixed icon, no longer per-view), Filter, Groups
(if any), Sync, and an "Einstellungen" entry that opens the existing full menu.
Removed the separate group/view/filter icons from both the flat bar and the
Liquid Glass toolbar.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per-event reminders (multiple, local calendars only) in the editor, prefilled
from a new "default reminder" setting that applies to all events otherwise.
CalEvent gains `reminders`; AppSettings/SettingsSync sync default_reminder_minutes
(always group). New NotificationScheduler requests permission and schedules the
soonest ≤60 upcoming reminders via UNUserNotificationCenter, rescheduling on
load/sync/edit and when the default changes (skipped in group overlay).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The NavigationStack toolbar title never refreshes on month change on iOS 26
(4 approaches tried: principal Text, navigationTitle, @Observable store). The
title now renders as a normal inline Text in a top safe-area inset just below
the system glass toolbar — the same mechanism as the flat variant, which does
update. The system toolbar keeps the buttons + the real Liquid Glass look.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Group icons are now semantic keys (people/home/heart/work/school/sports/party/
pet/travel/music/food/star) rendered as SF Symbols in the picker, group list,
switcher, banner and filter — instead of OS emoji that looked different on every
platform. Legacy emoji values still render as a fallback. GroupCombinedView uses
the server display_title.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The system NavigationStack toolbar title would not refresh on a plain @State
change (title kept disappearing on iPhone). Moved visibleMonth into the
@Observable CalendarStore so the toolbar's read is tracked with @Observable's
fine-grained observation and refreshes on month change. Reverted the @State/.id
workaround. Real system glass bar retained.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The custom safeAreaInset bar removed the actual iOS-26 Liquid Glass look
("no glass even though enabled"). Restored the system NavigationStack glass
toolbar and instead fixed the disappearing month title by driving it from a
@State (navTitle) updated via onChange(of: titleString) and keyed with .id,
which forces the system bar to refresh on month change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The calendar filter, when a group overlay is active, now lists the group's
members (+ the shared group calendar) and lets you hide each one individually
(Outlook-style). Filtering is client-side via CalendarStore.hiddenGroupKeys
(per-member gm:<id> / group-calendar gc keys), reset when switching groups.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Liquid Glass: the calendar content now scrolls underneath a translucent
safeAreaInset bar (real glass look restored) while the inline title stays
reliable — toggling Liquid Glass is visibly different again.
- Group events are prefixed with the group's own emoji icon (from group
settings) instead of a generic people glyph, so they're recognisable.
- Week view: today's column header now uses the configured "today" colour
instead of the accent colour (matches the current-time line).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
navigationTitle/principal items in the NavigationStack toolbar silently fail to
refresh on scroll-driven state changes (visibleMonth), so the month label
vanished on month change and only returned after an unrelated rebuild. The glass
variant now uses a custom top bar with the same inline Text title as the flat
variant (proven to update), styled with glassEffect (iOS 26) / ultraThinMaterial.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The glass variant rendered the month title in a `.principal` ToolbarItem, which
SwiftUI drops when the state it reads (visibleMonth) changes while on screen —
it only reappeared on an unrelated rebuild (opening/closing the menu) and
vanished again on the next month change. Switched to `.navigationTitle`, which
the system updates reliably.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Der Gruppen-Modus ersetzte events nur mit dem schmalen aktuellen Fetch, statt
denselben Cache/Prefetch-Pfad wie die Normalansicht zu nutzen -> Termine
erschienen erst nach Scrollen. Jetzt fetchForMode (personal/group) läuft durch
loadEvents + prefetchBackground + refreshFromCache; Moduswechsel lädt breit neu.
In der Gruppenansicht greift der "ausgeblendet"-Filter nicht.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Kalenderverwaltung: tappbarer ColorPicker pro Kalender (lokal/iCal direkt;
CalDAV/Google/HA klappen ihre Unterkalender mit je eigenem Farbwähler auf).
Neue API: updateLocalCalendarColor, updateICalColor, setCalendarColor
(caldav/google/homeassistant) -> PUT …/{id} {color}. Geteilte Kalender
read-only (nur Besitzer).
- Top-Bar: Gruppen-Umschalter nur bei vorhandenen Gruppen, "Heute" nicht mehr
quetschbar (fixedSize), kompaktere Icons -> "Heute" wird nicht mehr zu "H…".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Gruppen sind nicht mehr nur im Menü versteckt: im Top-Bar gibt es einen
Gruppen-Umschalter (Persönlich / <Gruppe>). Beim Wählen einer Gruppe zeigt
der echte Monats-/Wochen-/Tagesansicht die kombinierte Überlagerung
(GET /groups/{id}/combined) mit server-definierten Farben und
Besitzer-Präfix; ein Banner "Gruppenansicht: <Name>" mit "Verlassen".
CalendarStore.activeGroup steuert den Modus.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add two-way settings sync (SettingsSync) with toggle, app-start/foreground/
10-min pull and debounced push; server wins; view/week-start/dim-past always
sync. Wire previously-ignored settings (hour height, contrasts, week start,
default view, dim past) into the actual UI.
- Make AppSettings decoding resilient (decodeIfPresent) so getSettings no longer
fails on iOS-only fields the server omits; keep text/bg/line colors local-only;
month divider/label colors now sync.
- Auto-refresh after create/edit (cache-busting) and optimistic removal on
delete; switch delete confirm to a centered alert. Add HA event deletion.
- Calendar visibility: fix inverted hide/show toggle; normalize calendar keys so
local filtering works for all sources; sync banish with server sidebar_hidden
(CalDAV/Google/HA), refetch on un-banish.
- Manual "sync with server" button in the menu.
- Upcoming widget shows next 5 days (renamed).
- Week/Day view: route multi-day timed events to the all-day strip so they no
longer render as a full-height block.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Vertical-scroll month view with multi-day event spans, zig-zag month
divider, CW number per week, on-demand event loading while scrolling
- Top bar redesign: icon-only view picker on right, month title centered
- Long-press context menus on day cells (month) and hour slots (week/day)
for "New event", "Open in week view", "Open in day view", "Open in month view"
- Localization system with system/de/en switch covering top bar, view picker,
settings, menu, profile, server, accounts, event editor, agenda
- Three new color pickers (text/background/line) + today-marker color
applied in calendar views; current-time line now uses today color
- App icon: removed alpha channel, accent color set to icon green (#20A050)
- TestFlight: ITSAppUsesNonExemptEncryption=NO baked into Info.plist keys
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>