mirror of
https://github.com/bitwarden/android.git
synced 2025-01-08 09:17:36 +03:00
overlay offline edits with existing
This commit is contained in:
parent
d7b935031e
commit
b30e52245f
4 changed files with 77 additions and 5 deletions
|
@ -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) {
|
||||||
|
|
|
@ -87,6 +87,5 @@ data class OfflineCipherJson(
|
||||||
@SerialName("mergeConflict")
|
@SerialName("mergeConflict")
|
||||||
val mergeConflict: Boolean
|
val mergeConflict: Boolean
|
||||||
|
|
||||||
){
|
) {
|
||||||
// TODO: Add password history, fields, etc
|
|
||||||
}
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue