Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions apps/flipcash/core/src/main/res/drawable/ic_profile_picture.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Material Symbols Outlined "familiar_face_and_zone", 24dp/wght 400. The -960 viewBox
Google ships is translated into a 0..960 one, matching ic_biometrics. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M480,800 q-134,0,-227,-93 t-93,-227 q0,-134,93,-227 t227,-93 q134,0,227,93 t93,227 q0,134,-93,227 t-227,93 Z m0,-80 q100,0,170,-70 t70,-170 q0,-17,-2.5,-33.5 T710,414 q-15,3,-30,4.5 t-30,1.5 q-63,0,-120,-24 t-102,-70 q-28,57,-77,99 t-111,61 q3,98,72.5,166 T480,720 Z M256,394 q44,-23,67,-53.5 t45,-72.5 q-38,20,-67,52.5 T256,394 Z m95.5,154.5 Q340,537,340,520 t11.5,-28.5 Q363,480,380,480 t28.5,11.5 Q420,503,420,520 t-11.5,28.5 Q397,560,380,560 t-28.5,-11.5 Z M650,340 h12 q6,0,12,-1 -33,-45,-83.5,-72 T480,240 h-12 q-6,0,-11,1 39,45,82.5,72 T650,340 Z m-98.5,208.5 Q540,537,540,520 t11.5,-28.5 Q563,480,580,480 t28.5,11.5 Q620,503,620,520 t-11.5,28.5 Q597,560,580,560 t-28.5,-11.5 Z M40,240 v-120 q0,-33,23.5,-56.5 T120,40 h120 v80 H120 v120 H40 Z M240,920 H120 q-33,0,-56.5,-23.5 T40,840 v-120 h80 v120 h120 v80 Z m480,0 v-80 h120 v-120 h80 v120 q0,33,-23.5,56.5 T840,920 H720 Z m120,-680 v-120 H720 v-80 h120 q33,0,56.5,23.5 T920,120 v120 h-80 Z m-383,1 Z m-89,27 Z"
android:fillColor="#e8eaed"/>
</vector>
7 changes: 5 additions & 2 deletions apps/flipcash/core/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -941,7 +941,7 @@
<string name="title_usernameSelection">Enter username</string>
<string name="subtitle_usernameSelection">This is how you\'ll be uniquely identified on the platform</string>
<string name="hint_username">Username</string>
<string name="title_changeUsername">Change Username</string>
<string name="title_username">Username</string>

<!-- The "You" tab's username progress card (nodes 9536:4336 / 9537:1845). -->
<string name="title_usernameUpsell">Get a custom @username</string>
Expand Down Expand Up @@ -1026,6 +1026,9 @@
<string name="title_shareTipCode">My Flipcash Code</string>
<string name="error_title_tipCardExportFailed">Couldn\'t Export</string>
<string name="error_description_tipCardExportFailed">We were unable to export your tip code. Please try again</string>
<string name="title_changeDisplayName">Change Display Name</string>
<!-- My Account rows (node 9544:18478). Each names the thing it edits rather than the act of
editing it, so the list reads as an inventory of the profile. -->
<string name="title_displayName">Display Name</string>
<string name="title_profilePicture">Profile Picture</string>

</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,21 @@ fun MyAccountScreen() {
}.launchIn(this)
}

LaunchedEffect(viewModel) {
viewModel.eventFlow
.filterIsInstance<MyAccountScreenViewModel.Event.OnEditProfilePicture>()
.onEach {
navigator.push(
AppRoute.UpdateUserProfile(
origin = AppRoute.Menu.MyAccount,
nameSource = DisplayNameSource.MyAccount,
includeName = false,
includePhoto = true,
)
)
}.launchIn(this)
}

LaunchedEffect(viewModel) {
viewModel.eventFlow
.filterIsInstance<MyAccountScreenViewModel.Event.OnViewUserProfile>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ import com.flipcash.features.myaccount.R
* Log Out, Delete Account) moved to Advanced, and the standalone App Settings screen folded its one
* surviving toggle (Require Biometrics) in here.
*
* The row lands straight on the name step: [AppRoute.UpdateUserProfile] walks name then photo, and
* this is only ever about the name, so it asks for that step alone.
* The row lands straight on the name step: [AppRoute.UpdateUserProfile] walks name, username then
* photo, and this is only ever about the name, so it asks for that step alone.
*/
internal data object ChangeDisplayName : FullMenuItem<MyAccountScreenViewModel.Event>() {
override val icon: Painter
@Composable get() = rememberVectorPainter(Icons.Outlined.Badge)
override val name: String
@Composable get() = stringResource(CoreR.string.title_changeDisplayName)
@Composable get() = stringResource(CoreR.string.title_displayName)
override val action: MyAccountScreenViewModel.Event =
MyAccountScreenViewModel.Event.OnChangeDisplayNameClicked
}
Expand All @@ -47,11 +47,25 @@ internal data object ChangeUsername : FullMenuItem<MyAccountScreenViewModel.Even
override val icon: Painter
@Composable get() = rememberVectorPainter(Icons.Outlined.AlternateEmail)
override val name: String
@Composable get() = stringResource(CoreR.string.title_changeUsername)
@Composable get() = stringResource(CoreR.string.title_username)
override val action: MyAccountScreenViewModel.Event =
MyAccountScreenViewModel.Event.OnChangeUsernameClicked
}

