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 @@ -37,6 +37,7 @@ import com.flipcash.app.lab.LabsScreen
import com.flipcash.app.lab.NavBarSettingsScreen
import com.flipcash.app.login.OnboardingFlowScreen
import com.flipcash.app.menu.MenuScreen
import com.flipcash.app.myaccount.UserProfileScreen
import com.flipcash.app.myaccount.MyAccountScreen
import com.flipcash.app.scanner.ScannerScreen
import com.flipcash.app.shareapp.ShareAppScreen
Expand Down Expand Up @@ -117,6 +118,7 @@ fun appEntryProvider(
annotatedEntry<AppRoute.Menu.AppSettings> { AppSettingsScreen() }
annotatedEntry<AppRoute.Menu.Lab> { LabsScreen() }
annotatedEntry<AppRoute.Menu.NavBarSettings> { NavBarSettingsScreen() }
annotatedEntry<AppRoute.Menu.UserProfile> { UserProfileScreen() }
annotatedEntry<AppRoute.Menu.MyAccount> { MyAccountScreen() }
annotatedEntry<AppRoute.Menu.BackupKey> { BackupKeyScreen() }
annotatedEntry<AppRoute.Menu.AdvancedFeatures> { AdvancedFeaturesScreen() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ sealed interface AppRoute : NavKey, Parcelable {
@Serializable
data object DeviceLogs : Menu
@Serializable
data object UserProfile : Menu
@Serializable
data object Lab : Menu
@Serializable
data object NavBarSettings : Menu, com.getcode.navigation.Sheet, com.getcode.navigation.WrapContentSheet
Expand Down
18 changes: 18 additions & 0 deletions apps/flipcash/core/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,24 @@
<string name="action_unlinkPhone">Unlink Phone</string>
<string name="action_unlinkEmail">Unlink Email</string>

<string name="title_userProfile">User Profile</string>
<string name="title_sectionPhone">Phone Number</string>
<string name="title_sectionEmail">Email Address</string>
<string name="action_addPhoneNumber">Add Phone Number</string>
<string name="action_addEmailAddress">Add Email Address</string>
<string name="subtitle_linkedForPayments">Linked for payments</string>
<string name="prompt_title_unlinkPhone">Unlink Phone Number?</string>
<string name="prompt_description_unlinkPhone">Your phone number will be removed from your profile.</string>
<string name="prompt_title_unlinkEmail">Unlink Email Address?</string>
<string name="prompt_description_unlinkEmail">Your email address will be removed from your profile.</string>
<string name="title_sectionDisplayName">Display Name</string>
<string name="title_sectionSocialAccounts">Social Accounts</string>
<string name="subtitle_noDisplayName">No display name set</string>
<string name="subtitle_noSocialAccounts">No social accounts linked</string>
<string name="prompt_title_unlinkSocialAccount">Unlink Account?</string>
<string name="prompt_description_unlinkSocialAccount">This social account will be removed from your profile.</string>
<string name="action_unlinkAccount">Unlink Account</string>

<string name="title_verificationFlow">Verify Your Phone Number And Email To Continue</string>
<string name="subtitle_verificationFlowForOnramp">This will allow you to add funds from your debit card</string>
"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MarkEmailUnread
import androidx.compose.material.icons.filled.Navigation
import androidx.compose.material.icons.filled.PhonelinkErase
import androidx.compose.material.icons.filled.ContactMail
import androidx.compose.material.icons.filled.Token
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
Expand Down Expand Up @@ -45,7 +43,6 @@ internal fun LabsScreenContent(viewModel: LabsScreenViewModel) {
val betaFlagsController = LocalFeatureFlags.current
val betaFlags by betaFlagsController.observe().collectAsStateWithLifecycle()
val navigator = LocalCodeNavigator.current
val isLoggedIn by viewModel.isLoggedIn.collectAsStateWithLifecycle()
val isStaff by viewModel.isStaff.collectAsStateWithLifecycle()

val state = rememberLazyListState()
Expand Down Expand Up @@ -137,26 +134,6 @@ internal fun LabsScreenContent(viewModel: LabsScreenViewModel) {
}
}
}

if (isLoggedIn) {
item { SectionHeader(stringResource(R.string.title_settingsSectionAccount)) }
item {
ListItem(
headline = stringResource(R.string.action_unlinkPhone),
icon = rememberVectorPainter(Icons.Default.PhonelinkErase),
) {
viewModel.unlinkPhone()
}
}
item {
ListItem(
headline = stringResource(R.string.action_unlinkEmail),
icon = rememberVectorPainter(Icons.Default.MarkEmailUnread),
) {
viewModel.unlinkEmail()
}
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,19 @@ package com.flipcash.app.lab.internal
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.flipcash.app.userflags.UserFlagsCoordinator
import com.flipcash.features.lab.R
import com.flipcash.services.controllers.ContactVerificationController
import com.flipcash.services.models.UserFlags
import com.flipcash.services.models.ContactMethod
import com.flipcash.services.models.EmailVerificationError
import com.flipcash.services.models.PhoneVerificationError
import com.flipcash.services.user.AuthState
import com.flipcash.services.user.UserManager
import com.getcode.manager.BottomBarAction
import com.getcode.manager.BottomBarManager
import com.getcode.util.resources.ResourceHelper
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class LabsScreenViewModel @Inject constructor(
private val userManager: UserManager,
userManager: UserManager,
userFlags: UserFlagsCoordinator,
private val contactController: ContactVerificationController,
private val resources: ResourceHelper,
) : ViewModel() {

val isLoggedIn = userManager
Expand All @@ -38,52 +26,4 @@ class LabsScreenViewModel @Inject constructor(

val isStaff = userFlags.resolvedFlags.map { it.isStaff.effectiveValue }
.stateIn(viewModelScope, started = SharingStarted.Eagerly, initialValue = false)

fun unlinkEmail() = viewModelScope.launch {
val email = userManager.profile?.verifiedEmailAddress
if (email == null) {
BottomBarManager.showAlert(
title = resources.getString(R.string.error_title_failedToUnlinkEmail),
message = resources.getString(R.string.error_description_failedToUnlinkEmailNonePresent)
)
return@launch
}
val method = ContactMethod.Email(email)
contactController.unlink(method)
.onFailure {
BottomBarManager.showError(
title = resources.getString(R.string.error_title_failedToUnlinkEmail),
message = resources.getString(R.string.error_description_failedToUnlinkEmail)
)
}.onSuccess {
BottomBarManager.showSuccess(
title = resources.getString(R.string.prompt_title_emailUnlinked),
message = resources.getString(R.string.prompt_description_emailUnlinked),
)
}
}

fun unlinkPhone() = viewModelScope.launch {
val phone = userManager.profile?.verifiedPhoneNumber
if (phone == null) {
BottomBarManager.showAlert(
title = resources.getString(R.string.error_title_failedToUnlinkPhone),
message = resources.getString(R.string.error_description_failedToUnlinkPhoneNonePresent)
)
return@launch
}
val method = ContactMethod.Phone(phone)
contactController.unlink(method)
.onFailure {
BottomBarManager.showError(
title = resources.getString(R.string.error_title_failedToUnlinkPhone),
message = resources.getString(R.string.error_description_failedToUnlinkPhone)
)
}.onSuccess {
BottomBarManager.showSuccess(
title = resources.getString(R.string.prompt_title_phoneUnlinked),
message = resources.getString(R.string.prompt_description_phoneUnlinked),
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,30 +77,8 @@ fun MyAccountScreen() {

LaunchedEffect(viewModel) {
viewModel.eventFlow
.filterIsInstance<MyAccountScreenViewModel.Event.ConnectPhoneClicked>()
.onEach {
val flow = AppRoute.Verification(
origin = AppRoute.Menu.MyAccount,
includePhone = true,
includeEmail = false,
linkForPayment = it.linkForPayment
)

navigator.push(flow) }
.launchIn(this)
}

LaunchedEffect(viewModel) {
viewModel.eventFlow
.filterIsInstance<MyAccountScreenViewModel.Event.OnVerifyEmailClicked>()
.onEach {
val flow = AppRoute.Verification(
origin = AppRoute.Menu.MyAccount,
includePhone = false,
includeEmail = true,
)

navigator.push(flow) }
.filterIsInstance<MyAccountScreenViewModel.Event.OnViewUserProfile>()
.onEach { navigator.push(AppRoute.Menu.UserProfile) }
.launchIn(this)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.flipcash.app.myaccount

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.flipcash.app.core.AppRoute
import com.flipcash.app.myaccount.internal.UserProfileScreenContent
import com.flipcash.app.myaccount.internal.UserProfileViewModel
import com.flipcash.core.R
import com.getcode.navigation.core.LocalCodeNavigator
import com.getcode.ui.components.AppBarDefaults
import com.getcode.ui.components.AppBarWithTitle
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

@Composable
fun UserProfileScreen() {
val navigator = LocalCodeNavigator.current
val viewModel = hiltViewModel<UserProfileViewModel>()
val state by viewModel.stateFlow.collectAsStateWithLifecycle()

Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
AppBarWithTitle(
title = {
AppBarDefaults.Title(
text = stringResource(R.string.title_userProfile),
)
},
isInModal = true,
titleAlignment = Alignment.CenterHorizontally,
leftIcon = { AppBarDefaults.UpNavigation { navigator.pop() } },
)
UserProfileScreenContent(state = state, dispatch = viewModel::dispatchEvent)
}

LaunchedEffect(viewModel) {
viewModel.eventFlow
.filterIsInstance<UserProfileViewModel.Event.NavigateToPhoneVerification>()
.onEach {
navigator.push(
AppRoute.Verification(
origin = AppRoute.Menu.UserProfile,
includePhone = true,
includeEmail = false,
linkForPayment = state.phoneLinkedForPayment,
)
)
}.launchIn(this)
}

LaunchedEffect(viewModel) {
viewModel.eventFlow
.filterIsInstance<UserProfileViewModel.Event.NavigateToEmailVerification>()
.onEach {
navigator.push(
AppRoute.Verification(
origin = AppRoute.Menu.UserProfile,
includePhone = false,
includeEmail = true,
)
)
}.launchIn(this)
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package com.flipcash.app.myaccount.internal

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Email
import androidx.compose.material.icons.filled.Phone
import androidx.compose.material.icons.filled.ContactMail
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import com.flipcash.app.menu.FullMenuItem
import com.flipcash.app.menu.StaffMenuItem
import com.flipcash.core.R as CoreR
import com.flipcash.features.myaccount.R
import com.getcode.util.resources.icons.Delete

Expand All @@ -21,20 +22,12 @@ internal data object AccessKey : FullMenuItem<MyAccountScreenViewModel.Event>()
override val action: MyAccountScreenViewModel.Event = MyAccountScreenViewModel.Event.OnAccessKeyClicked
}

internal data object VerifyEmail : FullMenuItem<MyAccountScreenViewModel.Event>() {
internal data object UserProfile : StaffMenuItem<MyAccountScreenViewModel.Event>() {
override val icon: Painter
@Composable get() = rememberVectorPainter(Icons.Default.Email)
@Composable get() = rememberVectorPainter(Icons.Default.ContactMail)
override val name: String
@Composable get() = stringResource(R.string.title_connectEmailAddress)
override val action: MyAccountScreenViewModel.Event = MyAccountScreenViewModel.Event.OnVerifyEmailClicked
}

internal data object VerifyPhone : FullMenuItem<MyAccountScreenViewModel.Event>() {
override val icon: Painter
@Composable get() = rememberVectorPainter(Icons.Default.Phone)
override val name: String
@Composable get() = stringResource(R.string.title_connectPhoneNumber)
override val action: MyAccountScreenViewModel.Event = MyAccountScreenViewModel.Event.OnVerifyPhoneClicked
@Composable get() = stringResource(CoreR.string.title_userProfile)
override val action: MyAccountScreenViewModel.Event = MyAccountScreenViewModel.Event.OnContactMethodsClicked
}

internal data object LogOut : FullMenuItem<MyAccountScreenViewModel.Event>() {
Expand Down
Loading
Loading