diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceImpl.kt index 88144ffc8..512fafbdb 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/disk/VaultDiskSourceImpl.kt @@ -19,10 +19,12 @@ 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 +import com.x8bit.bitwarden.data.vault.repository.util.toSdkCipherJson import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge @@ -83,7 +85,6 @@ class VaultDiskSourceImpl( ) } - override fun getOfflineCiphers( userId: String, ): Flow> = @@ -130,6 +131,16 @@ class VaultDiskSourceImpl( .awaitAll() } }, + ).combine( + getOfflineCiphers(userId), + { ciphers, offlineCiphers -> + val overlaid = ciphers.map { cipher -> + offlineCiphers.find { it.id == cipher.id }?.toSdkCipherJson() ?: cipher + } + // TODO add new offline items to the vault list + // val newOffline = offlineCiphers.filter { it.id.startsWith("create") }.map { it.toSdkCipherJson() } + overlaid + } ) override suspend fun deleteCipher(userId: String, cipherId: String) { diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/model/OfflineCipherJson.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/model/OfflineCipherJson.kt index bcde004d9..37de6b0b9 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/model/OfflineCipherJson.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/network/model/OfflineCipherJson.kt @@ -87,6 +87,5 @@ data class OfflineCipherJson( @SerialName("mergeConflict") val mergeConflict: Boolean -){ - // TODO: Add password history, fields, etc +) { } \ No newline at end of file diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkCipherExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkCipherExtensions.kt index 7e78f1881..1d8df5948 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkCipherExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkCipherExtensions.kt @@ -28,6 +28,7 @@ import com.bitwarden.vault.SecureNoteType import com.bitwarden.vault.SecureNoteView import com.bitwarden.vault.UriMatchType import com.x8bit.bitwarden.data.platform.util.SpecialCharWithPrecedenceComparator +import com.x8bit.bitwarden.data.platform.util.isFdroid import com.x8bit.bitwarden.data.vault.datasource.network.model.AttachmentJsonRequest import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherJsonRequest import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherRepromptTypeJson @@ -41,6 +42,7 @@ 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.repository.model.OfflineCipherView import com.x8bit.bitwarden.ui.vault.feature.vault.model.NotificationSummary +import kotlinx.coroutines.flow.merge import java.time.ZoneOffset import java.time.ZonedDateTime import java.util.UUID @@ -118,7 +120,7 @@ fun OfflineCipher.toOfflineCipherJson(id: String): OfflineCipherJson = creationDate = ZonedDateTime.ofInstant(creationDate, ZoneOffset.UTC), deletedDate = deletedDate?.let { ZonedDateTime.ofInstant(deletedDate, ZoneOffset.UTC) }, revisionDate = ZonedDateTime.ofInstant(creationDate, ZoneOffset.UTC), - mergeConflict = false, // TODO: Copy from the new OfflineCipher type + mergeConflict = mergeConflict, ) fun OfflineCipherView.toNotificationSummary(): NotificationSummary = @@ -159,6 +161,60 @@ fun CipherView.toOfflineCipherView(offlineCipher: OfflineCipher) = mergeConflict = offlineCipher.mergeConflict ) +fun SyncResponseJson.Cipher.overlayOfflineCipherJson(offlineCipherJson: OfflineCipherJson) = + SyncResponseJson.Cipher( + notes = offlineCipherJson.notes, + attachments = offlineCipherJson.attachments, + shouldOrganizationUseTotp = shouldOrganizationUseTotp, + reprompt = offlineCipherJson.reprompt, + shouldEdit = shouldEdit, + passwordHistory = offlineCipherJson.passwordHistory, + revisionDate = offlineCipherJson.revisionDate, + type = offlineCipherJson.type, + login = offlineCipherJson.login, + creationDate = offlineCipherJson.creationDate, + secureNote = offlineCipherJson.secureNote, + folderId = offlineCipherJson.folderId, + organizationId = offlineCipherJson.organizationId, + deletedDate = offlineCipherJson.deletedDate, + identity = offlineCipherJson.identity, + collectionIds = offlineCipherJson.collectionIds, + name = offlineCipherJson.name, + id = id, + fields = offlineCipherJson.fields, + shouldViewPassword = shouldViewPassword, + isFavorite = offlineCipherJson.favorite, + card = offlineCipherJson.card, + key = offlineCipherJson.key + ) + +fun OfflineCipherJson.toSdkCipherJson(): SyncResponseJson.Cipher = + SyncResponseJson.Cipher( + id = id, // TODO, the "create_..." id is invalid, but it's not clear what's better + notes = notes, + attachments = attachments, + shouldOrganizationUseTotp = false, // TODO + reprompt = reprompt, + shouldEdit = false, // TODO + passwordHistory = passwordHistory, + revisionDate = revisionDate, + type = type, + login = login, + creationDate = creationDate, + secureNote = secureNote, + folderId = folderId, + organizationId = organizationId, + deletedDate = deletedDate, + identity = identity, + collectionIds = collectionIds, + name = name, + fields = fields, + shouldViewPassword = false, // TODO + isFavorite = favorite, + card = card, + key = key, + ) + fun OfflineCipherJson.toOfflineCipher(): OfflineCipher = OfflineCipher( id = if (id.startsWith("create")) null else id, diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt index 016f2c367..f1f449957 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt @@ -412,10 +412,16 @@ class VaultAddEditViewModel @Inject constructor( } is VaultAddEditType.EditItem -> { - val result = vaultRepository.updateCipher( + var result = vaultRepository.updateCipher( cipherId = vaultAddEditType.vaultItemId, cipherView = content.toCipherView(), ) + + if (result is UpdateCipherResult.Error) { + // TODO: Ask for permission to store locally + result = vaultRepository.updateOfflineCipher(cipherId = vaultAddEditType.vaultItemId, cipherView = content.toCipherView()) + } + sendAction(VaultAddEditAction.Internal.UpdateCipherResultReceive(result)) }