overlay offline edits with existing

This commit is contained in:
Matt Gibson 2024-10-24 15:08:15 -07:00
parent d7b935031e
commit b30e52245f
No known key found for this signature in database
GPG key ID: 7CBCA182C13B0912
4 changed files with 77 additions and 5 deletions

View file

@ -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.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
import com.x8bit.bitwarden.data.vault.repository.util.toSdkCipherJson
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.merge
@ -83,7 +85,6 @@ class VaultDiskSourceImpl(
) )
} }
override fun getOfflineCiphers( override fun getOfflineCiphers(
userId: String, userId: String,
): Flow<List<OfflineCipherJson>> = ): Flow<List<OfflineCipherJson>> =
@ -130,6 +131,16 @@ class VaultDiskSourceImpl(
.awaitAll() .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) { override suspend fun deleteCipher(userId: String, cipherId: String) {

View file

@ -87,6 +87,5 @@ data class OfflineCipherJson(
@SerialName("mergeConflict") @SerialName("mergeConflict")
val mergeConflict: Boolean val mergeConflict: Boolean
){ ) {
// TODO: Add password history, fields, etc
} }

View file

@ -28,6 +28,7 @@ import com.bitwarden.vault.SecureNoteType
import com.bitwarden.vault.SecureNoteView import com.bitwarden.vault.SecureNoteView
import com.bitwarden.vault.UriMatchType import com.bitwarden.vault.UriMatchType
import com.x8bit.bitwarden.data.platform.util.SpecialCharWithPrecedenceComparator 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.AttachmentJsonRequest
import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherJsonRequest import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherJsonRequest
import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherRepromptTypeJson 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.datasource.sdk.model.OfflineCipher
import com.x8bit.bitwarden.data.vault.repository.model.OfflineCipherView import com.x8bit.bitwarden.data.vault.repository.model.OfflineCipherView
import com.x8bit.bitwarden.ui.vault.feature.vault.model.NotificationSummary import com.x8bit.bitwarden.ui.vault.feature.vault.model.NotificationSummary
import kotlinx.coroutines.flow.merge
import java.time.ZoneOffset import java.time.ZoneOffset
import java.time.ZonedDateTime import java.time.ZonedDateTime
import java.util.UUID import java.util.UUID
@ -118,7 +120,7 @@ fun OfflineCipher.toOfflineCipherJson(id: String): OfflineCipherJson =
creationDate = ZonedDateTime.ofInstant(creationDate, ZoneOffset.UTC), creationDate = ZonedDateTime.ofInstant(creationDate, ZoneOffset.UTC),
deletedDate = deletedDate?.let { ZonedDateTime.ofInstant(deletedDate, ZoneOffset.UTC) }, deletedDate = deletedDate?.let { ZonedDateTime.ofInstant(deletedDate, ZoneOffset.UTC) },
revisionDate = ZonedDateTime.ofInstant(creationDate, ZoneOffset.UTC), revisionDate = ZonedDateTime.ofInstant(creationDate, ZoneOffset.UTC),
mergeConflict = false, // TODO: Copy from the new OfflineCipher type mergeConflict = mergeConflict,
) )
fun OfflineCipherView.toNotificationSummary(): NotificationSummary = fun OfflineCipherView.toNotificationSummary(): NotificationSummary =
@ -159,6 +161,60 @@ fun CipherView.toOfflineCipherView(offlineCipher: OfflineCipher) =
mergeConflict = offlineCipher.mergeConflict 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 = fun OfflineCipherJson.toOfflineCipher(): OfflineCipher =
OfflineCipher( OfflineCipher(
id = if (id.startsWith("create")) null else id, id = if (id.startsWith("create")) null else id,

View file

@ -412,10 +412,16 @@ class VaultAddEditViewModel @Inject constructor(
} }
is VaultAddEditType.EditItem -> { is VaultAddEditType.EditItem -> {
val result = vaultRepository.updateCipher( var result = vaultRepository.updateCipher(
cipherId = vaultAddEditType.vaultItemId, cipherId = vaultAddEditType.vaultItemId,
cipherView = content.toCipherView(), 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)) sendAction(VaultAddEditAction.Internal.UpdateCipherResultReceive(result))
} }