Улучшение UI/UX
This commit is contained in:
@@ -4,6 +4,11 @@ import com.github.nullptroma.wallenc.domain.datatypes.TextSecretEntryRecord
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.TextSecretRecord
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
@@ -20,9 +25,14 @@ import java.util.UUID
|
||||
class ManageTextSecretsUseCase {
|
||||
private val mutex = Mutex()
|
||||
|
||||
suspend fun list(storageInfo: IStorageInfo): List<TextSecretRecord> = mutex.withLock {
|
||||
val storage = storageInfo as? IStorage ?: return@withLock emptyList()
|
||||
readAll(storage)
|
||||
fun observe(storageInfo: IStorageInfo): Flow<List<TextSecretRecord>> {
|
||||
val storage = storageInfo as? IStorage ?: return flowOf(emptyList())
|
||||
return merge(
|
||||
flowOf(Unit),
|
||||
storage.accessor.filesUpdates.map { Unit },
|
||||
).map {
|
||||
mutex.withLock { readAll(storage) }
|
||||
}.distinctUntilChanged()
|
||||
}
|
||||
|
||||
suspend fun get(storageInfo: IStorageInfo, id: String): TextSecretRecord? = mutex.withLock {
|
||||
|
||||
@@ -3,6 +3,11 @@ package com.github.nullptroma.wallenc.usecases
|
||||
import com.github.nullptroma.wallenc.domain.datatypes.TwoFaTokenRecord
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
|
||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
@@ -16,9 +21,14 @@ import java.util.UUID
|
||||
class ManageTwoFaTokensUseCase {
|
||||
private val mutex = Mutex()
|
||||
|
||||
suspend fun list(storageInfo: IStorageInfo): List<TwoFaTokenRecord> = mutex.withLock {
|
||||
val storage = storageInfo as? IStorage ?: return@withLock emptyList()
|
||||
readAll(storage)
|
||||
fun observe(storageInfo: IStorageInfo): Flow<List<TwoFaTokenRecord>> {
|
||||
val storage = storageInfo as? IStorage ?: return flowOf(emptyList())
|
||||
return merge(
|
||||
flowOf(Unit),
|
||||
storage.accessor.filesUpdates.map { Unit },
|
||||
).map {
|
||||
mutex.withLock { readAll(storage) }
|
||||
}.distinctUntilChanged()
|
||||
}
|
||||
|
||||
suspend fun get(storageInfo: IStorageInfo, id: String): TwoFaTokenRecord? = mutex.withLock {
|
||||
|
||||
@@ -73,7 +73,7 @@ class StorageSyncEngine(
|
||||
return
|
||||
}
|
||||
|
||||
val leaseUntil = Instant.MAX
|
||||
val leaseUntil = Instant.now().plusSeconds(SYNC_LOCK_LEASE_SECONDS)
|
||||
val lockedAccessors = mutableListOf<IStorageAccessor>()
|
||||
try {
|
||||
reportProgress(null, "Storage sync: group \"$groupId\" acquiring locks")
|
||||
@@ -223,4 +223,8 @@ class StorageSyncEngine(
|
||||
}
|
||||
return a.revision.createdAt.compareTo(b.revision.createdAt)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val SYNC_LOCK_LEASE_SECONDS: Long = 30 * 60
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
@@ -46,7 +47,7 @@ class StorageDomainUseCasesTest {
|
||||
notes = "primary",
|
||||
)
|
||||
assertNotNull(created.id)
|
||||
assertEquals(1, useCase.list(storage).size)
|
||||
assertEquals(1, useCase.observe(storage).first().size)
|
||||
|
||||
val updated = useCase.update(
|
||||
storageInfo = storage,
|
||||
@@ -57,7 +58,7 @@ class StorageDomainUseCasesTest {
|
||||
|
||||
val removed = useCase.delete(storage, created.id)
|
||||
assertTrue(removed)
|
||||
assertTrue(useCase.list(storage).isEmpty())
|
||||
assertTrue(useCase.observe(storage).first().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -66,7 +67,7 @@ class StorageDomainUseCasesTest {
|
||||
setDomainFile(StorageDomainDataFiles.TWO_FA_TOKENS_FILE, "not-json")
|
||||
}
|
||||
val useCase = ManageTwoFaTokensUseCase()
|
||||
assertTrue(useCase.list(storage).isEmpty())
|
||||
assertTrue(useCase.observe(storage).first().isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -82,7 +83,7 @@ class StorageDomainUseCasesTest {
|
||||
TextSecretEntryRecord(label = null, value = "password"),
|
||||
),
|
||||
)
|
||||
assertEquals(1, useCase.list(storage).size)
|
||||
assertEquals(1, useCase.observe(storage).first().size)
|
||||
|
||||
val updated = useCase.update(
|
||||
storageInfo = storage,
|
||||
@@ -107,7 +108,7 @@ class StorageDomainUseCasesTest {
|
||||
setDomainFile(StorageDomainDataFiles.TEXT_SECRETS_FILE, "{broken")
|
||||
}
|
||||
val useCase = ManageTextSecretsUseCase()
|
||||
assertTrue(useCase.list(storage).isEmpty())
|
||||
assertTrue(useCase.observe(storage).first().isEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,11 +144,12 @@ private class FakeMetaInfo : IStorageMetaInfo {
|
||||
private class FakeStorageAccessor : IStorageAccessor {
|
||||
val dataFiles: MutableMap<String, ByteArray> = mutableMapOf()
|
||||
private val systemFiles: MutableMap<String, ByteArray> = mutableMapOf()
|
||||
private val _filesUpdates = MutableSharedFlow<DataPage<IFile>>(extraBufferCapacity = 16)
|
||||
|
||||
override val size: StateFlow<Long?> = MutableStateFlow(0L)
|
||||
override val numberOfFiles: StateFlow<Int?> = MutableStateFlow(0)
|
||||
override val isAvailable: StateFlow<Boolean> = MutableStateFlow(true)
|
||||
override val filesUpdates: SharedFlow<DataPage<IFile>> = MutableSharedFlow()
|
||||
override val filesUpdates: SharedFlow<DataPage<IFile>> = _filesUpdates
|
||||
override val dirsUpdates: SharedFlow<DataPage<IDirectory>> = MutableSharedFlow()
|
||||
|
||||
override suspend fun getAllFiles(): List<IFile> = emptyList()
|
||||
@@ -182,6 +184,7 @@ private class FakeStorageAccessor : IStorageAccessor {
|
||||
return object : ByteArrayOutputStream() {
|
||||
override fun close() {
|
||||
dataFiles[path] = toByteArray()
|
||||
_filesUpdates.tryEmit(DataPage(list = emptyList(), pageLength = 1, pageIndex = 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user