feat(register): Phase 3a — UIAA dummy flow for self-hosted servers#115
Open
TigerInYourDream wants to merge 23 commits intofix/registerfrom
Open
feat(register): Phase 3a — UIAA dummy flow for self-hosted servers#115TigerInYourDream wants to merge 23 commits intofix/registerfrom
TigerInYourDream wants to merge 23 commits intofix/registerfrom
Conversation
Align RegisterScreen's outer shell with origin/main LoginScreen structure: set_type_default + SolidView base, Overlay root with centered alignment, ScrollYView with hidden scrollbar, RoundedView card wrapper with 50/50 margin, inner column with spacing 15.0. Logo references the shared mod.widgets.IMG_APP_LOGO token (registered by login_screen.rs script_mod which runs first in app.rs:1549).
Retroactively commit the planning documents that guided the Phase 1+2 implementation already merged into this branch. These were written during PR#114 development but never checked in.
Add RegistrationSubmitted / RegistrationSuccess / RegistrationFailed(String) variants used by the upcoming UIAA register flow. No behavior change yet — these are dispatched by the sliding_sync handler (Task 5) and consumed by RegisterScreen (Task 6) and app.rs (Task 7).
validate_localpart() enforces Matrix historical localpart grammar (lowercase alnum + ._=-/), length 1-255. validate_passwords_match() checks both fields non-empty and equal. Seven unit tests cover empty / mismatch / long / uppercase / happy paths. Used by Task 6 RegistrationForm submit validation.
UiaaStage is the extensibility seam for future stages (Terms, Token, Email, etc.). Each stage decides via auto_submit() whether it can advance without user input (dummy case — returns Some(AuthData)) or needs UI (returns None — future stages). DummyStage covers the Palpo / default Synapse case. Two unit tests verify dummy auto-submit and stage type string compliance with Matrix spec.
…aps.base_url Previously the stale-cache guard compared the current normalized input against caps.base_url, which .well-known/matrix/client may rewrite (trailing slash, federation host). This false-triggered the 'homeserver changed' branch on every well-known-delegated server, silently hiding the registration form and the error label inside it — making it look like the app had navigated away. Fix by tracking the user-intent URL (last_discovery_input_url) that produced the discovery and comparing against that. Also stop hiding the form on mismatch — only clear the cache and show the error, since form_error_label lives inside the form and must stay visible. Removes diagnostic logging added in the previous commit.
Drop out-of-order discovery responses by echoing requested_url back with CapabilitiesDiscovered / DiscoveryFailed. RegisterScreen compares the response's requested_url against last_discovery_input_url (which tracks what the user most recently asked about) and skips stale actions. Without this, rapid Next-A → Next-B where A returns late would overwrite B's capabilities in the UI and route a subsequent register request to A.
When the user clicks Next for a different homeserver, clear the old last_discovery cache and hide the registration_form before the new probe returns. Prevents a submit click landing between Next and the response from firing a register request against the previous server. Combined with the requested_url echo from the previous commit, the race window is closed on both sides: the late response is dropped, and the stale cache it would have resolved to is gone.
The register request is already in login_sender's queue by the time the user might reach for Back, and we don't have a cancellation path. If the POST succeeds after navigation, LoginSuccess auto-logs the user into an account they thought they were walking away from. Swallow Back clicks during registration_pending; the terminal Success or Failed action will clear the flag, at which point Back works again.
Clear all form inputs + homeserver_input + last_discovery state the moment RegistrationSuccess fires. Passwords are cleared first so no credential text lingers in widget memory. The registration_form is hidden, but status_label keeps 'Account created! Loading...' as bridging feedback during the ~100-200ms until LoginSuccess lands. On LoginAction::LoginSuccess (dispatched after the sync service finishes building), hide status_area and clear status_label so the next entry into this screen is a completely fresh state — required because the same RegisterScreen widget instance is reused when the user logs out and taps 'Sign up here' again.
…ccess Between RegisterAction::RegistrationSuccess and LoginAction::LoginSuccess the sync service is still being built. If SyncService::build() fails there (network blip, token validation race), the login loop emits LoginAction::LoginFailure — but app.rs only acts on that failure when the app is NOT in LoggedOut state, and we haven't reached LoginSuccess yet. Previously RegisterScreen only handled LoginSuccess, so the screen stayed stuck on 'Account created! Loading your account...' forever. Add an awaiting_sync_startup flag set on RegistrationSuccess and cleared on either terminal action. When gated by the flag, a LoginFailure replaces the loading text with a recovery message pointing the user at the login screen — the account does exist on the server, so framing this as a registration failure would be wrong.
- Shrink RegisterAction: box HsCapabilities so enum size tracks the small variants instead of UiaaInfo + providers + strings. - Gate Next click on registration_pending / awaiting_sync_startup via a pure helper so rapid clicks during submit or post-register sync startup don't fire a new probe. - Modal coherence: suppress LoginScreen's failure modal while the register flow is active, with a ClearFailureState action so the modal closes when RegisterScreen itself catches the LoginFailure. - Render form_error_label on registration failures (was calling .view() on a Label, silently no-op on set_visible); also clear the text on clear_form_error so stale messages don't linger. - Show 'Checking...' on next_button during slow .well-known probes so the click is visibly acknowledged even on servers where discovery takes 3-5s. - Graceful matrix_worker_task shutdown suppresses the spurious 'ended unexpectedly' log during logout / account switch. - Remove redundant M_FORBIDDEN arm in discover_homeserver_capabilities (the else branch produces the same tuple). Adds pure-function tests for can_start_capability_discovery, should_show_login_failure_modal, and worker_shutdown_is_unexpected.
6 tasks
6 tasks
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.
Summary
Phase 3a eliminates the "Phase 3 will handle the form" placeholder on UIAA servers. Adds an inline registration form + single-stage
m.login.dummyhandler so a robrix2 user can create a Matrix account on a UIAA server (e.g. local Palpo athttp://192.168.1.58:8128) end-to-end and be auto-logged in, without leaving the app.Stacked on #114 (
fix/register). When #114 lands, this PR auto-rebases onto main.What landed
mode() == Uiaa; hidden in MAS / Disabled pathsMatrixRequest::RegisterViaUiaa) that repackages the orphanLoginRequest::Registerbranch atsrc/sliding_sync.rs:435-501— first POST gets 401+session, second POST sendsAuthData::Dummy(session), 200 returns access_tokenRegisterAction::RegistrationSuccess(early bridging feedback) thenLoginAction::LoginSuccess(app.rs swaps to main UI after sync service builds)UiaaStagetrait +DummyStageas the extensibility seam for Terms / RegistrationToken stages in future phasesvalidate_localpart(lowercase +._=-/),validate_passwords_match(empty + equality) with 7 unit testsCorrectness fixes discovered during review (Codex rounds 1 & 2)
requested_urlwithCapabilitiesDiscovered/DiscoveryFailed, compare againstlast_discovery_input_urllast_discovery+ hide form before firing a fresh proberegistration_pendinggate so users don't get auto-logged-in after navigating awaycaps.base_url—.well-knownrewrites URLs, so strict string compare onbase_urlfalse-triggers on every well-known-delegated serverawaiting_sync_startupflag gates aLoginFailurearm on RegisterScreen;app.rscan't recover because state is stillLoggedOutsuppress_login_failure_modalon LoginScreen +ClearFailureStateaction prevent LoginScreen's modal from stacking on top of RegisterScreen's recovery textform_error_labelvisibility — was calling.view()on a Label (silently no-op), switched to.label()accessorUX polish
.well-knownprobes (matrix.org can take 3-5s)Architecture notes
Cargo.toml. Reusesregistration_request()helper,finalize_authenticated_client(), and thelogin_senderchannelRegisterScreen, each mapped to a specific UX / race-condition concern (no speculative state)RegisterActionenum so tiny variants don't pay for UiaaInfo + SSO providersScope (explicitly deferred)
Phase 3a is MAS +
m.login.dummy-only — Synapse default requiresm.login.terms, matrix.org requires reCAPTCHA, closed Synapses requirem.login.registration_token. See "Intentionally Deferred" section of plan.m.login.termsm.login.registration_token/login/register/available)Not element-desktop parity
This PR brings us to ~55-80% of real-world Matrix servers (Palpo/Dendrite/Conduit default + MAS-delegated). It is not feature-equivalent to element-web's
Registration.tsx— see scope table above.Test plan
cargo test --lib register— 21 passing (validators + uiaa + register_screen pure-fn tests)cargo test --lib login::login_screen— 3 passing (modal suppression pure-fn tests)cargo test --lib sliding_sync— 7 passing (includesworker_shutdown_is_unexpected)cargo clippy --lib --no-deps -- -W clippy::all— 0 new warnings on touched filesM_USER_IN_USEerror renders inform_error_labelafter visibility fix🤖 Generated with Claude Code