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.messenger.ChatFlowScreen
import com.flipcash.app.discovery.TokenDiscoveryScreen
import com.flipcash.app.internal.ui.navigation.decorators.rememberNavMessagingEntryDecorator
import com.flipcash.app.lab.LabsScreen
import com.flipcash.app.login.AccountSelectionScreen
import com.flipcash.app.login.OnboardingFlowScreen
import com.flipcash.app.menu.MenuScreen
import com.flipcash.app.myaccount.BlocklistScreen
Expand Down Expand Up @@ -148,6 +149,7 @@ fun appEntryProvider(
annotatedEntry<AppRoute.Menu.BackupKey> { BackupKeyScreen() }
annotatedEntry<AppRoute.Menu.AdvancedFeatures> { AdvancedFeaturesScreen() }
annotatedEntry<AppRoute.Menu.DeviceLogs> { DeviceLogsScreen() }
annotatedEntry<AppRoute.Menu.AccountSelection> { AccountSelectionScreen() }

annotatedEntry<AppRoute.UserFlags> { UserFlagsScreen() }

Expand Down
3 changes: 3 additions & 0 deletions apps/flipcash/app/src/main/res/xml/backup_rules.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@
<exclude
domain="file"
path="datastore/release-stage.preferences_pb" />
<exclude
domain="file"
path="datastore/credentials.preferences_pb" />
</full-backup-content>
6 changes: 6 additions & 0 deletions apps/flipcash/app/src/main/res/xml/data_extraction_rules.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
<exclude
domain="file"
path="datastore/release-stage.preferences_pb" />
<exclude
domain="file"
path="datastore/credentials.preferences_pb" />
</cloud-backup>
<device-transfer>
<exclude
Expand All @@ -21,5 +24,8 @@
<exclude
domain="file"
path="datastore/release-stage.preferences_pb" />
<exclude
domain="file"
path="datastore/credentials.preferences_pb" />
</device-transfer>
</data-extraction-rules>
2 changes: 0 additions & 2 deletions apps/flipcash/core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ dependencies {
implementation(libs.firebase.messaging)

implementation(libs.androidx.localbroadcastmanager)
implementation(libs.androidx.credentials)
implementation(libs.androidx.credentials.play.auth)
implementation(libs.androidx.datastore)
implementation(libs.compose.material3)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ sealed interface AppRoute : NavKey, Parcelable {
@Serializable
data object DeviceLogs : Menu
@Serializable
data object AccountSelection : Menu
@Serializable
data object UserProfile : Menu
@Serializable
data class Lab(val onboarding: Boolean = false) : Menu
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ sealed interface OnboardingStep : FlowStep, Parcelable {
@Serializable
data object SeedInput : OnboardingStep

@Parcelize
@Serializable
data object AccountSelection : OnboardingStep

@Parcelize
@Serializable
data object AccessKey : OnboardingStep
Expand Down
3 changes: 0 additions & 3 deletions apps/flipcash/core/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,6 @@

<string name="subtitle_validAddress">Valid address</string>

<string name="error_title_selectCredential">Something went wrong</string>
<string name="error_description_selectCredential">Failed to restore selected account. Please try again</string>

<string name="permissions_description_camera">Your camera is used to grab Digital Cash. Please allow access to the camera to proceed</string>
<string name="permissions_title_push">Push Notifications Required</string>
<string name="permissions_description_push">Push notifications are used to update you on tips and messages</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,6 @@ fun AdvancedFeaturesScreen() {
.launchIn(this)
}

LaunchedEffect(viewModel) {
viewModel.eventFlow
.filterIsInstance<AdvancedFeaturesScreenViewModel.Event.OnSwitchAccountTo>()
.onEach { navigator.hide() }
.launchIn(this)
}

LaunchedEffect(viewModel) {
viewModel.eventFlow
.filterIsInstance<AdvancedFeaturesScreenViewModel.Event.OnLoggedOutCompletely>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ 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.featureflags.FeatureFlag
import com.flipcash.app.menu.FullMenuItem
import com.flipcash.app.menu.StaffMenuItem
import com.flipcash.core.R
Expand Down Expand Up @@ -56,6 +55,20 @@ internal data object BetaFlags : FullMenuItem<AdvancedFeaturesScreenViewModel.Ev
AdvancedFeaturesScreenViewModel.Event.OpenScreen(AppRoute.Menu.Lab())
}

/**
* Staff/beta only, as it was before the account list replaced Google's Password Manager behind it.
* The switcher is still a beta tool, so it keeps the beta badge [StaffMenuItem] carries and sits
* next to the other one rather than on the You tab.
*/
internal data object SwitchAccount : StaffMenuItem<AdvancedFeaturesScreenViewModel.Event>() {
override val icon: Painter
@Composable get() = painterResource(R.drawable.ic_menu_switchaccounts)
override val name: String
@Composable get() = stringResource(R.string.title_switchAccounts)
override val action: AdvancedFeaturesScreenViewModel.Event =
AdvancedFeaturesScreenViewModel.Event.OpenScreen(AppRoute.Menu.AccountSelection)
}

internal data object LogOut : FullMenuItem<AdvancedFeaturesScreenViewModel.Event>() {
override val icon: Painter
@Composable get() = painterResource(R.drawable.ic_menu_logout)
Expand All @@ -73,18 +86,3 @@ internal data object DeleteAccount : FullMenuItem<AdvancedFeaturesScreenViewMode
override val action: AdvancedFeaturesScreenViewModel.Event =
AdvancedFeaturesScreenViewModel.Event.OnDeleteAccountClicked
}

/**
* Staff/beta only, and dead without Google's Password Manager behind it —
* `PassphraseCredentialManager.selectCredential()` refuses outright when the flag is off. Sits here
* rather than on the You tab: it's a beta tool, next to the other one.
*/
internal data object SwitchAccount : StaffMenuItem<AdvancedFeaturesScreenViewModel.Event>() {
override val icon: Painter
@Composable get() = painterResource(R.drawable.ic_menu_switchaccounts)
override val name: String
@Composable get() = stringResource(R.string.title_switchAccounts)
override val action: AdvancedFeaturesScreenViewModel.Event =
AdvancedFeaturesScreenViewModel.Event.OnSwitchAccountsClicked
override val featureFlag: FeatureFlag<*> = FeatureFlag.CredentialManager
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ package com.flipcash.app.advanced.internal
import androidx.lifecycle.viewModelScope
import com.flipcash.app.auth.AuthManager
import com.flipcash.app.core.AppRoute
import com.flipcash.app.core.extensions.onResult
import com.flipcash.app.featureflags.BetaFeature
import com.flipcash.app.featureflags.FeatureFlagController
import com.flipcash.app.menu.MenuItem
import com.flipcash.app.menu.StaffMenuItem
import com.flipcash.app.userflags.UserFlagsCoordinator
import com.getcode.opencode.managers.MnemonicManager
import com.flipcash.core.R
import com.flipcash.libs.coroutines.DispatcherProvider
import com.getcode.manager.BottomBarAction
Expand All @@ -26,7 +24,7 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import javax.inject.Inject

private val FullMenuList = buildList {
private val FullMenuList: List<MenuItem<AdvancedFeaturesScreenViewModel.Event>> = buildList {
add(AccessKey)
add(BetaFlags)
add(DeviceLogs)
Expand All @@ -42,7 +40,6 @@ internal class AdvancedFeaturesScreenViewModel @Inject constructor(
userFlags: UserFlagsCoordinator,
resources: ResourceHelper,
authManager: AuthManager,
mnemonicManager: MnemonicManager,
dispatchers: DispatcherProvider,
) : BaseViewModel<AdvancedFeaturesScreenViewModel.State, AdvancedFeaturesScreenViewModel.Event>(
initialState = State(),
Expand All @@ -55,7 +52,7 @@ internal class AdvancedFeaturesScreenViewModel @Inject constructor(
// Default hides staff-only AND flag-gated items until the real state loads, so a beta-gated
// row never flashes before its flag resolves.
val items: List<MenuItem<Event>> =
FullMenuList.filterNot { it is StaffMenuItem || it.featureFlag != null },
FullMenuList.filterNot { it is StaffMenuItem<*> || it.featureFlag != null },
)

sealed interface Event {
Expand All @@ -64,8 +61,6 @@ internal class AdvancedFeaturesScreenViewModel @Inject constructor(
val flags: List<BetaFeature> = emptyList(),
) : Event
data class OpenScreen(val screen: AppRoute) : Event
data object OnSwitchAccountsClicked : Event
data class OnSwitchAccountTo(val entropy: String) : Event
data object OpenBillPlayground : Event
data object OnAccessKeyClicked : Event
data object OnViewAccessKey : Event
Expand All @@ -84,24 +79,6 @@ internal class AdvancedFeaturesScreenViewModel @Inject constructor(
dispatchEvent(Event.OnBetaFeaturesUnlocked(override || isStaff, flags))
}.launchIn(viewModelScope)

// Hands off to Google's Password Manager to pick another access key, then re-logs in as it.
eventFlow
.filterIsInstance<Event.OnSwitchAccountsClicked>()
.map {
authManager.selectAccount()
.fold(
onSuccess = {
authManager.logoutAndSwitchAccount(
mnemonicManager.getEncodedBase64(it)
)
},
onFailure = { Result.failure(it) }
)
}.onResult(
onError = { },
onSuccess = { dispatchEvent(Event.OnSwitchAccountTo(it)) }
).launchIn(viewModelScope)

eventFlow
.filterIsInstance<Event.OnAccessKeyClicked>()
.onEach {
Expand Down Expand Up @@ -179,7 +156,7 @@ internal class AdvancedFeaturesScreenViewModel @Inject constructor(
unlocked: Boolean,
flags: List<BetaFeature>,
): List<MenuItem<Event>> = FullMenuList
.filter { it !is StaffMenuItem || unlocked }
.filter { it !is StaffMenuItem<*> || unlocked }
.filter { item ->
val flag = item.featureFlag ?: return@filter true
flags.find { it.flag.key == flag.key }?.enabled == true
Expand All @@ -196,8 +173,6 @@ internal class AdvancedFeaturesScreenViewModel @Inject constructor(
}

is Event.OpenScreen,
Event.OnSwitchAccountsClicked,
is Event.OnSwitchAccountTo,
Event.OpenBillPlayground,
Event.OnAccessKeyClicked,
Event.OnViewAccessKey,
Expand Down
1 change: 1 addition & 0 deletions apps/flipcash/features/login/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ dependencies {
implementation(project(":libs:datetime"))
implementation(project(":libs:messaging"))
implementation(project(":libs:permissions:bindings"))
implementation(project(":services:opencode"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.flipcash.app.login

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
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.login.internal.accounts.AccountSelectionContent
import com.flipcash.app.login.internal.accounts.AccountSelectionViewModel
import com.flipcash.features.login.R
import com.getcode.navigation.core.LocalCodeNavigator
import com.getcode.ui.components.AppBarWithTitle

/**
* The logged-in switcher. Selecting a row logs out and parks the entropy; `App.kt` picks it up
* once the logout lands and restarts onboarding with it.
*
* The "Enter a Different Access Key" footer is hidden here: from inside the app there is no
* onboarding flow to fall through to, and a logged-in user reaching for a new access key is
* logging out, which the menu already offers. That makes the back control the only way out, so
* the bar is not optional — a Block Store read that returns nothing would otherwise leave a
* signed-in user on a screen with nothing to press.
*/
@Composable
fun AccountSelectionScreen() {
val navigator = LocalCodeNavigator.current
val viewModel: AccountSelectionViewModel = hiltViewModel()
val state by viewModel.stateFlow.collectAsStateWithLifecycle()

Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
AppBarWithTitle(
modifier = Modifier.fillMaxWidth(),
title = stringResource(R.string.title_selectAccount),
titleAlignment = Alignment.CenterHorizontally,
onBackIconClicked = { navigator.pop() },
)
AccountSelectionContent(
state = state,
onSelect = { entropy ->
viewModel.dispatchEvent(AccountSelectionViewModel.Event.OnAccountSelected(entropy))
},
onRemove = { entropy ->
viewModel.dispatchEvent(AccountSelectionViewModel.Event.OnRemoveRequested(entropy))
},
showEnterAccessKey = false,
)
}
}
Loading
Loading