fix(consent): show consent once, and recover from a failed upload - #671
Open
Zeroupper wants to merge 4 commits into
Open
fix(consent): show consent once, and recover from a failed upload#671Zeroupper wants to merge 4 commits into
Zeroupper wants to merge 4 commits into
Conversation
…load Informed consent was shown twice after accepting it: the page was pushed with context.push(), and go_router keeps an imperative push in its stored navigation - so configuring the study, which refreshes the router, replayed the push and put consent back on top of the app. The page is now rendered inline by CarpAppShell from a ConsentStatus the view model owns, so there is no route left to replay. Deploying the study moves to HomePage, the first page shown once consent is in place. Accepting is also atomic now. The local "accepted" flag used to be set before the upload, so a failed upload left the user on a signed document with no error and nothing to do - carp_webservices retries a POST for about 11 minutes before surfacing anything. The upload now has to succeed first, is capped at 20s, cannot be started twice, and on failure the user is told and leaves the study rather than being stranded.
Consent is the third onboarding precondition, next to authentication and having a study - but it was the only one not in the redirect, which even had the gap to show for it: the steps ran 1, 2, 4. It is step 3 now. A redirect replaces the location rather than pushing it, so the original bug - a stored push replayed by every router refresh - cannot happen, and consent stays a real route: no consent gate in the shell's build, no local Navigator to absorb RPUITask's pops, and the page is reachable by location like every other page. Resolving it is a cached Future rather than a hand-maintained status: the redirect awaits it on every navigation and it resolves once, and leaveStudy() clears it with the rest of the view models.
Zeroupper
force-pushed
the
feat/consent-status-and-error-handling
branch
2 times, most recently
from
August 31, 2026 10:56
e8136b5 to
285bb0c
Compare
RP 3.2.0 stops RPUITask popping its own route when it is the only one in the stack - required now that consent is a redirect target. Also fixes the two consent tests failing on CI against RP 3.0.0.
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.
The bug
Informed consent was shown twice after accepting it — the user signed, and the document came straight back on top of the app.
The page was pushed with
context.push(), and go_router keeps an imperative push in its stored navigation. Configuring the study refreshes the router, the stored location is re-parsed, and the push is replayed. Onlycontext.go()replaces it; popping doesn't clear it.The fix
CarpAppShellrenders the page inline from aConsentStatusthe view model owns, so there is no route left to replay:InformedConsentViewModelownsresolving/needsSigning/given/failed, andresolve()settles it.ListenableBuilderover that status: spinner, document, app, or error page.HomePage— the first page shown once consent is in place — so the shell only gates.This also removes a
Futurefired fromaddPostFrameCallbackininitState.Failed uploads
Pulling the network while signing froze the app on the signed document: no error, no way out.
The local "accepted" flag was set before the upload, so a failure was invisible, and
carp_webservicesretries a POST 15 times with backoff — about 11 minutes of silence.RPUITaskalso keeps DONE enabled during the upload, so taps stacked more uploads.Accepting is atomic now:
Tests
test/informed_consent_page_test.dart— signing swaps consent for the app, and refreshing the router does not bring it back (the original bug).test/informed_consent_upload_failure_test.dart— a failed upload is recorded once, tells the user, and leaves the study.Separate files on purpose: research_package's global
blocTaskcarries a finished task between tests in one file.Not verified on hardware yet — the failure path (kill the network mid-upload → dialog → back to invitations) still wants a device run.