Skip to content

Commit

Permalink
Merge pull request #759 from radixdlt/feature/ABW-2754-show-addlinkco…
Browse files Browse the repository at this point in the history
…nnector-only-when-no-linkconnectors

[ABW-2754] Show add link connector screen only when no link connectors available and connector status indicator
  • Loading branch information
giannis-rdx authored Jan 24, 2024
2 parents 0b8cec9 + e80a351 commit 7b4bcd5
Show file tree
Hide file tree
Showing 22 changed files with 360 additions and 130 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.babylon.wallet.android

import androidx.compose.ui.graphics.Color
import com.babylon.wallet.android.di.coroutines.ApplicationScope
import com.babylon.wallet.android.utils.Constants
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import rdx.works.core.preferences.PreferencesManager
import rdx.works.peerdroid.data.PeerdroidConnector
import rdx.works.peerdroid.domain.PeerConnectionStatus
import javax.inject.Inject

class LinkConnectionStatusObserver @Inject constructor(
peerdroidConnector: PeerdroidConnector,
preferencesManager: PreferencesManager,
@ApplicationScope private val applicationScope: CoroutineScope
) {

val isEnabled = preferencesManager
.isLinkConnectionStatusIndicatorEnabled
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(Constants.VM_STOP_TIMEOUT_MS),
initialValue = true
)

val currentStatus = peerdroidConnector
.peerConnectionStatus
.map { mapOfPeerConnectionStatus ->
LinkConnectionsStatus(
peerConnectionStatus = mapOfPeerConnectionStatus.values.toPersistentList()
)
}
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(Constants.VM_STOP_TIMEOUT_MS),
initialValue = LinkConnectionsStatus()
)

data class LinkConnectionsStatus(
private val peerConnectionStatus: ImmutableList<PeerConnectionStatus> = persistentListOf()
) {

fun currentStatus() = peerConnectionStatus
.map { state ->
when (state) {
PeerConnectionStatus.OPEN -> {
Color.Green
}

PeerConnectionStatus.CLOSED -> {
Color.Red
}

PeerConnectionStatus.CONNECTING -> {
Color.Yellow
}
}
}
}
}
18 changes: 17 additions & 1 deletion app/src/main/java/com/babylon/wallet/android/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,24 @@ import androidx.core.splashscreen.SplashScreen
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.fragment.app.FragmentActivity
import com.babylon.wallet.android.LinkConnectionStatusObserver.LinkConnectionsStatus
import com.babylon.wallet.android.designsystem.theme.RadixWalletTheme
import com.babylon.wallet.android.presentation.main.AppState
import com.babylon.wallet.android.presentation.main.MainViewModel
import com.babylon.wallet.android.presentation.ui.composables.DevBannerState
import com.babylon.wallet.android.presentation.ui.composables.DevelopmentPreviewWrapper
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

// Extending from FragmentActivity because of Biometric
@AndroidEntryPoint
class MainActivity : FragmentActivity() {

private val viewModel: MainViewModel by viewModels()

@Inject
lateinit var linkConnectionStatusObserver: LinkConnectionStatusObserver

override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition {
Expand All @@ -46,7 +51,18 @@ class MainActivity : FragmentActivity() {
derivedStateOf { DevBannerState(isVisible = isDevBannerVisible) }
}

DevelopmentPreviewWrapper(devBannerState = devBannerState) { padding ->
var linkConnectionsStatus: LinkConnectionsStatus? = null
if (BuildConfig.EXPERIMENTAL_FEATURES_ENABLED) {
val isLinkConnectionsStatusEnabled by linkConnectionStatusObserver.isEnabled.collectAsState()
if (isLinkConnectionsStatusEnabled) {
linkConnectionsStatus = linkConnectionStatusObserver.currentStatus.collectAsState().value
}
}

DevelopmentPreviewWrapper(
devBannerState = devBannerState,
linkConnectionsStatus = linkConnectionsStatus
) { padding ->
WalletApp(
modifier = Modifier.padding(padding),
mainViewModel = viewModel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import javax.inject.Inject

interface LedgerMessenger {

val isConnected: Flow<Boolean>
val isAnyLinkedConnectorConnected: Flow<Boolean>

suspend fun sendDeviceInfoRequest(interactionId: String): Result<MessageFromDataChannel.LedgerResponse.GetDeviceInfoResponse>

suspend fun signTransactionRequest(
Expand Down Expand Up @@ -55,7 +56,7 @@ class LedgerMessengerImpl @Inject constructor(
private val peerdroidClient: PeerdroidClient,
) : LedgerMessenger {

override val isConnected: Flow<Boolean>
override val isAnyLinkedConnectorConnected: Flow<Boolean>
get() = peerdroidClient.hasAtLeastOneConnection

override suspend fun sendDeviceInfoRequest(interactionId: String): Result<MessageFromDataChannel.LedgerResponse.GetDeviceInfoResponse> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ fun ChooseLedgerScreen(
context.biometricAuthenticateSuspend()
})
},
linkingToConnector = state.linkingToConnector
linkingToConnector = state.isAddingLinkConnector
)
}

Expand All @@ -126,7 +126,7 @@ fun ChooseLedgerScreen(
isNewConnectorContinueButtonEnabled = addLinkConnectorState.isContinueButtonEnabled,
onNewConnectorContinueClick = {
addLinkConnectorViewModel.onContinueClick()
viewModel.onNewConnectorAdded(showContent.addDeviceAfterLinking)
viewModel.onNewLinkConnectorAdded(showContent.addDeviceAfterLinking)
},
onNewConnectorCloseClick = {
addLinkConnectorViewModel.onCloseClick()
Expand Down Expand Up @@ -161,7 +161,7 @@ fun ChooseLedgerScreen(
viewModel.onCloseClick()
},
onMessageShown = addLedgerDeviceViewModel::onMessageShown,
isLinkConnectionEstablished = addLedgerDeviceState.isLinkConnectionEstablished && state.linkingToConnector.not()
isLinkedConnectorEstablished = addLedgerDeviceState.isAnyLinkedConnectorConnected && state.isAddingLinkConnector.not()
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,6 @@ class ChooseLedgerViewModel @Inject constructor(
override fun initialState(): ChooseLedgerUiState = ChooseLedgerUiState()

init {
viewModelScope.launch {
ledgerMessenger.isConnected.collect { connected ->
_state.update { it.copy(isLinkConnectionEstablished = connected) }
}
}
viewModelScope.launch {
getProfileUseCase.ledgerFactorSources.collect { ledgerDevices ->
_state.update { uiState ->
Expand Down Expand Up @@ -117,23 +112,15 @@ class ChooseLedgerViewModel @Inject constructor(
selectableLedgerDevice.selected
}?.let { ledgerFactorSource ->
viewModelScope.launch {
if (getProfileUseCase.p2pLinks.first().isEmpty()) {
val hasAtLeastOneLinkedConnector = getProfileUseCase.p2pLinks.first().isNotEmpty()
// check if there is not linked connector and show link new connector screen
if (hasAtLeastOneLinkedConnector.not()) {
_state.update {
it.copy(showContent = ChooseLedgerUiState.ShowContent.LinkNewConnector(false))
}
return@launch
} else {
if (!state.value.isLinkConnectionEstablished) {
_state.update {
it.copy(
showLinkConnectorPromptState = ShowLinkConnectorPromptState.Show(
ShowLinkConnectorPromptState.Source.UseLedger
)
)
}
return@launch
}
}

when (args.ledgerSelectionPurpose) {
LedgerSelectionPurpose.CreateAccount -> {
// check again if link connector exists
Expand Down Expand Up @@ -192,19 +179,12 @@ class ChooseLedgerViewModel @Inject constructor(

fun onAddLedgerDeviceClick() {
viewModelScope.launch {
_state.update {
if (getProfileUseCase.p2pLinks.first().isEmpty()) {
it.copy(showContent = ChooseLedgerUiState.ShowContent.LinkNewConnector())
_state.update { uiState ->
val hasAtLeastOneLinkedConnector = getProfileUseCase.p2pLinks.first().isNotEmpty()
if (hasAtLeastOneLinkedConnector) {
uiState.copy(showContent = ChooseLedgerUiState.ShowContent.AddLedger)
} else {
if (!it.isLinkConnectionEstablished) {
it.copy(
showLinkConnectorPromptState = ShowLinkConnectorPromptState.Show(
ShowLinkConnectorPromptState.Source.AddLedgerDevice
)
)
} else {
it.copy(showContent = ChooseLedgerUiState.ShowContent.AddLedger)
}
uiState.copy(showContent = ChooseLedgerUiState.ShowContent.LinkNewConnector())
}
}
}
Expand All @@ -222,16 +202,16 @@ class ChooseLedgerViewModel @Inject constructor(
}
}

fun onNewConnectorAdded(addDeviceAfterLinking: Boolean) {
_state.update { it.copy(linkingToConnector = true) }
fun onNewLinkConnectorAdded(addDeviceAfterLinking: Boolean) {
_state.update { it.copy(isAddingLinkConnector = true) }
if (addDeviceAfterLinking) {
showAddLedgerDeviceContent()
} else {
onCloseClick()
}
viewModelScope.launch {
ledgerMessenger.isConnected.filter { it }.firstOrNull()?.let {
_state.update { state -> state.copy(linkingToConnector = false) }
ledgerMessenger.isAnyLinkedConnectorConnected.filter { it }.firstOrNull()?.let {
_state.update { state -> state.copy(isAddingLinkConnector = false) }
}
}
}
Expand All @@ -250,7 +230,7 @@ data class ChooseLedgerUiState(
val selectedLedgerDeviceId: FactorSource.FactorSourceID.FromHash? = null,
val isLinkConnectionEstablished: Boolean = false,
val showLinkConnectorPromptState: ShowLinkConnectorPromptState = ShowLinkConnectorPromptState.None,
val linkingToConnector: Boolean = false
val isAddingLinkConnector: Boolean = false
) : UiState {

sealed interface ShowContent {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,23 +108,28 @@ sealed interface SettingsItem {
sealed interface DebugSettingsItem {
data object InspectProfile : DebugSettingsItem

data object LinkConnectionStatusIndicator : DebugSettingsItem

@StringRes
fun descriptionRes(): Int {
return when (this) {
InspectProfile -> R.string.settings_debugSettings_inspectProfile
LinkConnectionStatusIndicator -> R.string.linkedConnectors_title
}
}

@DrawableRes
fun getIcon(): Int? { // add rest of icons
return when (this) {
InspectProfile -> com.babylon.wallet.android.designsystem.R.drawable.ic_personas
LinkConnectionStatusIndicator -> com.babylon.wallet.android.designsystem.R.drawable.ic_desktop_connection
}
}

companion object {
fun values() = setOf(
InspectProfile
InspectProfile,
LinkConnectionStatusIndicator
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ private fun ImportLegacyWalletContent(
onClose = onCloseSettings,
waitingForLedgerResponse = waitingForLedgerResponse,
onBackClick = onCloseSettings,
isLinkConnectionEstablished = true
isLinkedConnectorEstablished = true

)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,6 @@ class ImportLegacyWalletViewModel @Inject constructor(
)

init {
viewModelScope.launch {
ledgerMessenger.isConnected.collect { connected ->
_state.update { it.copy(isLinkConnectionEstablished = connected) }
}
}
viewModelScope.launch {
useLedgerDelegate.state.collect { delegateState ->
_state.update { uiState ->
Expand Down Expand Up @@ -446,15 +441,11 @@ class ImportLegacyWalletViewModel @Inject constructor(

fun onContinueWithLedgerClick() {
viewModelScope.launch {
if (getProfileUseCase.p2pLinks.first().isNotEmpty()) {
if (state.value.isLinkConnectionEstablished.not()) {
_state.update {
it.copy(shouldShowAddLinkConnectorScreen = true)
}
} else {
useLedgerDelegate.onSendAddLedgerRequest()
}
} else if (getProfileUseCase.p2pLinks.first().isEmpty()) {
val hasAtLeastOneLinkedConnector = getProfileUseCase.p2pLinks.first().isNotEmpty()

if (hasAtLeastOneLinkedConnector) {
useLedgerDelegate.onSendAddLedgerRequest()
} else if (hasAtLeastOneLinkedConnector.not()) {
_state.update {
it.copy(shouldShowAddLinkConnectorScreen = true)
}
Expand Down Expand Up @@ -522,8 +513,7 @@ data class ImportLegacyWalletUiState(
val seedPhraseInputState: SeedPhraseInputDelegate.State = SeedPhraseInputDelegate.State(),
val shouldShowAddLinkConnectorScreen: Boolean = false,
val shouldShowAddLedgerDeviceScreen: Boolean = false,
var existingOlympiaFactorSourceId: FactorSourceID.FromHash? = null,
val isLinkConnectionEstablished: Boolean = false,
var existingOlympiaFactorSourceId: FactorSourceID.FromHash? = null
) : UiState {

fun mnemonicWithPassphrase(): MnemonicWithPassphrase {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ class AddLedgerDeviceViewModel @Inject constructor(

private fun observeLinkConnectionStatus() {
viewModelScope.launch {
ledgerMessenger.isConnected.collect { connected ->
_state.update { it.copy(isLinkConnectionEstablished = connected) }
ledgerMessenger.isAnyLinkedConnectorConnected.collect { isConnected ->
_state.update { it.copy(isAnyLinkedConnectorConnected = isConnected) }
}
}
}
Expand All @@ -101,7 +101,7 @@ class AddLedgerDeviceViewModel @Inject constructor(

fun initState() {
_state.update { current ->
AddLedgerDeviceUiState.init.copy(isLinkConnectionEstablished = current.isLinkConnectionEstablished)
AddLedgerDeviceUiState.init.copy(isAnyLinkedConnectorConnected = current.isAnyLinkedConnectorConnected)
}
}

Expand Down Expand Up @@ -136,7 +136,7 @@ data class AddLedgerDeviceUiState(
val showContent: ShowContent,
val newConnectedLedgerDevice: LedgerDeviceUiModel?,
val uiMessage: UiMessage?,
val isLinkConnectionEstablished: Boolean = false
val isAnyLinkedConnectorConnected: Boolean = false
) : UiState {

enum class ShowContent {
Expand Down
Loading

0 comments on commit 7b4bcd5

Please sign in to comment.