Receive screen account expanded state is now reset on account change
This commit is contained in:
parent
9ae240ce29
commit
1580f7045c
|
@ -30,6 +30,7 @@ import co.electriccoin.zcash.ui.common.usecase.ObserveContactPickedUseCase
|
||||||
import co.electriccoin.zcash.ui.common.usecase.ObserveCurrentTransactionsUseCase
|
import co.electriccoin.zcash.ui.common.usecase.ObserveCurrentTransactionsUseCase
|
||||||
import co.electriccoin.zcash.ui.common.usecase.ObserveFastestServersUseCase
|
import co.electriccoin.zcash.ui.common.usecase.ObserveFastestServersUseCase
|
||||||
import co.electriccoin.zcash.ui.common.usecase.ObserveIsFlexaAvailableUseCase
|
import co.electriccoin.zcash.ui.common.usecase.ObserveIsFlexaAvailableUseCase
|
||||||
|
import co.electriccoin.zcash.ui.common.usecase.ObserveOnAccountChangedUseCase
|
||||||
import co.electriccoin.zcash.ui.common.usecase.ObservePersistableWalletUseCase
|
import co.electriccoin.zcash.ui.common.usecase.ObservePersistableWalletUseCase
|
||||||
import co.electriccoin.zcash.ui.common.usecase.ObserveSelectedEndpointUseCase
|
import co.electriccoin.zcash.ui.common.usecase.ObserveSelectedEndpointUseCase
|
||||||
import co.electriccoin.zcash.ui.common.usecase.ObserveSelectedWalletAccountUseCase
|
import co.electriccoin.zcash.ui.common.usecase.ObserveSelectedWalletAccountUseCase
|
||||||
|
@ -122,4 +123,5 @@ val useCaseModule =
|
||||||
factoryOf(::ObserveTransactionProposalUseCase)
|
factoryOf(::ObserveTransactionProposalUseCase)
|
||||||
factoryOf(::SharePCZTUseCase)
|
factoryOf(::SharePCZTUseCase)
|
||||||
factoryOf(::CreateKeystoneProposalPCZTEncoderUseCase)
|
factoryOf(::CreateKeystoneProposalPCZTEncoderUseCase)
|
||||||
|
factoryOf(::ObserveOnAccountChangedUseCase)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,12 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.WhileSubscribed
|
import kotlinx.coroutines.flow.WhileSubscribed
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
|
@ -37,11 +39,14 @@ import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.retryWhen
|
import kotlinx.coroutines.flow.retryWhen
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
interface AccountDataSource {
|
interface AccountDataSource {
|
||||||
|
val onAccountChanged: Flow<Unit>
|
||||||
|
|
||||||
val allAccounts: StateFlow<List<WalletAccount>?>
|
val allAccounts: StateFlow<List<WalletAccount>?>
|
||||||
|
|
||||||
val selectedAccount: Flow<WalletAccount?>
|
val selectedAccount: Flow<WalletAccount?>
|
||||||
|
@ -72,6 +77,8 @@ class AccountDataSourceImpl(
|
||||||
) : AccountDataSource {
|
) : AccountDataSource {
|
||||||
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||||
|
|
||||||
|
override val onAccountChanged = MutableSharedFlow<Unit>()
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
private val internalAccounts: Flow<List<InternalAccountWithBalances>?> =
|
private val internalAccounts: Flow<List<InternalAccountWithBalances>?> =
|
||||||
synchronizerProvider
|
synchronizerProvider
|
||||||
|
@ -199,12 +206,14 @@ class AccountDataSourceImpl(
|
||||||
.map { account ->
|
.map { account ->
|
||||||
account?.firstOrNull { it.isSelected }
|
account?.firstOrNull { it.isSelected }
|
||||||
}
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
|
||||||
override val zashiAccount: Flow<ZashiAccount?> =
|
override val zashiAccount: Flow<ZashiAccount?> =
|
||||||
allAccounts
|
allAccounts
|
||||||
.map { account ->
|
.map { account ->
|
||||||
account?.filterIsInstance<ZashiAccount>()?.firstOrNull()
|
account?.filterIsInstance<ZashiAccount>()?.firstOrNull()
|
||||||
}
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
|
||||||
override suspend fun getAllAccounts() =
|
override suspend fun getAllAccounts() =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
|
@ -221,15 +230,21 @@ class AccountDataSourceImpl(
|
||||||
zashiAccount.filterNotNull().first()
|
zashiAccount.filterNotNull().first()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun selectAccount(account: Account) =
|
override suspend fun selectAccount(account: Account) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
selectedAccountUUIDProvider.setUUID(account.accountUuid)
|
val current = selectedAccountUUIDProvider.getUUID()
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun selectAccount(account: WalletAccount) =
|
selectedAccountUUIDProvider.setUUID(account.accountUuid)
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
selectedAccountUUIDProvider.setUUID(account.sdkAccount.accountUuid)
|
scope.launch {
|
||||||
|
if (current != account.accountUuid) {
|
||||||
|
onAccountChanged.emit(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun selectAccount(account: WalletAccount) = selectAccount(account.sdkAccount)
|
||||||
|
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
override suspend fun importKeystoneAccount(
|
override suspend fun importKeystoneAccount(
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package co.electriccoin.zcash.ui.common.usecase
|
||||||
|
|
||||||
|
import co.electriccoin.zcash.ui.common.datasource.AccountDataSource
|
||||||
|
|
||||||
|
class ObserveOnAccountChangedUseCase(private val accountDataSource: AccountDataSource) {
|
||||||
|
operator fun invoke() = accountDataSource.onAccountChanged
|
||||||
|
}
|
|
@ -11,6 +11,8 @@ data class ReceiveAddressState(
|
||||||
val icon: Int,
|
val icon: Int,
|
||||||
val title: StringResource,
|
val title: StringResource,
|
||||||
val subtitle: StringResource,
|
val subtitle: StringResource,
|
||||||
|
val isExpanded: Boolean,
|
||||||
|
val onClick: () -> Unit,
|
||||||
val isShielded: Boolean,
|
val isShielded: Boolean,
|
||||||
val onCopyClicked: () -> Unit,
|
val onCopyClicked: () -> Unit,
|
||||||
val onQrClicked: () -> Unit,
|
val onQrClicked: () -> Unit,
|
||||||
|
|
|
@ -21,10 +21,6 @@ import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
@ -85,8 +81,6 @@ private fun ReceiveContents(
|
||||||
items: List<ReceiveAddressState>,
|
items: List<ReceiveAddressState>,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
var expandedIndex by rememberSaveable { mutableIntStateOf(0) }
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier =
|
modifier =
|
||||||
modifier
|
modifier
|
||||||
|
@ -122,8 +116,6 @@ private fun ReceiveContents(
|
||||||
|
|
||||||
AddressPanel(
|
AddressPanel(
|
||||||
state = state,
|
state = state,
|
||||||
expanded = index == expandedIndex,
|
|
||||||
onExpand = { expandedIndex = index },
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -133,8 +125,6 @@ private fun ReceiveContents(
|
||||||
@Composable
|
@Composable
|
||||||
private fun AddressPanel(
|
private fun AddressPanel(
|
||||||
state: ReceiveAddressState,
|
state: ReceiveAddressState,
|
||||||
expanded: Boolean,
|
|
||||||
onExpand: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
|
@ -151,7 +141,7 @@ private fun AddressPanel(
|
||||||
RoundedCornerShape(ZashiDimensions.Radius.radius3xl)
|
RoundedCornerShape(ZashiDimensions.Radius.radius3xl)
|
||||||
)
|
)
|
||||||
.clip(RoundedCornerShape(ZashiDimensions.Radius.radius3xl))
|
.clip(RoundedCornerShape(ZashiDimensions.Radius.radius3xl))
|
||||||
.clickable { onExpand() }
|
.clickable(onClick = state.onClick)
|
||||||
.padding(all = ZcashTheme.dimens.spacingLarge)
|
.padding(all = ZcashTheme.dimens.spacingLarge)
|
||||||
) {
|
) {
|
||||||
Row(modifier = Modifier.fillMaxWidth()) {
|
Row(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
@ -192,7 +182,7 @@ private fun AddressPanel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimatedVisibility(visible = expanded) {
|
AnimatedVisibility(visible = state.isExpanded) {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
modifier =
|
modifier =
|
||||||
|
@ -298,6 +288,8 @@ private fun ZashiPreview() =
|
||||||
onCopyClicked = {},
|
onCopyClicked = {},
|
||||||
onQrClicked = { },
|
onQrClicked = { },
|
||||||
onRequestClicked = {},
|
onRequestClicked = {},
|
||||||
|
isExpanded = true,
|
||||||
|
onClick = {}
|
||||||
),
|
),
|
||||||
ReceiveAddressState(
|
ReceiveAddressState(
|
||||||
icon = R.drawable.ic_zec_round_stroke,
|
icon = R.drawable.ic_zec_round_stroke,
|
||||||
|
@ -307,6 +299,8 @@ private fun ZashiPreview() =
|
||||||
onCopyClicked = {},
|
onCopyClicked = {},
|
||||||
onQrClicked = { },
|
onQrClicked = { },
|
||||||
onRequestClicked = { },
|
onRequestClicked = { },
|
||||||
|
isExpanded = false,
|
||||||
|
onClick = {}
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
isLoading = false
|
isLoading = false
|
||||||
|
@ -314,28 +308,3 @@ private fun ZashiPreview() =
|
||||||
zashiMainTopAppBarState = ZashiMainTopAppBarStateFixture.new()
|
zashiMainTopAppBarState = ZashiMainTopAppBarStateFixture.new()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreviewScreens
|
|
||||||
@Composable
|
|
||||||
private fun KeystonePreview() =
|
|
||||||
ZcashTheme {
|
|
||||||
ReceiveView(
|
|
||||||
state =
|
|
||||||
ReceiveState(
|
|
||||||
items =
|
|
||||||
listOf(
|
|
||||||
ReceiveAddressState(
|
|
||||||
icon = co.electriccoin.zcash.ui.design.R.drawable.ic_item_keystone,
|
|
||||||
title = stringRes("Keystone Address"),
|
|
||||||
subtitle = stringRes("subtitle"),
|
|
||||||
isShielded = true,
|
|
||||||
onCopyClicked = {},
|
|
||||||
onQrClicked = {},
|
|
||||||
onRequestClicked = {},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
isLoading = false
|
|
||||||
),
|
|
||||||
zashiMainTopAppBarState = ZashiMainTopAppBarStateFixture.new()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,39 +11,48 @@ import co.electriccoin.zcash.ui.common.model.KeystoneAccount
|
||||||
import co.electriccoin.zcash.ui.common.model.WalletAccount
|
import co.electriccoin.zcash.ui.common.model.WalletAccount
|
||||||
import co.electriccoin.zcash.ui.common.model.ZashiAccount
|
import co.electriccoin.zcash.ui.common.model.ZashiAccount
|
||||||
import co.electriccoin.zcash.ui.common.usecase.CopyToClipboardUseCase
|
import co.electriccoin.zcash.ui.common.usecase.CopyToClipboardUseCase
|
||||||
|
import co.electriccoin.zcash.ui.common.usecase.ObserveOnAccountChangedUseCase
|
||||||
import co.electriccoin.zcash.ui.common.usecase.ObserveSelectedWalletAccountUseCase
|
import co.electriccoin.zcash.ui.common.usecase.ObserveSelectedWalletAccountUseCase
|
||||||
import co.electriccoin.zcash.ui.design.util.stringRes
|
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||||
import co.electriccoin.zcash.ui.screen.addressbook.viewmodel.ADDRESS_MAX_LENGTH
|
import co.electriccoin.zcash.ui.screen.addressbook.viewmodel.ADDRESS_MAX_LENGTH
|
||||||
import co.electriccoin.zcash.ui.screen.receive.model.ReceiveAddressState
|
import co.electriccoin.zcash.ui.screen.receive.model.ReceiveAddressState
|
||||||
import co.electriccoin.zcash.ui.screen.receive.model.ReceiveAddressType
|
import co.electriccoin.zcash.ui.screen.receive.model.ReceiveAddressType
|
||||||
import co.electriccoin.zcash.ui.screen.receive.model.ReceiveState
|
import co.electriccoin.zcash.ui.screen.receive.model.ReceiveState
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.WhileSubscribed
|
import kotlinx.coroutines.flow.WhileSubscribed
|
||||||
import kotlinx.coroutines.flow.mapLatest
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class ReceiveViewModel(
|
class ReceiveViewModel(
|
||||||
observeSelectedWalletAccount: ObserveSelectedWalletAccountUseCase,
|
observeSelectedWalletAccount: ObserveSelectedWalletAccountUseCase,
|
||||||
|
observeOnAccountChanged: ObserveOnAccountChangedUseCase,
|
||||||
private val application: Application,
|
private val application: Application,
|
||||||
private val copyToClipboard: CopyToClipboardUseCase,
|
private val copyToClipboard: CopyToClipboardUseCase,
|
||||||
private val navigationRouter: NavigationRouter,
|
private val navigationRouter: NavigationRouter,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
private val expandedIndex = MutableStateFlow(0)
|
||||||
|
|
||||||
internal val state =
|
internal val state =
|
||||||
observeSelectedWalletAccount.require().mapLatest { account ->
|
combine(expandedIndex, observeSelectedWalletAccount.require()) { expandedIndex, account ->
|
||||||
ReceiveState(
|
ReceiveState(
|
||||||
items =
|
items =
|
||||||
listOfNotNull(
|
listOfNotNull(
|
||||||
createAddressState(
|
createAddressState(
|
||||||
account = account,
|
account = account,
|
||||||
address = account.unified.address.address,
|
address = account.unified.address.address,
|
||||||
type = ReceiveAddressType.Unified
|
type = ReceiveAddressType.Unified,
|
||||||
|
isExpanded = expandedIndex == 0,
|
||||||
|
onClick = { onAddressClick(0) }
|
||||||
),
|
),
|
||||||
createAddressState(
|
createAddressState(
|
||||||
account = account,
|
account = account,
|
||||||
address = account.transparent.address.address,
|
address = account.transparent.address.address,
|
||||||
type = ReceiveAddressType.Transparent
|
type = ReceiveAddressType.Transparent,
|
||||||
|
isExpanded = expandedIndex == 1,
|
||||||
|
onClick = { onAddressClick(1) }
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
isLoading = false
|
isLoading = false
|
||||||
|
@ -54,10 +63,20 @@ class ReceiveViewModel(
|
||||||
initialValue = ReceiveState(items = null, isLoading = true)
|
initialValue = ReceiveState(items = null, isLoading = true)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch {
|
||||||
|
observeOnAccountChanged().collect {
|
||||||
|
expandedIndex.update { 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun createAddressState(
|
private fun createAddressState(
|
||||||
account: WalletAccount,
|
account: WalletAccount,
|
||||||
address: String,
|
address: String,
|
||||||
type: ReceiveAddressType
|
type: ReceiveAddressType,
|
||||||
|
isExpanded: Boolean,
|
||||||
|
onClick: () -> Unit,
|
||||||
) = ReceiveAddressState(
|
) = ReceiveAddressState(
|
||||||
icon =
|
icon =
|
||||||
when (account) {
|
when (account) {
|
||||||
|
@ -94,6 +113,8 @@ class ReceiveViewModel(
|
||||||
},
|
},
|
||||||
onQrClicked = { onQrCodeClick(type) },
|
onQrClicked = { onQrCodeClick(type) },
|
||||||
onRequestClicked = { onRequestClick(type) },
|
onRequestClicked = { onRequestClick(type) },
|
||||||
|
onClick = onClick,
|
||||||
|
isExpanded = isExpanded
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun onRequestClick(addressType: ReceiveAddressType) =
|
private fun onRequestClick(addressType: ReceiveAddressType) =
|
||||||
|
@ -101,4 +122,8 @@ class ReceiveViewModel(
|
||||||
|
|
||||||
private fun onQrCodeClick(addressType: ReceiveAddressType) =
|
private fun onQrCodeClick(addressType: ReceiveAddressType) =
|
||||||
navigationRouter.forward("${NavigationTargets.QR_CODE}/${addressType.ordinal}")
|
navigationRouter.forward("${NavigationTargets.QR_CODE}/${addressType.ordinal}")
|
||||||
|
|
||||||
|
private fun onAddressClick(index: Int) {
|
||||||
|
expandedIndex.update { index }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue