feat(user-profile): gate Save on a real edit and discard it on back - #1350
Merged
Conversation
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.
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.
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.
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:AppNavHostinstallsrememberViewModelStoreNavEntryDecorator, so each step's ViewModel is scoped to its nav entry and goes with the pop. NoBackHandler— 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 localUri, so it cannot be mistaken for a pick — Save stays disabled until one is made. That gate moved intoState.isChanged, so all three steps express their dirty check the same way.Two more fixes came out of this:
UserProfile.Emptywhen it cannot find the server profile — a different value, sodistinctUntilChangedlet 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.PhotoSelectionViewModel.onClearednow 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.