Skip to content

feat: Android release fix, Orbit time picker, calendar picker + pagination, retrospective fix - #306

Merged
thomasluizon merged 1 commit into
mainfrom
fix/android-release-and-orbit-time-picker
Jun 25, 2026
Merged

feat: Android release fix, Orbit time picker, calendar picker + pagination, retrospective fix#306
thomasluizon merged 1 commit into
mainfrom
fix/android-release-and-orbit-time-picker

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

The orbit-ui-mobile half of a paired, cross-repo session change (sibling: the orbit-api PR linked in the comments). Everything from this session in one PR.

Android release build fix

@siteed/audio-studio pinned to 3.2.0. Root cause: 3.2.1 (published 2026-06-20) changed its reject override's code param to nullable, which no longer matches expo-modules-core's Promise interface → the release AAB Kotlin compile failed. 3.2.0 is the last good version; the ^ caret had silently pulled 3.2.1.

Time selector → custom Orbit component

The habit form's time field was a raw <input type="time"> (the ugly browser popup). Rebuilt as a custom Orbit popover (hour / minute / AM-PM columns, violet-selected, Roboto tabular numerals), with the trigger matched to the date field's exact height. Mobile keeps the native Android dialog behind a matching trigger. Both derive 12h/24h from profile.uses24HourClock (computed server-side from the user's timezone — São Paulo→24h, New York→12h), read from the already-cached profile (no extra fetch).

Calendar picker + pagination

  • A "Calendars" section in the calendar-sync screen (web + mobile) listing the user's calendars with a Switch each, persisted via the new endpoints. Default = all owned.
  • Client-side pagination ("Show more", 20/page) of the events list, now that we pull all owned calendars.
  • Events show their source calendar name.

Retrospective display

One-time tasks render Completed / Not completed instead of a percentage (paired with the backend cap + isOneTime).

Tests

shared 1013, web 1619, mobile 620 — all green. The only failures are the pre-existing local @sentry/* + @playwright/test install gap, which CI resolves via npm ci.

🤖 Generated with Claude Code

…calendar picker + pagination

- fix(mobile): pin @siteed/audio-studio to 3.2.0 (3.2.1 made reject(code) nullable, breaking the
  release AAB Kotlin compile against expo-modules-core)
- feat(web): rebuild the time selector as a custom Orbit popover (no browser picker), trigger
  height matched to the date field; both pickers derive 12h/24h from profile.uses24HourClock
- feat(calendar): client-side pagination of the events list + a calendar picker section
  (web + mobile) backed by GET /calendar/calendars + PUT /selected-calendars; events show their
  source calendar name
- fix(retro): render one-time tasks as Completed / Not completed instead of a percentage
- shared: calendarId/calendarName on events, calendar endpoints + UserCalendar types,
  uses24HourClock on the profile schema, isOneTime on retrospective stats

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
orbit-ui-mobile-web Ignored Ignored Jun 25, 2026 5:12am

Request Review

@thomasluizon

Copy link
Copy Markdown
Owner Author

Paired with thomasluizon/orbit-api#249 (the backend half: multi-calendar sync, retrospective caps, server-derived clock format).

@sonarqubecloud

Copy link
Copy Markdown

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review: PR #306 — APPROVE

Summary

PR bundles four features: Android release fix (pin audio-studio to 3.2.0), custom Orbit time picker, Calendar picker + pagination, and retrospective one-time task display fix. All land on both platforms, i18n is in sync, schema changes are additive, and tests cover all new paths.

Findings

Critical / High

None

Medium

calendarId, calendarName, and uses24HourClock declared .optional() — may break if API emits null

  • location: packages/shared/src/types/calendar.ts:37-38, packages/shared/src/types/profile.ts:24
  • issue: Zod's .optional() accepts undefined (absent key) but throws on null. The default C# System.Text.Json serialiser emits null for nullable strings/bools rather than omitting the key, unless WhenWritingNull is configured. Every other nullable string in the same schema uses .nullable() (e.g. startDate, endTime, recurrenceRule). If calendarId, calendarName, or uses24HourClock follow the same C# null convention, userCalendarsSchema.parse() and the profile parse will throw at runtime.
  • risk: Calendar picker section and time picker 12h/24h detection silently broken on real API responses.
  • fix: Apply .nullable().optional() to match the existing schema convention, or confirm the C# DTO side uses WhenWritingNull:
// packages/shared/src/types/calendar.ts
calendarId: z.string().nullable().optional(),
calendarName: z.string().nullable().optional(),

// packages/shared/src/types/profile.ts
uses24HourClock: z.boolean().nullable().optional(),
  • reference: CLAUDE.md rule 8; sibling fields startDate, endTime, recurrenceRule all use .nullable()

Subagents

Agent Verdict
parity-checker PAIRED (web actions/calendar.ts has no mobile mirror — correct adapter-split)
i18n-syncer IN SYNC (14 new keys in both locales; total 1,986 keys each)
contract-aligner NOT VERIFIABLE IN CI (orbit-api is Windows-local; 1 risk flag = Medium above)
security-reviewer N/A (no orbit-api changes)

Validation

Check Result
Lint PASS (static)
Type check PASS (static)
Tests N/A (node_modules not installed; author reports 1013/1619/620 green)
Build (api) N/A

What is good

  • Android pin is surgical, root cause in PR body.
  • from12Hour edge cases correct (12-noon, midnight).
  • Optimistic update + rollback pattern correct in both useSetSelectedCalendars hooks.
  • 14 new i18n keys in both locales, correct interpolation.
  • Zero dead code / any / console.log across 31 files.
  • All schema additions are optional and additive — no backward-compat breaks.

Recommendation

Resolve the Medium before or shortly after merge by either confirming the C# serialiser omits null for those three fields, or applying .nullable().optional(). 2-minute fix. Everything else is clean.

@thomasluizon
thomasluizon merged commit f48118d into main Jun 25, 2026
10 checks passed
@thomasluizon
thomasluizon deleted the fix/android-release-and-orbit-time-picker branch June 25, 2026 05:28
thomasluizon added a commit that referenced this pull request Jun 25, 2026
…ity) (#313)

#306 rebuilt the web time selector as a custom Orbit popover but left the
mobile picker on the native Android clock dial — mobile only got the 12h/24h
profile wiring (8 lines), not the UI. Port the web component to mobile:
scrollable hour/minute/AM-PM columns with the same to12Hour/from12Hour/parseTime
logic, violet selection pill, and the "Selecionar horário"/"Concluir" header.
is24Hour drives HOURS_24 (no period column) vs HOURS_12 + PERIODS.

Remove the now-unused @react-native-community/datetimepicker (component import,
package.json, app.json plugin, vitest alias, test mock, lockfile) and rewrite
the mobile test against the popover behavior.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant