Skip to content

feat(user-profile): gate Save on a real edit and discard it on back - #1350

Merged
bmc08gt merged 2 commits into
code/cashfrom
feat/user-profile-discard-changes
Aug 28, 2026
Merged

feat(user-profile): gate Save on a real edit and discard it on back#1350
bmc08gt merged 2 commits into
code/cashfrom
feat/user-profile-discard-changes

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Opening Change Display Name or Change Username armed the confirm button before anything had been edited. Both steps seed the field from the stored profile, and the button was gated on the text merely being non-blank, so the first tap submitted the value that was already there.

Both view models now keep the loaded value in state as a baseline and compare the field against it. One character's difference arms the button; typing the original back in disarms it. Nodes 9553:113166 and 9553:113168.

Leaving a step throws the edit away. The back icon dispatches DiscardChanges, which puts the baseline back in the field. A gesture back lands in the same place by a different route: AppNavHost installs rememberViewModelStoreNavEntryDecorator, so each step's ViewModel is scoped to its nav entry and goes with the pop. No BackHandler — adding one would have cost predictive-back animation on these screens.

Set Profile Picture opened on an empty well with a "+", so an account that already had a picture looked like it had none. It now seeds from the stored picture and falls back to it when a pick is discarded. The seeded value is a server-side MediaItem, never a local Uri, so it cannot be mistaken for a pick — Save stays disabled until one is made. That gate moved into State.isChanged, so all three steps express their dirty check the same way.

Two more fixes came out of this:

  • The name and username fields could be clobbered mid-edit. Both seeded on every profile emission, and the 60s poll publishes UserProfile.Empty when it cannot find the server profile — a different value, so distinctUntilChanged let it through and it overwrote whatever was being typed. The field now follows the store only while it is untouched; the baseline still moves either way.
  • An abandoned photo pick left a re-encoded file in the cache with nothing to delete it. PhotoSelectionViewModel.onCleared now drops it, which covers the gesture back and the successful upload that makes the local copy redundant.

Adds 24 unit tests over the three reducers and their computed state.

The name and username steps seeded their field from the stored profile and
then enabled the confirm button on any non-blank text, so opening either one
armed Save before the user had changed anything — a tap that submitted the
value that was already there. Both now keep the loaded value as a baseline in
state and compare the field against it, so one character's difference is what
arms the button and typing the original back in disarms it. Nodes 9553:113166
and 9553:113168.

The seeding flows also lacked distinctUntilChanged, so any unrelated emission
from the profile re-seeded the field and overwrote in-progress typing.

Leaving a step throws the edit away. The back icon dispatches DiscardChanges,
which puts the baseline back in the field; a gesture back reaches the same
outcome because each step's ViewModel is scoped to its nav entry and goes with
the pop. The photo step needed more than that: an abandoned pick left a
re-encoded file in the cache with nothing to delete it, so onCleared drops it,
covering the gesture back and the successful upload alike.
@bmc08gt bmc08gt self-assigned this Aug 27, 2026
@github-actions github-actions Bot added the type: feature New functionality label Aug 27, 2026
The Set Profile Picture step opened on an empty well with a "+", so an account
that already has a picture looked like it had none. It now seeds from the stored
profile picture and falls back to it when a pick is discarded.

The seeded value is a server-side MediaItem, never a local Uri, so it cannot be
mistaken for a pick: Save stays disabled until one is made. That gate moves into
State.isChanged, matching the name and username steps.

Also stops the name and username fields being clobbered mid-edit. Both seeded the
field on every profile emission, and the 60s poll publishes UserProfile.Empty when
it cannot find the server profile — a different value, so distinctUntilChanged let
it through and it overwrote whatever was being typed. The field now follows the
store only while it is untouched; the baseline still moves either way, so the
confirm button stays honest.
@bmc08gt
bmc08gt merged commit 91be8b7 into code/cash Aug 28, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feature New functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant