Skip to main content

max / goingson

Mobile UX rework: touch-native interactions, segmented events, iOS fixes Day plan timeline (touch-native): - Drag-to-paint replaced with tap (30min default) + long-press (1hr) on touch - Drag-to-reschedule replaced with tap-to-open + long-press action sheet (Open / Reschedule / Unschedule) - Slot height driven by --timeline-slot-h CSS var: 12px desktop, 22px mobile (88px/hour). All JS positioning reads via getSlotHeight(). - Touch interactions snap to 30-min boundaries to absorb finger imprecision Calendar (touch-native): - Month: enlarged tap targets (64px min-height) on mobile, tap-to-expand kept - Week: single-day swipe carousel at <=600px via renderMobileDay; arrows step 1 day on mobile, 7 days on desktop Events view flattened: - Removed List/Month/Week toggle and the inner calendar grid containers - Segmented flat list: Recurring (templates only, expandable) at top, Upcoming in middle, Past collapsible at bottom - renderEventRow gains an isRecurring branch that shows the pattern in place of the date column Time pill subtabs renamed: Day / Week / Month / Timer / Events (was Day Plan / Weekly Review / Monthly Review / Focus Timer / Events) Mobile chrome / overlays: - html, body get overflow-x: hidden and touch-action: pan-y under mobile media query; pill-nav opts back in to pan-x for its own scroller - .modal-overlay, .bulk-actions-bar, .filter-bar.mobile-visible lifted to sit above the 52px tab bar; modal max-height capped to usable viewport - compose.html viewport-fit=cover for safe-area support - Context menu swallows touch-invoked contextmenu (long-press); touch users get the kebab button instead iOS app icon: regenerated with --ios-color #1B365D so the dark border extends edge-to-edge under iOS's corner mask (was "set-in" before). Welcome modal mobile-aware: step 1 says "tap the + tab", step 2 says "tap or long-press the timeline to schedule"; keyboard hint hidden on touch. Email account form: per-provider call-out beneath the email field when the domain is detected. Covers iCloud/me/mac (app-specific password + appleid.apple.com link), Fastmail, Gmail, Yahoo, AOL, Outlook (conditional), Proton (Bridge required note). iOS commands missing from lib.rs invoke_handler (the mobile entry point maintains a separate list from desktop main.rs). Added: get_monthly_review, upsert_monthly_goal, update_monthly_goal_status, delete_monthly_goal, save_monthly_reflection, sync_get_tiers, list_themes, get_theme, get_custom_themes_dir, import_theme, export_theme. Both handler lists must stay in sync going forward.
Co-Authored-By
Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-05-18 22:25 UTC
Commit: b79f301f3607fded7ecc340858fe5696fd51b2a2
Parent: 40bde4b
70 files changed, +531 insertions, -139 deletions
@@ -1,6 +1,6 @@
1 1 # GoingsOn Todo
2 2
3 - v0.3.1. Audit grade A (Run 24). ~773 tests. ~74K LOC. Rust 2024 edition.
3 + v0.3.3 (iOS-only bump for icon fix; desktop still at 0.3.1). Audit grade A (Run 24). ~773 tests. ~74K LOC. Rust 2024 edition.
4 4
5 5 Completed items: [todo_done.md](./todo_done.md)
6 6
@@ -21,10 +21,12 @@
21 21
22 22 ### Mobile (iOS TestFlight — keep in scope, near completion)
23 23 See `todo_mobile.md` for full breakdown. Launch requires:
24 + - [x] iOS TestFlight upload working — v0.3.3 delivered 2026-05-17 (icon fix; v0.3.1 had Tauri default logo)
24 25 - [ ] Android emulator smoke (`cargo tauri android dev`) + CRUD verified on mobile WebView
25 - - [ ] Physical device testing (iOS + Android)
26 + - [ ] Physical device testing (iOS + Android) — run mobile P0+P2 checklist in `docs/human_testing.md`
26 27 - [ ] Safe area insets across device models, keyboard-doesn't-obscure-inputs, background/foreground transitions
27 - - [ ] iOS internal testing: invite phone Apple ID, add to TestFlight Internal group, install + smoke-test
28 + - [ ] iOS internal testing: invite phone Apple ID into ASC Users and Access (Developer role), add to TestFlight Internal group, install + smoke-test on v0.3.3
29 + - [ ] Resolve Missing Compliance prompt on v0.3.3 build (encryption answer — HTTPS-only exempt). **Plist key added 2026-05-18** in `gen/apple/goingson-desktop_iOS/Info.plist` (`ITSAppUsesNonExemptEncryption = false`); future uploads won't re-prompt. The currently-stuck v0.3.3 build still needs the answer set once via App Store Connect UI (TestFlight → build → Encryption: "Uses only exempt encryption").
28 30 - [ ] Add Privacy Policy page to GoingsOn project on MNW (URL: `https://makenot.work/p/goingson#section-privacy-policy`)
29 31 - [ ] iOS External Testing: add build, fill Beta App Information, submit for Beta App Review, enable Public Link
30 32 - [ ] Android release: Google Play Developer account ($25), release AAB, Play Console listing, submit
@@ -1,6 +1,6 @@
1 1 # GoingsOn - Mobile Port
2 2
3 - Done: Phases 1-7 (CSS, touch, navigation, views, build config, tab bar, distribution setup). Active: None. Next: Testing + release.
3 + Done: Phases 1-7 (CSS, touch, navigation, views, build config, tab bar, distribution setup); touch-native UX rework (paint, reschedule, week carousel); polish (tap targets, scroll lock, modal cutoffs, icon, provider hints, segmented events view). Active: None. Next: Internal TestFlight install + smoke test on a real device.
4 4
5 5 ---
6 6
@@ -18,6 +18,23 @@
18 18 - [x] Keyboard doesn't obscure inputs in modals — 2026-05-16: `wireKeyboardScrollIntoView()` in `mobile.js` uses `visualViewport` to scroll focused inputs into view on keyboard show. Verify on device.
19 19 - [x] Background/foreground transitions work correctly — 2026-05-16: `visibilitychange` handler in `app.js` invalidates caches and refreshes view/sync indicator/day-plan time when app was hidden >30s. Verify resume behavior on device.
20 20
21 + ### UX Rework (Touch-Native Interactions)
22 + Mobile UX audit 2026-05-17 — desktop interactions that don't translate.
23 + - [x] **Drag-to-paint event creation** — 2026-05-18: `day-planning-render.js` skips `onmousedown`/`onmouseenter` on touch. Tap opens picker with 30-min default; long-press opens picker with 1-hour default. Both snap to 30-min boundaries on touch to absorb finger imprecision. Wired in `wireTouchInteractions` in `day-planning.js`.
24 + - [x] **Drag-to-reschedule events** — 2026-05-18: `onmousedown` on `.timeline-item` is gated to non-touch. Tap opens the item; long-press opens an action sheet (Open / Reschedule / Unschedule for tasks; Edit time for events/blocks). Reschedule reuses `openScheduleTaskModal`.
25 + - [x] **Dense month/week calendar grids** — 2026-05-18: Month grid tap targets enlarged (64px min-height, larger date numbers); tap-to-expand behavior kept. Week view: single-day swipe carousel at `max-width: 600px` (`renderMobileDay` in `events-calendar.js`); arrows step by 1 day on mobile, 7 on desktop.
26 +
27 + ### UX Rework (additional, 2026-05-18)
28 + - [x] **Day plan timeline tap targets** — `--timeline-slot-h` CSS variable drives `.timeline-slot` height. Default 12px; 22px under `max-width: 600px` (88px/hour on mobile). JS reads it via `GoingsOn.dayPlanRender.getSlotHeight()` so positioning math stays in sync.
29 + - [x] **Horizontal scroll lock** — `html, body { overflow-x: hidden; touch-action: pan-y }` under mobile media query. `.pill-nav` opts back in to `pan-x` for its own scroller.
30 + - [x] **Modal/overlay cutoff behind tab bar** — `.modal-overlay`, `.bulk-actions-bar`, `.filter-bar.mobile-visible` all lifted to sit above the 52px mobile tab bar. Modal `max-height` capped to actual usable viewport.
31 + - [x] **App icon on iOS** — regenerated with `--ios-color "#E8F4F8"` then `#1B365D` so the icon background extends edge-to-edge under iOS's corner mask instead of looking "set-in".
32 + - [x] **Welcome modal mobile copy** — `app.js` `showWelcome` branches on `GoingsOn.touch.isTouchDevice`: step 1 says "tap the + tab", step 2 says "tap or long-press the timeline to schedule"; keyboard-shortcut hint hidden on touch.
33 + - [x] **Email provider hints** — `email-accounts.js` now shows a per-provider call-out beneath the email field once the domain is detected. Covers iCloud/me/mac (app-specific password + link), Fastmail, Gmail, Yahoo, AOL, Outlook (conditional), Proton (Bridge required).
34 + - [x] **Time tab subtab labels** — renamed to **Day / Week / Month / Timer / Events** in `index.html` (was Day Plan / Weekly Review / Monthly Review / Focus Timer / Events). Shared markup → applies to desktop and mobile.
35 + - [x] **Events view flattened** — removed the inner List/Month/Week toggle and the calendar grid containers. Events view is now a segmented flat list: Recurring (templates only, expandable, at top) → Upcoming → Past (collapsible). `events.js` `load()` splits the response into three buckets via `recurrence !== 'None' && !isRecurringInstance` filter; new `renderEventRow` `isRecurring` branch shows the recurrence pattern in place of the date column.
36 + - [x] **iOS missing commands** — `lib.rs` (mobile entry point) was missing several commands present in desktop `main.rs`. Added: `get_monthly_review`, `upsert_monthly_goal`, `update_monthly_goal_status`, `delete_monthly_goal`, `save_monthly_reflection`, `sync_get_tiers`, `list_themes`, `get_theme`, `get_custom_themes_dir`, `import_theme`, `export_theme`. Both handler lists must be kept in sync going forward.
37 +
21 38 ### Platform Features (Future)
22 39 - [ ] Push notifications via Tauri plugin
23 40 - [ ] Haptic feedback for actions
@@ -2,7 +2,7 @@
2 2 <html lang="en">
3 3 <head>
4 4 <meta charset="UTF-8">
5 - <meta name="viewport" content="width=device-width, initial-scale=1.0">
5 + <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
6 6 <title>Compose Email</title>
7 7 <link rel="stylesheet" href="css/styles.css">
8 8 <style>
@@ -234,11 +234,11 @@
234 234 <!-- Time Tab Group -->
235 235 <div id="time-view" class="view tab-group hidden">
236 236 <div class="pill-nav" role="tablist" aria-label="Time views">
237 - <button class="pill active" data-subview="day-plan">Day Plan</button>
238 - <button class="pill" data-subview="weekly-review">Weekly Review</button>
239 - <button class="pill" data-subview="monthly-review">Monthly Review</button>
237 + <button class="pill active" data-subview="day-plan">Day</button>
238 + <button class="pill" data-subview="weekly-review">Week</button>
239 + <button class="pill" data-subview="monthly-review">Month</button>
240 + <button class="pill" data-subview="timer" title="Pomodoro-style focus sessions">Timer</button>
240 241 <button class="pill" data-subview="events">Events</button>
241 - <button class="pill" data-subview="timer" title="Pomodoro-style focus sessions">Focus Timer</button>
242 242 </div>
243 243
244 244 <!-- Day Plan Sub-View -->
@@ -344,14 +344,7 @@
344 344 <div id="events-view" class="subview hidden" role="tabpanel" aria-labelledby="events-tab">
345 345 <div class="page-header">
346 346 <h2 class="page-title">Events</h2>
347 - <div style="display: flex; align-items: center; gap: 1rem;">
348 - <div class="view-toggle" id="events-view-toggle">
349 - <button class="view-toggle-btn active" data-mode="list" onclick="GoingsOn.events.setViewMode('list')">List</button>
350 - <button class="view-toggle-btn" data-mode="month" onclick="GoingsOn.events.setViewMode('month')">Month</button>
351 - <button class="view-toggle-btn" data-mode="week" onclick="GoingsOn.events.setViewMode('week')">Week</button>
352 - </div>
353 - <button class="btn btn-primary" onclick="GoingsOn.events.openNew()" title="New event (n)">+ New Event <kbd class="kbd-hint">n</kbd></button>
354 - </div>
347 + <button class="btn btn-primary" onclick="GoingsOn.events.openNew()" title="New event (n)">+ New Event <kbd class="kbd-hint">n</kbd></button>
355 348 </div>
356 349 <div id="events-bulk-bar" class="bulk-actions-bar hidden">
357 350 <span class="bulk-count">0 selected</span>
@@ -360,7 +353,24 @@
360 353 <button class="btn btn-sm" onclick="GoingsOn.events.clearEventSelection()">Cancel</button>
361 354 </div>
362 355 <div class="events-list" id="events-list">
363 - <div class="event-table-virtual" id="event-table" role="grid" aria-label="Events list">
356 + <details class="past-events-section" id="recurring-events-section" open style="display: none;">
357 + <summary class="past-events-toggle">
358 + <span class="past-events-label">Recurring</span>
359 + <span class="past-events-count" id="recurring-events-count">0</span>
360 + </summary>
361 + <div class="event-table-virtual" id="recurring-event-table">
362 + <div class="event-header-row" role="row">
363 + <div class="event-cell" role="columnheader">Pattern</div>
364 + <div class="event-cell" role="columnheader">Time</div>
365 + <div class="event-cell" role="columnheader">Event</div>
366 + <div class="event-cell" role="columnheader">Location</div>
367 + <div class="event-cell" role="columnheader"></div>
368 + </div>
369 + <div class="event-list-container" id="recurring-event-list-container"></div>
370 + </div>
371 + </details>
372 + <h3 class="events-section-heading" id="future-events-heading" style="display: none;">Upcoming</h3>
373 + <div class="event-table-virtual" id="event-table" role="grid" aria-label="Upcoming events">
364 374 <div class="event-header-row" role="row">
365 375 <div class="event-cell" role="columnheader">Date</div>
366 376 <div class="event-cell" role="columnheader">Time</div>
@@ -393,27 +403,6 @@
393 403 </div>
394 404 </details>
395 405 </div>
396 - <!-- Month Calendar View -->
397 - <div id="events-month-view" class="events-calendar-container hidden">
398 - <div class="calendar-nav">
399 - <button class="btn btn-secondary" onclick="GoingsOn.eventsCalendar.prevMonth()">&larr;</button>
400 - <button class="btn btn-secondary" onclick="GoingsOn.eventsCalendar.goToThisMonth()">This Month</button>
401 - <button class="btn btn-secondary" onclick="GoingsOn.eventsCalendar.nextMonth()">&rarr;</button>
402 - <span class="calendar-nav-label" id="month-calendar-label"></span>
403 - </div>
404 - <div id="month-calendar-grid"></div>
405 - <div id="month-day-detail" class="month-day-detail hidden"></div>
406 - </div>
407 - <!-- Week Calendar View -->
408 - <div id="events-week-view" class="events-calendar-container hidden">
409 - <div class="calendar-nav">
410 - <button class="btn btn-secondary" onclick="GoingsOn.eventsCalendar.prevWeek()">&larr;</button>
411 - <button class="btn btn-secondary" onclick="GoingsOn.eventsCalendar.goToThisWeek()">This Week</button>
412 - <button class="btn btn-secondary" onclick="GoingsOn.eventsCalendar.nextWeek()">&rarr;</button>
413 - <span class="calendar-nav-label" id="week-calendar-label"></span>
414 - </div>
415 - <div id="week-calendar-grid"></div>
416 - </div>
417 406 </div>
418 407 </div><!-- /time-view -->
419 408
@@ -574,7 +563,6 @@
574 563 </nav>
575 564 <div id="mobile-more-popover" class="mobile-more-popover" role="menu" aria-hidden="true" aria-label="More options">
576 565 <button role="menuitem" onclick="GoingsOn.settings.open(); GoingsOn.navigation.closeMorePopover();">Settings</button>
577 - <button role="menuitem" onclick="GoingsOn.keyboard.showShortcuts(); GoingsOn.navigation.closeMorePopover();">Shortcuts</button>
578 566 </div>
579 567
580 568 <!-- Action Bottom Sheet (mobile context menus) -->
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file