/**
* Node 9544:20116. The avatar, on its own row. [AppRoute.UpdateUserProfile] walks name then username
* then photo, so this asks for the photo step alone — the same single-step edit the staff-only
* profile editor already pushes.
*/
internal data object ProfilePicture : FullMenuItem<MyAccountScreenViewModel.Event>() {
override val icon: Painter
@Composable get() = painterResource(CoreR.drawable.ic_profile_picture)
override val name: String
@Composable get() = stringResource(CoreR.string.title_profilePicture)
override val action: MyAccountScreenViewModel.Event =
MyAccountScreenViewModel.Event.OnProfilePictureClicked
}

/**
* A toggle, not a destination — the screen renders a switch in its trailing slot and routes the tap
* through a biometric prompt. Its [action] is what a row tap dispatches, same as the switch.
Expand All @@ -73,8 +87,9 @@ internal data object Blocklist : FullMenuItem<MyAccountScreenViewModel.Event>()
}

/**
* Staff/beta only: the whole profile editor — contact methods, photo, name. The name on its own is
* reachable by everyone through [ChangeDisplayName].
* Staff/beta only: the whole profile editor — contact methods, photo, name. What it adds over the
* rows above is the contact methods; the name and the photo are already reachable by everyone
* through [ChangeDisplayName] and [ProfilePicture].
*/
internal data object UserProfile : StaffMenuItem<MyAccountScreenViewModel.Event>() {
override val icon: Painter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import javax.inject.Inject
private val FullMenuList = buildList {
add(ChangeDisplayName)
add(ChangeUsername)
add(ProfilePicture)
add(RequireBiometrics)
add(Blocklist)
add(UserProfile)
Expand Down Expand Up @@ -75,6 +76,8 @@ internal class MyAccountScreenViewModel @Inject constructor(
data class OnUsernameClaimChanged(val claimed: Boolean) : Event
data object OnChangeUsernameClicked : Event
data object OnEditUsername : Event
data object OnProfilePictureClicked : Event
data object OnEditProfilePicture : Event
data object OnBlocklistClicked: Event
data object OnViewBlocklist: Event
data object OnContactMethodsClicked : Event
Expand Down Expand Up @@ -131,6 +134,12 @@ internal class MyAccountScreenViewModel @Inject constructor(
dispatchEvent(Event.OnEditUsername)
}.launchIn(viewModelScope)

eventFlow
.filterIsInstance<Event.OnProfilePictureClicked>()
.onEach {
dispatchEvent(Event.OnEditProfilePicture)
}.launchIn(viewModelScope)

eventFlow
.filterIsInstance<Event.OnBlocklistClicked>()
.onEach {
Expand Down Expand Up @@ -165,6 +174,8 @@ internal class MyAccountScreenViewModel @Inject constructor(
Event.OnEditDisplayName,
Event.OnChangeUsernameClicked,
Event.OnEditUsername,
Event.OnProfilePictureClicked,
Event.OnEditProfilePicture,
Event.OnContactMethodsClicked,
Event.OnViewUserProfile,
Event.OnBlocklistClicked,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.flipcash.app.myaccount.internal.myaccount.Blocklist
import com.flipcash.app.myaccount.internal.myaccount.ChangeDisplayName
import com.flipcash.app.myaccount.internal.myaccount.ChangeUsername
import com.flipcash.app.myaccount.internal.myaccount.MyAccountScreenViewModel
import com.flipcash.app.myaccount.internal.myaccount.ProfilePicture
import com.flipcash.app.myaccount.internal.myaccount.RequireBiometrics
import com.flipcash.app.myaccount.internal.myaccount.UserProfile
import kotlin.test.Test
Expand All @@ -19,12 +20,29 @@ class MyAccountScreenViewModelStateTest {
reduce(MyAccountScreenViewModel.Event.OnUsernameClaimChanged(claimed = true))(state)

@Test
fun `default state lists the display name, biometrics and blocklist`() {
fun `default state lists the display name, profile picture, biometrics and blocklist`() {
val state = MyAccountScreenViewModel.State()
assertEquals(listOf(ChangeDisplayName, RequireBiometrics, Blocklist), state.items)
assertEquals(
listOf(ChangeDisplayName, ProfilePicture, RequireBiometrics, Blocklist),
state.items,
)
assertFalse(state.biometricsRequired)
}

@Test
fun `the profile picture row carries no condition`() {
val noBiometrics = reduce(
MyAccountScreenViewModel.Event.OnBiometricsSettingChanged(
required = false,
supported = false,
available = false,
)
)(MyAccountScreenViewModel.State())

assertTrue(noBiometrics.items.any { it is ProfilePicture })
assertTrue(claimed(noBiometrics).items.any { it is ProfilePicture })
}

@Test
fun `changing the username is offered only once a handle is claimed`() {
val unclaimed = MyAccountScreenViewModel.State()
Expand All @@ -35,7 +53,7 @@ class MyAccountScreenViewModelStateTest {

assertTrue(withHandle.usernameClaimed)
assertEquals(
listOf(ChangeDisplayName, ChangeUsername, RequireBiometrics, Blocklist),
listOf(ChangeDisplayName, ChangeUsername, ProfilePicture, RequireBiometrics, Blocklist),
withHandle.items,
)
}
Expand Down Expand Up @@ -232,6 +250,8 @@ class MyAccountScreenViewModelStateTest {
MyAccountScreenViewModel.Event.OnEditDisplayName,
MyAccountScreenViewModel.Event.OnChangeUsernameClicked,
MyAccountScreenViewModel.Event.OnEditUsername,
MyAccountScreenViewModel.Event.OnProfilePictureClicked,
MyAccountScreenViewModel.Event.OnEditProfilePicture,
MyAccountScreenViewModel.Event.OnContactMethodsClicked,
MyAccountScreenViewModel.Event.OnViewUserProfile,
MyAccountScreenViewModel.Event.OnBlocklistClicked,
Expand Down
Loading