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
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ fun MyAccountScreen() {
MyAccountScreen(viewModel)
}

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

LaunchedEffect(viewModel) {
viewModel.eventFlow
.filterIsInstance<MyAccountScreenViewModel.Event.OnViewUserProfile>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package com.flipcash.app.myaccount.internal.myaccount

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ContactMail
import androidx.compose.material.icons.outlined.Badge
import androidx.compose.material.icons.outlined.Block
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import com.flipcash.app.core.AppRoute
import com.flipcash.app.menu.FullMenuItem
import com.flipcash.app.menu.StaffMenuItem
import com.flipcash.core.R as CoreR
Expand All @@ -18,9 +20,21 @@ 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.
*
* Require Biometrics is 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.
* 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.
*/
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)
override val action: MyAccountScreenViewModel.Event =
MyAccountScreenViewModel.Event.OnChangeDisplayNameClicked
}

/**
* 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.
*/
internal data object RequireBiometrics : FullMenuItem<MyAccountScreenViewModel.Event>() {
override val icon: Painter
Expand All @@ -39,8 +53,8 @@ internal data object Blocklist : FullMenuItem<MyAccountScreenViewModel.Event>()
}

/**
* Staff/beta only. Promoting this to an always-visible "Change Display Name" row is its own change;
* until then it stays where it was — behind the staff gate.
* Staff/beta only: the whole profile editor — contact methods, photo, name. The name on its own is
* reachable by everyone through [ChangeDisplayName].
*/
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 @@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.onEach
import javax.inject.Inject

private val FullMenuList = buildList {
add(ChangeDisplayName)
add(RequireBiometrics)
add(Blocklist)
add(UserProfile)
Expand Down Expand Up @@ -58,6 +59,8 @@ internal class MyAccountScreenViewModel @Inject constructor(
) : Event
/** Dispatched only after the screen's biometric prompt succeeds. */
data object OnBiometricsToggled : Event
data object OnChangeDisplayNameClicked : Event
data object OnEditDisplayName : Event
data object OnBlocklistClicked: Event
data object OnViewBlocklist: Event
data object OnContactMethodsClicked : Event
Expand Down Expand Up @@ -95,6 +98,12 @@ internal class MyAccountScreenViewModel @Inject constructor(
)
}.launchIn(viewModelScope)

eventFlow
.filterIsInstance<Event.OnChangeDisplayNameClicked>()
.onEach {
dispatchEvent(Event.OnEditDisplayName)
}.launchIn(viewModelScope)

eventFlow
.filterIsInstance<Event.OnBlocklistClicked>()
.onEach {
Expand All @@ -120,6 +129,8 @@ internal class MyAccountScreenViewModel @Inject constructor(
val updateStateForEvent: (Event) -> ((State) -> State) = { event ->
when (event) {
Event.OnBiometricsToggled,
Event.OnChangeDisplayNameClicked,
Event.OnEditDisplayName,
Event.OnContactMethodsClicked,
Event.OnViewUserProfile,
Event.OnBlocklistClicked,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.flipcash.app.myaccount.internal

import com.flipcash.app.myaccount.internal.myaccount.Blocklist
import com.flipcash.app.myaccount.internal.myaccount.ChangeDisplayName
import com.flipcash.app.myaccount.internal.myaccount.MyAccountScreenViewModel
import com.flipcash.app.myaccount.internal.myaccount.RequireBiometrics
import com.flipcash.app.myaccount.internal.myaccount.UserProfile
Expand All @@ -14,12 +15,28 @@ class MyAccountScreenViewModelStateTest {
private val reduce = MyAccountScreenViewModel.Companion.updateStateForEvent

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

@Test
fun `changing the display name is offered without the beta unlock`() {
val locked = MyAccountScreenViewModel.State()
assertTrue(locked.items.any { it is ChangeDisplayName })

val noBiometrics = reduce(
MyAccountScreenViewModel.Event.OnBiometricsSettingChanged(
required = false,
supported = false,
available = false,
)
)(locked)

assertTrue(noBiometrics.items.any { it is ChangeDisplayName })
}

@Test
fun `unsupported biometrics hides the row`() {
val updated = reduce(
Expand Down Expand Up @@ -142,6 +159,8 @@ class MyAccountScreenViewModelStateTest {
val state = MyAccountScreenViewModel.State(biometricsRequired = true)
val noOpEvents = listOf(
MyAccountScreenViewModel.Event.OnBiometricsToggled,
MyAccountScreenViewModel.Event.OnChangeDisplayNameClicked,
MyAccountScreenViewModel.Event.OnEditDisplayName,
MyAccountScreenViewModel.Event.OnContactMethodsClicked,
MyAccountScreenViewModel.Event.OnViewUserProfile,
MyAccountScreenViewModel.Event.OnBlocklistClicked,
Expand Down
Loading