mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +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)
|
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].
|
* Combines [dataState2] with the given [DataState] using the provided [transform].
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.x8bit.bitwarden.data.vault.datasource.disk
|
package com.x8bit.bitwarden.data.vault.datasource.disk
|
||||||
|
|
||||||
import com.bitwarden.vault.Cipher
|
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 com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
@ -21,6 +21,11 @@ interface VaultDiskSource {
|
||||||
*/
|
*/
|
||||||
suspend fun saveCipher(userId: String, cipher: SyncResponseJson.Cipher)
|
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].
|
* 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.FolderEntity
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.OfflineCipherEntity
|
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.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.datasource.network.model.SyncResponseJson
|
||||||
import com.x8bit.bitwarden.data.vault.repository.util.toOfflineCipher
|
import com.x8bit.bitwarden.data.vault.repository.util.toOfflineCipher
|
||||||
import com.x8bit.bitwarden.data.vault.repository.util.toOfflineCipherJson
|
import com.x8bit.bitwarden.data.vault.repository.util.toOfflineCipherJson
|
||||||
|
@ -46,6 +47,7 @@ class VaultDiskSourceImpl(
|
||||||
private val dispatcherManager: DispatcherManager,
|
private val dispatcherManager: DispatcherManager,
|
||||||
) : VaultDiskSource {
|
) : VaultDiskSource {
|
||||||
|
|
||||||
|
private val forceOfflineCiphersFlow = bufferedMutableSharedFlow<List<OfflineCipherJson>>()
|
||||||
private val forceCiphersFlow = bufferedMutableSharedFlow<List<SyncResponseJson.Cipher>>()
|
private val forceCiphersFlow = bufferedMutableSharedFlow<List<SyncResponseJson.Cipher>>()
|
||||||
private val forceCollectionsFlow =
|
private val forceCollectionsFlow =
|
||||||
bufferedMutableSharedFlow<List<SyncResponseJson.Collection>>()
|
bufferedMutableSharedFlow<List<SyncResponseJson.Collection>>()
|
||||||
|
@ -59,7 +61,9 @@ class VaultDiskSourceImpl(
|
||||||
id = cipher.id ?: "create_${UUID.randomUUID()}",
|
id = cipher.id ?: "create_${UUID.randomUUID()}",
|
||||||
userId = userId,
|
userId = userId,
|
||||||
cipherType = json.encodeToString(cipher.type),
|
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(
|
override fun getCiphers(
|
||||||
userId: String,
|
userId: String,
|
||||||
): Flow<List<SyncResponseJson.Cipher>> =
|
): Flow<List<SyncResponseJson.Cipher>> =
|
||||||
|
|
|
@ -100,6 +100,11 @@ interface CipherManager {
|
||||||
collectionIds: List<String>,
|
collectionIds: List<String>,
|
||||||
): ShareCipherResult
|
): ShareCipherResult
|
||||||
|
|
||||||
|
suspend fun updateOfflineCipher(
|
||||||
|
cipherId: String,
|
||||||
|
cipherView: CipherView,
|
||||||
|
): UpdateCipherResult
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to update a cipher.
|
* 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(
|
override suspend fun updateCipher(
|
||||||
cipherId: String,
|
cipherId: String,
|
||||||
cipherView: CipherView,
|
cipherView: CipherView,
|
||||||
|
|
|
@ -56,6 +56,13 @@ interface VaultRepository : CipherManager, VaultLockManager {
|
||||||
*/
|
*/
|
||||||
val vaultDataStateFlow: StateFlow<DataState<VaultData>>
|
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.
|
* 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.VaultData
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
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.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.toDomainsData
|
||||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkFolder
|
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkFolder
|
||||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkSend
|
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 mutableSendDataStateFlow = MutableStateFlow<DataState<SendData>>(DataState.Loading)
|
||||||
|
|
||||||
|
private val mutableOfflineCiphersStateFlow =
|
||||||
|
MutableStateFlow<DataState<List<CipherView>>>(DataState.Loading)
|
||||||
|
|
||||||
private val mutableCiphersStateFlow =
|
private val mutableCiphersStateFlow =
|
||||||
MutableStateFlow<DataState<List<CipherView>>>(DataState.Loading)
|
MutableStateFlow<DataState<List<CipherView>>>(DataState.Loading)
|
||||||
|
|
||||||
|
@ -172,18 +176,21 @@ class VaultRepositoryImpl(
|
||||||
|
|
||||||
override val vaultDataStateFlow: StateFlow<DataState<VaultData>> =
|
override val vaultDataStateFlow: StateFlow<DataState<VaultData>> =
|
||||||
combine(
|
combine(
|
||||||
|
offlineCiphersStateFlow,
|
||||||
ciphersStateFlow,
|
ciphersStateFlow,
|
||||||
foldersStateFlow,
|
foldersStateFlow,
|
||||||
collectionsStateFlow,
|
collectionsStateFlow,
|
||||||
sendDataStateFlow,
|
sendDataStateFlow,
|
||||||
) { ciphersDataState, foldersDataState, collectionsDataState, sendsDataState ->
|
) { offlineCiphersDataState, ciphersDataState, foldersDataState, collectionsDataState, sendsDataState ->
|
||||||
combineDataStates(
|
combineDataStates(
|
||||||
|
offlineCiphersDataState,
|
||||||
ciphersDataState,
|
ciphersDataState,
|
||||||
foldersDataState,
|
foldersDataState,
|
||||||
collectionsDataState,
|
collectionsDataState,
|
||||||
sendsDataState,
|
sendsDataState,
|
||||||
) { ciphersData, foldersData, collectionsData, sendsData ->
|
) { offlineCiphersData, ciphersData, foldersData, collectionsData, sendsData ->
|
||||||
VaultData(
|
VaultData(
|
||||||
|
offlineCipherViewList = offlineCiphersData,
|
||||||
cipherViewList = ciphersData,
|
cipherViewList = ciphersData,
|
||||||
fido2CredentialAutofillViewList = null,
|
fido2CredentialAutofillViewList = null,
|
||||||
folderViewList = foldersData,
|
folderViewList = foldersData,
|
||||||
|
@ -201,6 +208,9 @@ class VaultRepositoryImpl(
|
||||||
override val totpCodeFlow: Flow<TotpCodeResult>
|
override val totpCodeFlow: Flow<TotpCodeResult>
|
||||||
get() = mutableTotpCodeResultFlow.asSharedFlow()
|
get() = mutableTotpCodeResultFlow.asSharedFlow()
|
||||||
|
|
||||||
|
override val offlineCiphersStateFlow: StateFlow<DataState<List<CipherView>>>
|
||||||
|
get() = mutableOfflineCiphersStateFlow.asStateFlow();
|
||||||
|
|
||||||
override val ciphersStateFlow: StateFlow<DataState<List<CipherView>>>
|
override val ciphersStateFlow: StateFlow<DataState<List<CipherView>>>
|
||||||
get() = mutableCiphersStateFlow.asStateFlow()
|
get() = mutableCiphersStateFlow.asStateFlow()
|
||||||
|
|
||||||
|
@ -234,6 +244,11 @@ class VaultRepositoryImpl(
|
||||||
}
|
}
|
||||||
.launchIn(unconfinedScope)
|
.launchIn(unconfinedScope)
|
||||||
|
|
||||||
|
// Setup offline ciphers MutableStateFlow
|
||||||
|
mutableOfflineCiphersStateFlow
|
||||||
|
.observeWhenSubscribedAndLoggedIn(authDiskSource.userStateFlow) { activeUserId ->
|
||||||
|
observeVaultDiskOfflineCiphers(activeUserId)
|
||||||
|
}.launchIn(unconfinedScope)
|
||||||
// Setup ciphers MutableStateFlow
|
// Setup ciphers MutableStateFlow
|
||||||
mutableCiphersStateFlow
|
mutableCiphersStateFlow
|
||||||
.observeWhenSubscribedAndLoggedIn(authDiskSource.userStateFlow) { activeUserId ->
|
.observeWhenSubscribedAndLoggedIn(authDiskSource.userStateFlow) { activeUserId ->
|
||||||
|
@ -302,6 +317,7 @@ class VaultRepositoryImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun clearUnlockedData() {
|
private fun clearUnlockedData() {
|
||||||
|
mutableOfflineCiphersStateFlow.update { DataState.Loading }
|
||||||
mutableCiphersStateFlow.update { DataState.Loading }
|
mutableCiphersStateFlow.update { DataState.Loading }
|
||||||
mutableDomainsStateFlow.update { DataState.Loading }
|
mutableDomainsStateFlow.update { DataState.Loading }
|
||||||
mutableFoldersStateFlow.update { DataState.Loading }
|
mutableFoldersStateFlow.update { DataState.Loading }
|
||||||
|
@ -318,6 +334,7 @@ class VaultRepositoryImpl(
|
||||||
override fun sync() {
|
override fun sync() {
|
||||||
val userId = activeUserId ?: return
|
val userId = activeUserId ?: return
|
||||||
if (!syncJob.isCompleted) return
|
if (!syncJob.isCompleted) return
|
||||||
|
mutableOfflineCiphersStateFlow.updateToPendingOrLoading()
|
||||||
mutableCiphersStateFlow.updateToPendingOrLoading()
|
mutableCiphersStateFlow.updateToPendingOrLoading()
|
||||||
mutableDomainsStateFlow.updateToPendingOrLoading()
|
mutableDomainsStateFlow.updateToPendingOrLoading()
|
||||||
mutableFoldersStateFlow.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(
|
private fun observeVaultDiskCiphers(
|
||||||
userId: String,
|
userId: String,
|
||||||
): Flow<DataState<List<CipherView>>> =
|
): Flow<DataState<List<CipherView>>> =
|
||||||
|
@ -1029,6 +1065,11 @@ class VaultRepositoryImpl(
|
||||||
.onEach { mutableSendDataStateFlow.value = it }
|
.onEach { mutableSendDataStateFlow.value = it }
|
||||||
|
|
||||||
private fun updateVaultStateFlowsToError(throwable: Throwable) {
|
private fun updateVaultStateFlowsToError(throwable: Throwable) {
|
||||||
|
mutableOfflineCiphersStateFlow.update { currentState ->
|
||||||
|
throwable.toNetworkOrErrorState(
|
||||||
|
data = currentState.data,
|
||||||
|
)
|
||||||
|
}
|
||||||
mutableCiphersStateFlow.update { currentState ->
|
mutableCiphersStateFlow.update { currentState ->
|
||||||
throwable.toNetworkOrErrorState(
|
throwable.toNetworkOrErrorState(
|
||||||
data = currentState.data,
|
data = currentState.data,
|
||||||
|
|
|
@ -9,6 +9,7 @@ import com.bitwarden.vault.FolderView
|
||||||
/**
|
/**
|
||||||
* Represents decrypted vault data.
|
* Represents decrypted vault data.
|
||||||
*
|
*
|
||||||
|
* @param offlineCipherViewList List of decrypted ciphers from offline cache.
|
||||||
* @param cipherViewList List of decrypted ciphers.
|
* @param cipherViewList List of decrypted ciphers.
|
||||||
* @param collectionViewList List of decrypted collections.
|
* @param collectionViewList List of decrypted collections.
|
||||||
* @param folderViewList List of decrypted folders.
|
* @param folderViewList List of decrypted folders.
|
||||||
|
@ -16,6 +17,7 @@ import com.bitwarden.vault.FolderView
|
||||||
* @param fido2CredentialAutofillViewList List of decrypted fido 2 credentials.
|
* @param fido2CredentialAutofillViewList List of decrypted fido 2 credentials.
|
||||||
*/
|
*/
|
||||||
data class VaultData(
|
data class VaultData(
|
||||||
|
val offlineCipherViewList: List<CipherView>,
|
||||||
val cipherViewList: List<CipherView>,
|
val cipherViewList: List<CipherView>,
|
||||||
val collectionViewList: List<CollectionView>,
|
val collectionViewList: List<CollectionView>,
|
||||||
val folderViewList: List<FolderView>,
|
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.SyncResponseJson
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UriMatchTypeJson
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.UriMatchTypeJson
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.OfflineCipher
|
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.ZoneOffset
|
||||||
import java.time.ZonedDateTime
|
import java.time.ZonedDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
@ -111,6 +112,12 @@ fun OfflineCipher.toOfflineCipherJson(): OfflineCipherJson =
|
||||||
mergeConflict = false, // TODO: Copy from the new OfflineCipher type
|
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 =
|
fun OfflineCipherJson.toOfflineCipher(): OfflineCipher =
|
||||||
OfflineCipher(
|
OfflineCipher(
|
||||||
id = if(id.startsWith("create")) null else id,
|
id = if(id.startsWith("create")) null else id,
|
||||||
|
@ -136,6 +143,8 @@ fun OfflineCipherJson.toOfflineCipher(): OfflineCipher =
|
||||||
mergeConflict = mergeConflict
|
mergeConflict = mergeConflict
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun OfflineCipher.toCipher(): Cipher =
|
fun OfflineCipher.toCipher(): Cipher =
|
||||||
Cipher(
|
Cipher(
|
||||||
id = id,
|
id = id,
|
||||||
|
@ -427,6 +436,13 @@ private fun CipherType.toNetworkCipherType(): CipherTypeJson =
|
||||||
fun List<SyncResponseJson.Cipher>.toEncryptedSdkCipherList(): List<Cipher> =
|
fun List<SyncResponseJson.Cipher>.toEncryptedSdkCipherList(): List<Cipher> =
|
||||||
map { it.toEncryptedSdkCipher() }
|
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
|
* Converts a [SyncResponseJson.Cipher] object to a corresponding
|
||||||
* Bitwarden SDK [Cipher] object.
|
* Bitwarden SDK [Cipher] object.
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.x8bit.bitwarden.ui.vault.feature.unsyncedvaultitem
|
package com.x8bit.bitwarden.ui.vault.feature.unsyncedvaultitem
|
||||||
|
|
||||||
import android.util.Range
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.animation.core.updateTransition
|
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.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
|
||||||
import com.x8bit.bitwarden.R
|
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.scrolledContainerBackground
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.toSafeOverlayColor
|
import com.x8bit.bitwarden.ui.platform.base.util.toSafeOverlayColor
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.toUnscaledTextUnit
|
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.BitwardenSelectionDialog
|
||||||
import com.x8bit.bitwarden.ui.platform.components.dialog.row.BitwardenBasicDialogRow
|
import com.x8bit.bitwarden.ui.platform.components.dialog.row.BitwardenBasicDialogRow
|
||||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
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.scrim.BitwardenAnimatedScrim
|
||||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
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.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.ImmutableList
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
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
|
* @param isVisible Whether or not this component is visible. Changing this value will animate the
|
||||||
* component in or out of view.
|
* 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
|
* @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.
|
* 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 modifier A [Modifier] for the composable.
|
||||||
* @param topAppBarScrollBehavior Used to derive the background color of the content and keep it in
|
* @param topAppBarScrollBehavior Used to derive the background color of the content and keep it in
|
||||||
* sync with the associated app bar.
|
* sync with the associated app bar.
|
||||||
|
@ -104,7 +83,6 @@ private const val MAXIMUM_ACCOUNT_LIMIT = 5
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
@Composable
|
@Composable
|
||||||
fun NotificationCenter(
|
fun NotificationCenter(
|
||||||
viewModel: NotificationCenterViewModel = hiltViewModel(),
|
|
||||||
isVisible: Boolean,
|
isVisible: Boolean,
|
||||||
notificationSummaries: ImmutableList<NotificationSummary>,
|
notificationSummaries: ImmutableList<NotificationSummary>,
|
||||||
onNotificationClick: (NotificationSummary) -> Unit,
|
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 android.os.Parcelable
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.bitwarden.vault.CipherView
|
||||||
import com.x8bit.bitwarden.R
|
import com.x8bit.bitwarden.R
|
||||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||||
import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult
|
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.VaultRepository
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
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.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.BaseViewModel
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||||
|
@ -553,8 +555,9 @@ class VaultViewModel @Inject constructor(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
mutableStateFlow.update {
|
mutableStateFlow.update { it ->
|
||||||
it.copy(
|
it.copy(
|
||||||
|
notificationSummaries = vaultData.data.offlineCipherViewList.map { view -> view.toNotificationSummary()},
|
||||||
viewState = vaultData.data.toViewState(
|
viewState = vaultData.data.toViewState(
|
||||||
baseIconUrl = state.baseIconUrl,
|
baseIconUrl = state.baseIconUrl,
|
||||||
isIconLoadingDisabled = state.isIconLoadingDisabled,
|
isIconLoadingDisabled = state.isIconLoadingDisabled,
|
||||||
|
|
Loading…
Reference in a new issue