mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 07:05:35 +03:00
wip
This commit is contained in:
parent
5c62290a3d
commit
cee686f92e
12 changed files with 159 additions and 35 deletions
|
@ -164,6 +164,34 @@ fun <T1, T2, T3, T4, R> combineDataStates(
|
|||
transform(t1t2t3Triple.first, t1t2t3Triple.second, t1t2t3Triple.third, t3)
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines the [dataState1], [dataState2], [dataState3], and [dataState4] [DataState]s together
|
||||
* using the provided [transform].
|
||||
*
|
||||
* See [combineDataStates] for details.
|
||||
*
|
||||
* I'm not proud of this...
|
||||
*/
|
||||
@OmitFromCoverage
|
||||
fun <T1, T2, T3, T4, T5, R> combineDataStates(
|
||||
dataState1: DataState<T1>,
|
||||
dataState2: DataState<T2>,
|
||||
dataState3: DataState<T3>,
|
||||
dataState4: DataState<T4>,
|
||||
dataState5: DataState<T5>,
|
||||
transform: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5) -> R,
|
||||
): DataState<R> =
|
||||
dataState1
|
||||
.combineDataStatesWith(dataState2) { t1, t2 -> t1 to t2 }
|
||||
.combineDataStatesWith(dataState3) { t1t2Pair, t3 ->
|
||||
Triple(t1t2Pair.first, t1t2Pair.second, t3)
|
||||
}
|
||||
.combineDataStatesWith(dataState4) { t1t2t3Triple, t4 -> t1t2t3Triple to t4 }
|
||||
.combineDataStatesWith(dataState5) { t1t2t3Triplet4Pair, t5 ->
|
||||
transform(t1t2t3Triplet4Pair.first.first, t1t2t3Triplet4Pair.first.second, t1t2t3Triplet4Pair.first.third, t1t2t3Triplet4Pair.second, t5)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Combines [dataState2] with the given [DataState] using the provided [transform].
|
||||
*
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.disk
|
||||
|
||||
import com.bitwarden.vault.Cipher
|
||||
import com.bitwarden.vault.CipherView
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.OfflineCipherJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
|
@ -21,6 +21,11 @@ interface VaultDiskSource {
|
|||
*/
|
||||
suspend fun saveCipher(userId: String, cipher: SyncResponseJson.Cipher)
|
||||
|
||||
/**
|
||||
* Retrieves all ciphers from the offline cache for a given [userId]
|
||||
*/
|
||||
fun getOfflineCiphers(userId: String): Flow<List<OfflineCipherJson>>
|
||||
|
||||
/**
|
||||
* Retrieves all ciphers from the data source for a given [userId].
|
||||
*/
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.x8bit.bitwarden.data.vault.datasource.disk.entity.DomainsEntity
|
|||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.FolderEntity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.OfflineCipherEntity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.SendEntity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.OfflineCipherJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toOfflineCipher
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toOfflineCipherJson
|
||||
|
@ -46,6 +47,7 @@ class VaultDiskSourceImpl(
|
|||
private val dispatcherManager: DispatcherManager,
|
||||
) : VaultDiskSource {
|
||||
|
||||
private val forceOfflineCiphersFlow = bufferedMutableSharedFlow<List<OfflineCipherJson>>()
|
||||
private val forceCiphersFlow = bufferedMutableSharedFlow<List<SyncResponseJson.Cipher>>()
|
||||
private val forceCollectionsFlow =
|
||||
bufferedMutableSharedFlow<List<SyncResponseJson.Collection>>()
|
||||
|
@ -59,7 +61,9 @@ class VaultDiskSourceImpl(
|
|||
id = cipher.id ?: "create_${UUID.randomUUID()}",
|
||||
userId = userId,
|
||||
cipherType = json.encodeToString(cipher.type),
|
||||
cipherJson = json.encodeToString(cipher.toOfflineCipher().toOfflineCipherJson()),
|
||||
cipherJson = json.encodeToString(
|
||||
cipher.toOfflineCipher().toOfflineCipherJson()
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -78,6 +82,29 @@ class VaultDiskSourceImpl(
|
|||
)
|
||||
}
|
||||
|
||||
|
||||
override fun getOfflineCiphers(
|
||||
userId: String,
|
||||
): Flow<List<OfflineCipherJson>> =
|
||||
merge(
|
||||
forceOfflineCiphersFlow,
|
||||
offlineCiphersDao
|
||||
.getAllCiphers(userId = userId)
|
||||
.map { entities ->
|
||||
withContext(context = dispatcherManager.default) {
|
||||
entities
|
||||
.map { entity ->
|
||||
async {
|
||||
json.decodeFromString<OfflineCipherJson>(
|
||||
string = entity.cipherJson,
|
||||
)
|
||||
}
|
||||
}
|
||||
.awaitAll()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
override fun getCiphers(
|
||||
userId: String,
|
||||
): Flow<List<SyncResponseJson.Cipher>> =
|
||||
|
|
|
@ -100,6 +100,11 @@ interface CipherManager {
|
|||
collectionIds: List<String>,
|
||||
): ShareCipherResult
|
||||
|
||||
suspend fun updateOfflineCipher(
|
||||
cipherId: String,
|
||||
cipherView: CipherView,
|
||||
): UpdateCipherResult
|
||||
|
||||
/**
|
||||
* Attempt to update a cipher.
|
||||
*/
|
||||
|
|
|
@ -208,6 +208,26 @@ class CipherManagerImpl(
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun updateOfflineCipher(
|
||||
cipherId: String,
|
||||
cipherView: CipherView,
|
||||
): UpdateCipherResult {
|
||||
val userId = activeUserId ?: return UpdateCipherResult.Error(errorMessage = null)
|
||||
|
||||
return vaultSdkSource.encryptCipher(
|
||||
userId = userId,
|
||||
cipherView = cipherView
|
||||
)
|
||||
.map {
|
||||
vaultDiskSource.saveOfflineCipher(userId = userId, cipher = it)
|
||||
UpdateCipherResult.Success
|
||||
}
|
||||
.fold(
|
||||
onFailure = { UpdateCipherResult.Error(errorMessage = null) },
|
||||
onSuccess = { it },
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun updateCipher(
|
||||
cipherId: String,
|
||||
cipherView: CipherView,
|
||||
|
|
|
@ -56,6 +56,13 @@ interface VaultRepository : CipherManager, VaultLockManager {
|
|||
*/
|
||||
val vaultDataStateFlow: StateFlow<DataState<VaultData>>
|
||||
|
||||
/**
|
||||
* Flow that represents all ciphers stored in the offline cache for the active user.
|
||||
*
|
||||
* Note that the [StateFlow.value] will return the last known value but the [StateFlow] itself
|
||||
* must be collected in order to trigger state changes.
|
||||
*/
|
||||
val offlineCiphersStateFlow: StateFlow<DataState<List<CipherView>>>
|
||||
/**
|
||||
* Flow that represents all ciphers for the active user.
|
||||
*
|
||||
|
|
|
@ -72,6 +72,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.UpdateSendResult
|
|||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.sortAlphabetically
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toCipherList
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toDomainsData
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkFolder
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkSend
|
||||
|
@ -156,6 +157,9 @@ class VaultRepositoryImpl(
|
|||
|
||||
private val mutableSendDataStateFlow = MutableStateFlow<DataState<SendData>>(DataState.Loading)
|
||||
|
||||
private val mutableOfflineCiphersStateFlow =
|
||||
MutableStateFlow<DataState<List<CipherView>>>(DataState.Loading)
|
||||
|
||||
private val mutableCiphersStateFlow =
|
||||
MutableStateFlow<DataState<List<CipherView>>>(DataState.Loading)
|
||||
|
||||
|
@ -172,18 +176,21 @@ class VaultRepositoryImpl(
|
|||
|
||||
override val vaultDataStateFlow: StateFlow<DataState<VaultData>> =
|
||||
combine(
|
||||
offlineCiphersStateFlow,
|
||||
ciphersStateFlow,
|
||||
foldersStateFlow,
|
||||
collectionsStateFlow,
|
||||
sendDataStateFlow,
|
||||
) { ciphersDataState, foldersDataState, collectionsDataState, sendsDataState ->
|
||||
) { offlineCiphersDataState, ciphersDataState, foldersDataState, collectionsDataState, sendsDataState ->
|
||||
combineDataStates(
|
||||
offlineCiphersDataState,
|
||||
ciphersDataState,
|
||||
foldersDataState,
|
||||
collectionsDataState,
|
||||
sendsDataState,
|
||||
) { ciphersData, foldersData, collectionsData, sendsData ->
|
||||
) { offlineCiphersData, ciphersData, foldersData, collectionsData, sendsData ->
|
||||
VaultData(
|
||||
offlineCipherViewList = offlineCiphersData,
|
||||
cipherViewList = ciphersData,
|
||||
fido2CredentialAutofillViewList = null,
|
||||
folderViewList = foldersData,
|
||||
|
@ -201,6 +208,9 @@ class VaultRepositoryImpl(
|
|||
override val totpCodeFlow: Flow<TotpCodeResult>
|
||||
get() = mutableTotpCodeResultFlow.asSharedFlow()
|
||||
|
||||
override val offlineCiphersStateFlow: StateFlow<DataState<List<CipherView>>>
|
||||
get() = mutableOfflineCiphersStateFlow.asStateFlow();
|
||||
|
||||
override val ciphersStateFlow: StateFlow<DataState<List<CipherView>>>
|
||||
get() = mutableCiphersStateFlow.asStateFlow()
|
||||
|
||||
|
@ -234,6 +244,11 @@ class VaultRepositoryImpl(
|
|||
}
|
||||
.launchIn(unconfinedScope)
|
||||
|
||||
// Setup offline ciphers MutableStateFlow
|
||||
mutableOfflineCiphersStateFlow
|
||||
.observeWhenSubscribedAndLoggedIn(authDiskSource.userStateFlow) { activeUserId ->
|
||||
observeVaultDiskOfflineCiphers(activeUserId)
|
||||
}.launchIn(unconfinedScope)
|
||||
// Setup ciphers MutableStateFlow
|
||||
mutableCiphersStateFlow
|
||||
.observeWhenSubscribedAndLoggedIn(authDiskSource.userStateFlow) { activeUserId ->
|
||||
|
@ -302,6 +317,7 @@ class VaultRepositoryImpl(
|
|||
}
|
||||
|
||||
private fun clearUnlockedData() {
|
||||
mutableOfflineCiphersStateFlow.update { DataState.Loading }
|
||||
mutableCiphersStateFlow.update { DataState.Loading }
|
||||
mutableDomainsStateFlow.update { DataState.Loading }
|
||||
mutableFoldersStateFlow.update { DataState.Loading }
|
||||
|
@ -318,6 +334,7 @@ class VaultRepositoryImpl(
|
|||
override fun sync() {
|
||||
val userId = activeUserId ?: return
|
||||
if (!syncJob.isCompleted) return
|
||||
mutableOfflineCiphersStateFlow.updateToPendingOrLoading()
|
||||
mutableCiphersStateFlow.updateToPendingOrLoading()
|
||||
mutableDomainsStateFlow.updateToPendingOrLoading()
|
||||
mutableFoldersStateFlow.updateToPendingOrLoading()
|
||||
|
@ -926,6 +943,25 @@ class VaultRepositoryImpl(
|
|||
)
|
||||
}
|
||||
|
||||
private fun observeVaultDiskOfflineCiphers(
|
||||
userId: String,
|
||||
): Flow<DataState<List<CipherView>>> =
|
||||
vaultDiskSource.getOfflineCiphers(userId = userId)
|
||||
.onStart { mutableOfflineCiphersStateFlow.updateToPendingOrLoading() }
|
||||
.map {
|
||||
waitUntilUnlocked(userId = userId)
|
||||
vaultSdkSource
|
||||
.decryptCipherList(
|
||||
userId = userId,
|
||||
cipherList = it.toCipherList(),
|
||||
)
|
||||
.fold(
|
||||
onSuccess = { ciphers -> DataState.Loaded(ciphers.sortAlphabetically()) },
|
||||
onFailure = { throwable -> DataState.Error(throwable) },
|
||||
)
|
||||
}
|
||||
.onEach { mutableOfflineCiphersStateFlow.value = it }
|
||||
|
||||
private fun observeVaultDiskCiphers(
|
||||
userId: String,
|
||||
): Flow<DataState<List<CipherView>>> =
|
||||
|
@ -1029,6 +1065,11 @@ class VaultRepositoryImpl(
|
|||
.onEach { mutableSendDataStateFlow.value = it }
|
||||
|
||||
private fun updateVaultStateFlowsToError(throwable: Throwable) {
|
||||
mutableOfflineCiphersStateFlow.update { currentState ->
|
||||
throwable.toNetworkOrErrorState(
|
||||
data = currentState.data,
|
||||
)
|
||||
}
|
||||
mutableCiphersStateFlow.update { currentState ->
|
||||
throwable.toNetworkOrErrorState(
|
||||
data = currentState.data,
|
||||
|
|
|
@ -9,6 +9,7 @@ import com.bitwarden.vault.FolderView
|
|||
/**
|
||||
* Represents decrypted vault data.
|
||||
*
|
||||
* @param offlineCipherViewList List of decrypted ciphers from offline cache.
|
||||
* @param cipherViewList List of decrypted ciphers.
|
||||
* @param collectionViewList List of decrypted collections.
|
||||
* @param folderViewList List of decrypted folders.
|
||||
|
@ -16,6 +17,7 @@ import com.bitwarden.vault.FolderView
|
|||
* @param fido2CredentialAutofillViewList List of decrypted fido 2 credentials.
|
||||
*/
|
||||
data class VaultData(
|
||||
val offlineCipherViewList: List<CipherView>,
|
||||
val cipherViewList: List<CipherView>,
|
||||
val collectionViewList: List<CollectionView>,
|
||||
val folderViewList: List<FolderView>,
|
||||
|
|
|
@ -31,6 +31,7 @@ import com.x8bit.bitwarden.data.vault.datasource.network.model.SecureNoteTypeJso
|
|||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UriMatchTypeJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.OfflineCipher
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.model.NotificationSummary
|
||||
import java.time.ZoneOffset
|
||||
import java.time.ZonedDateTime
|
||||
import java.util.UUID
|
||||
|
@ -111,6 +112,12 @@ fun OfflineCipher.toOfflineCipherJson(): OfflineCipherJson =
|
|||
mergeConflict = false, // TODO: Copy from the new OfflineCipher type
|
||||
)
|
||||
|
||||
fun CipherView.toNotificationSummary(): NotificationSummary =
|
||||
NotificationSummary(
|
||||
title = name,
|
||||
subtitle = "edited on ${revisionDate.toString()}",
|
||||
)
|
||||
|
||||
fun OfflineCipherJson.toOfflineCipher(): OfflineCipher =
|
||||
OfflineCipher(
|
||||
id = if(id.startsWith("create")) null else id,
|
||||
|
@ -136,6 +143,8 @@ fun OfflineCipherJson.toOfflineCipher(): OfflineCipher =
|
|||
mergeConflict = mergeConflict
|
||||
)
|
||||
|
||||
|
||||
|
||||
fun OfflineCipher.toCipher(): Cipher =
|
||||
Cipher(
|
||||
id = id,
|
||||
|
@ -427,6 +436,13 @@ private fun CipherType.toNetworkCipherType(): CipherTypeJson =
|
|||
fun List<SyncResponseJson.Cipher>.toEncryptedSdkCipherList(): List<Cipher> =
|
||||
map { it.toEncryptedSdkCipher() }
|
||||
|
||||
/**
|
||||
* Converts a list of [OfflineCipherJson] objects to a list of corresponding
|
||||
* Bitwarden SDK [Cipher] objects.
|
||||
*/
|
||||
fun List<OfflineCipherJson>.toCipherList(): List<Cipher> =
|
||||
map { it.toOfflineCipher().toCipher() }
|
||||
|
||||
/**
|
||||
* Converts a [SyncResponseJson.Cipher] object to a corresponding
|
||||
* Bitwarden SDK [Cipher] object.
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.x8bit.bitwarden.ui.vault.feature.unsyncedvaultitem
|
||||
|
||||
import android.util.Range
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
|
@ -42,14 +41,10 @@ import androidx.compose.ui.semantics.clearAndSetSemantics
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.lowercaseWithCurrentLocal
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.scrolledContainerBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toSafeOverlayColor
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toUnscaledTextUnit
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLogoutConfirmationDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenRemovalConfirmationDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenSelectionDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.row.BitwardenBasicDialogRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
|
@ -57,12 +52,7 @@ import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
|
|||
import com.x8bit.bitwarden.ui.platform.components.scrim.BitwardenAnimatedScrim
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.VaultViewModel
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.model.NotificationSummary
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.util.iconRes
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.util.iconTestTag
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.util.initials
|
||||
import com.x8bit.bitwarden.ui.vault.feature.vault.util.supportingTextResOrNull
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
|
@ -83,19 +73,8 @@ private const val MAXIMUM_ACCOUNT_LIMIT = 5
|
|||
*
|
||||
* @param isVisible Whether or not this component is visible. Changing this value will animate the
|
||||
* component in or out of view.
|
||||
* @param accountSummaries The accounts to display in the switcher.
|
||||
* @param onSwitchAccountClick A callback when an account is clicked indicating that the account
|
||||
* should be switched to.
|
||||
* @param onLockAccountClick A callback when an account is clicked indicating that the account
|
||||
* should be locked.
|
||||
* @param onLogoutAccountClick A callback when an account is clicked indicating that the account
|
||||
* should be logged out.
|
||||
* @param onAddAccountClick A callback when the Add Account row is clicked.
|
||||
* @param onDismissRequest A callback when the component requests to be dismissed. This is triggered
|
||||
* whenever the user clicks on the scrim or any of the switcher items.
|
||||
* @param isAddAccountAvailable Whether or not the "Add account" button is available. Note that even
|
||||
* when `true`, this button may be hidden when there are more than [MAXIMUM_ACCOUNT_LIMIT] accounts
|
||||
* present.
|
||||
* @param modifier A [Modifier] for the composable.
|
||||
* @param topAppBarScrollBehavior Used to derive the background color of the content and keep it in
|
||||
* sync with the associated app bar.
|
||||
|
@ -104,7 +83,6 @@ private const val MAXIMUM_ACCOUNT_LIMIT = 5
|
|||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun NotificationCenter(
|
||||
viewModel: NotificationCenterViewModel = hiltViewModel(),
|
||||
isVisible: Boolean,
|
||||
notificationSummaries: ImmutableList<NotificationSummary>,
|
||||
onNotificationClick: (NotificationSummary) -> Unit,
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
import androidx.lifecycle.ViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class NotificationCenterViewModel @Inject constructor(
|
||||
|
||||
): ViewModel() {}
|
|
@ -4,6 +4,7 @@ import android.os.Build
|
|||
import android.os.Parcelable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.bitwarden.vault.CipherView
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult
|
||||
|
@ -24,6 +25,7 @@ import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
|
|||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toNotificationSummary
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
|
@ -553,8 +555,9 @@ class VaultViewModel @Inject constructor(
|
|||
),
|
||||
)
|
||||
}
|
||||
mutableStateFlow.update {
|
||||
mutableStateFlow.update { it ->
|
||||
it.copy(
|
||||
notificationSummaries = vaultData.data.offlineCipherViewList.map { view -> view.toNotificationSummary()},
|
||||
viewState = vaultData.data.toViewState(
|
||||
baseIconUrl = state.baseIconUrl,
|
||||
isIconLoadingDisabled = state.isIconLoadingDisabled,
|
||||
|
|
Loading…
Reference in a new issue