Repository navigation
Conversation
The built-in toast is a plain system toast. ToastKit draws native overlays on both platforms with variants and action buttons, which the outbox work needs for Retry and Edit. Every Dialog::toast call now goes through App\Devnull\Toasts, which sets the position, font and durations in one place. Confirmations are neutral and failures are errors. Dialog::alert is unchanged. The plugin is free, so it also works with composer.free.json. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Starring went through a synchronous request on the UI thread, and the composer and reply bar waited on devnull before closing. Now every one of these writes goes into a pending_writes table first, the screen shows it straight away, and the request goes from a background task. Nothing on the tap path touches the network. The answer comes back as a shared AsyncTask event, so whichever screen is showing handles it, not only the one the write came from. On success the row goes and the screen and ResponseCache take devnull's answer. On failure the screen and cache go back and an error toast says why: - A star flips back, and the toast offers Retry. - A commit or reply leaves the screen. Its text goes back into its stash and the failed row keeps the photos and poll. The screen that owns the composer opens it again with everything. Elsewhere the toast offers Edit, which opens the composer or the commit it was for. - A Remotes message stays in the thread as rejected with its inline Retry, and the toast has Retry too. Staged and rejected messages now survive leaving the thread. New commits and replies show as a dimmed "staged" row at the top of the Log or under their parent until the real one replaces them. When the Log opens, rows left over from a killed app are handled. Stars and messages are sent again since devnull can't do them twice (messages dedupe on client_id). Commits are not: the commit API has no idempotency key, so they're marked failed with a toast and Edit. A commit whose task times out says devnull may have it for the same reason. Signing out empties the outbox. Tests use LazilyRefreshDatabase now that screens touch the database. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
If the sheet was swiped away before devnull opened the issue, the toast used to say "Opened issue #N." with no way to get to it. It now has a View issue button that opens the issue in the in-app browser, the same as the button in the sheet. Only http and https links are opened. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mmit With no connection, devnull's failure lands while the sheet the commit was sent from is still sliding away. The sheet was opened again on the draft right then, and when iOS reported the old sheet gone, commitSheetClosed() reset the sheet and closed the new one too. Because the reopen had reported success, the toast had no Edit button, so the draft was only reachable through the stash. Closing the sheet from here now marks it as closing until iOS says it has gone. A failure that lands in that window is held, the toast keeps Edit, and the sheet opens on the draft once commitSheetClosed() runs. The toast only drops Edit when the sheet really opened. Quotes and replies written in the sheet go through the same path. The reply bar has no sheet, so it isn't affected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The last fix held the reopen until the sheet's @Dismiss arrived. That works on iOS, where the sheet reports every close. On Android the sheet only sends @Dismiss when the user drags it away or presses back, never when PHP closes it. So sheetClosing stayed true for good and every failed commit after that waited for an event that never came. Android doesn't have the race anyway. It keeps the sheet drawn through its hide animation, so showing it again right away just shows it. The closing flag is now only set on iOS (System::isIos()), so on Android the sheet opens again at once and the toast has no Edit. Nothing else waits on @Dismiss after a close from PHP. Cleanup on Android works as it did on main: the next open resets the sheet, and photos sent with a commit leave the sheet before it closes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ToastKit 1.4.0 puts its Android toasts in the activity's decor view, so any toast shown while a bottom sheet is open lands behind the sheet. The commit sheet's failure toasts were hidden that way. The fork's fix/android-toasts-above-sheets branch hosts them in their own windows instead. It's required as 1.4.0 until the fix is released upstream. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Starring, committing, replying and sending a Remotes message now feel instant. Each tap writes a row to a new
pending_writestable in the app's SQLite, updates the screen and the response cache straight away, and sends the request from a background task. Before this, a star waited on a synchronous request on the UI thread.When devnull turns a write down, or the request fails or times out, the change is undone and a toast says why in devnull's words.
If the app is killed mid-send, stars and messages are resent on the next launch. Messages de-dupe on their client id. Commits are not resent, because the commit API has no idempotency key and a resend could post twice. They come back as failed with Edit instead.
Every
Dialog::toastis now a ToastKit toast (victorycodedev/toastkit1.4, free), through a smallApp\Devnull\Toastshelper. "Opened issue #N." gets a View issue button. Alerts are unchanged.Things to look at:
ToastKit comes from Simon's fork (
dev-fix/android-toasts-above-sheets as 1.4.0) until Draw Android toasts above sheets and dialogs victorycodedev/toastkit#5 is released. 1.4.0 draws Android toasts behind bottom sheets and dialogs, so a failed commit's error toast was hidden under the reopened composer. Switch back to the release once it's out.A failed commit reopens the composer once the old sheet has gone. iOS reports that close, so the reopen waits for it. Android's bottom sheet only fires
@dismisswhen the user drags it down or presses back, so on Android it reopens straight away. Android's sheet also covers the error toast once it's back up.Sending from the reply bar no longer jumps to the new reply. It shows under the parent as staged, then becomes the real reply.
If a commit lands while you're on another screen, the Log shows it in the new commits pill rather than at the top.
Composer server errors now show as a toast with the composer reopened, not inline in the sheet. Photo and camera errors still show inline.
ToastKit 1.4.0 came out on 21 October. I registered its service provider by hand because
native:plugin:registercouldn't editNativeServiceProvider.Tests now use
LazilyRefreshDatabasesince screens touch the database.Screenshots
Taken on an iOS 26.2 simulator and an Android 15 emulator, signed in against a local devnull API. The failure shots were taken with the API stopped.
Toast
Deleting a commit. Before is the old Dialog toast at the bottom, after is the ToastKit toast at the top.
Star while devnull is unreachable
Before shows the error inline on the commit page. After, the star goes back and the toast offers Retry.
Commit on its way
Taken just after tapping Post, with the local API slowed down. Before, the sheet waits for the answer. After, the sheet is gone and the commit sits dimmed at the top of the Log as staged.
Commit while devnull is unreachable
Before shows the error inline in the sheet. After, the composer opens again with the text. On iOS the toast shows over it with Edit. On Android the reopened sheet covers the toast.
Remotes message while devnull is unreachable
Both show the message as rejected with retry. After adds a toast with Retry.
Tests: 1226 passed on main, 1234 passed here. New coverage is in
tests/Feature/Screens/OutboxTest.php.🤖 Generated with Claude Code