Skip to content

Schedule commits and choose a timezone, as on the web - #23

Draft
simonhamp wants to merge 2 commits into
mainfrom
feat/scheduling
Draft

simonhamp wants to merge 2 commits into
mainfrom
feat/scheduling

Conversation

@simonhamp

@simonhamp simonhamp commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Brings the web's scheduled commits and timezone setting to the app.

It needs three devnull API PRs: nunomaduro/dev-null.sh#669 (timezone on /me), #670 (scheduling) and #671 (listing, editing and canceling). Until they merge, the new parts get a 404 from dev-null.sh and show devnull's error, and the timezone section stays unset.

Scheduling

The composer sheet gets the web's clock button, at the start of the counter's row. It opens "Submit at" under the field with the system date and time picker (native:date-picker, compact on iOS) in the phone's timezone, limited to the next 30 days, with the web's lines on what scheduling means and a "Scheduled commits →" link. The bar's check becomes Schedule commit and waits for a time. As on the web, a /feature or /bug draft can't be scheduled: scheduling, a command stays text, and an issue draft has no clock. A time the phone's timezone skips, like 01:30 on the night the clocks go forward, gets the web's message. Once devnull has it, the sheet closes with the web's toast and the draft is cleared. Quotes have no clock, as on the web.

"Scheduled commits →" opens a new screen, pushed in the tab it was opened from, laid out like the web's page: its intro, "Times in …", then each one with its status, time, body, poll options and image count. One still waiting has Edit and Cancel. Edit opens a sheet with its body and time, which saves both. The web has no edit, so this uses the new PUT from #671. A submitted one says what became of it and opens its commit.

Timezone

Account gets the web's Timezone section between Color and Pastéis mode: the timezone chosen (or null), the phone's, Choose timezone (a sheet with a search field, listing the timezones at the phone's offset until you type, then up to 50 matches by offset), "Use … from this phone", Unset, and Save timezone.

App\Devnull\Timezone asks NativePHP for the phone's timezone first. NativePHP/mobile-air#566 (draft) adds it to Device.GetInfo on iOS and Android. Native builds without that change don't send it, so then it reads the zone the way the C library does, from where /etc/localtime links (…/zoneinfo/Europe/Lisbon). That fallback works on the simulator. On a real iPhone it goes through /var/db/timezone/localtime, which I couldn't try, and Android has no /etc/localtime at all. When neither tells, the chosen timezone or UTC stands in. Until #566 is in combined/dev-null-app and the lock moves to it, the app always takes the fallback.

Timezone is bound once and keeps the phone's timezone for a minute before asking again. Device.GetInfo does more than read the zone (on Android it reads memory info and the WebView version), and the Notifications page asks for the zone twice a row.

What uses it, matching the web:

  • Your times show in the timezone you chose, or else the phone's, as the web falls back to the browser's. The focused commit's time is now tappable and toasts it ("9:13 PM · Oct 14, 2026 in Lisbon"), standing in for the web's hover tooltip. I didn't put the full date back under the focused commit, since bea32a3 took it out on purpose.
  • Times you schedule at, and the times on the scheduled list, are in the phone's timezone, as the web uses the browser's there.
  • Day markers on the Log and Notifications now count days in the phone's timezone, as the web counts them in the browser's. Until now they were UTC, because PHP couldn't see the phone's.

Worth checking

  • Rebased onto main 34a654d, which redesigned the composer's bar (Put Cancel and Commit in a bar at the top of the commit sheet #19, Draw the compose sheet's Cancel and Commit as a cross and a check #48) and added slash commands (Turn a new commit into a /feature or /bug issue, as the web does #22), Remotes (Bring Remotes to the app #25) and starred commits (Replace saved bookmarks with starred commits #42). Scheduling now uses the bar's check and sheetPosting, DraftsIssues::canDraftIssue() returns false while scheduling, and scheduled bodies open their links through the same openLink() as a feed. Main already takes one-character commits (Commit::MinLength is 1), and scheduling and the edit sheet use Commit::fits(), so one character can be scheduled too.
  • The timezone sheet doesn't list all ~420 timezones at once like the web's dropdown. On main's PHP binaries a frame over 512 KiB is dropped (php-bin-mobile#46 isn't in yet), and the full list on top of the Account screen went over it: the sheet just never appeared.
  • The timezone sheet and the edit sheet sit after their screen's scroll view. Before it, their rows coming and going shifted the auto-assigned node ids of the whole page, and the Account screen jumped back to the top when a timezone was picked.
  • Tests pin the phone's timezone: TestCase binds a Timezone with no localtime (so UTC unless a test's fake bridge sends a timezone in Device.GetInfo), and phoneIn('Europe/Lisbon') in tests/Pest.php puts the phone somewhere through localtime.

Testing

1,153 tests pass (php artisan test --compact) after the rebase, and Pint is clean. The simulator checks below were done before the rebase.

Checked on an iOS 27 simulator against the three API branches merged together, served through a tunnel.

  • Signed in through the tunnel as local-dev. The phone's timezone was read as Atlantic/Canary from the simulator's /etc/localtime.
  • Scheduled a commit for 21:35 in the picker: devnull stored 20:35 UTC, the toast showed and the draft was cleared. Scheduled a second for two minutes later, saw it go to "Waiting for submission", ran commits:submit-scheduled, and canceling it then got the 409 toast and a refreshed list showing "Submitted for review" with a link to the pending commit.
  • Edited the first one's body and moved it to 22:35 (stored 21:35 UTC), then canceled it.
  • Chose Asia/Tokyo on Account and saved it, then tapped a commit's time: it showed in Tokyo time. Unset, it showed in Canary time.

Not tried: a real iPhone (the /etc/localtime read goes through one more link there) or Android. The Device.GetInfo path is covered by tests with a fake bridge but hasn't run in a build yet.

Submit at Scheduled commits Edit Already submitted
Canceled Timezone sheet Timezone saved A commit's time

Screenshots live on the scheduling-screenshots branch; delete it after merging.

🤖 Generated with Claude Code

simonhamp and others added 2 commits October 21, 2026 14:49
The composer sheet gets the web's clock button. It opens "Submit at" with
the system date and time picker in the phone's timezone, and Commit becomes
Schedule commit. Scheduled commits are listed on their own screen, reached
from "Scheduled commits →" as on the web, where one still waiting can be
edited or canceled.

Account gets the web's Timezone section between Color and Pastéis mode.
NativePHP doesn't tell PHP the phone's timezone, so App\Devnull\Timezone
reads it from where /etc/localtime links, as the C library does. Day markers
now count days in the phone's timezone, and tapping the focused commit's
time says when it was made in yours.

Needs the timezone, scheduling and scheduled-commit API PRs on devnull.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NativePHP/mobile-air#566 adds the phone's timezone to Device.GetInfo on
iOS and Android. Timezone::phone() now reads it from there first and
only follows /etc/localtime when the device info doesn't carry it, as
with builds from before that change. Android gets a timezone this way,
since it has no /etc/localtime.

Device.GetInfo does more than read the zone (on Android it reads memory
info and the WebView version), and the notifications page asks for the
phone's timezone twice a row. So Timezone is now bound once and keeps
the answer for a minute before asking again.

Co-Authored-By: Claude Opus 5.5 <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