Yandex штуки

This commit is contained in:
2026-05-03 22:03:47 +03:00
parent be1ba29f4d
commit ad985679ee
11 changed files with 113 additions and 38 deletions

View File

@@ -23,6 +23,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
import java.util.UUID
import kotlin.system.measureTimeMillis
@@ -32,7 +33,7 @@ import kotlin.system.measureTimeMillis
*/
abstract class AbstractVaultBrowserViewModel(
storagesFlow: Flow<List<IStorage>>,
private val canAddStorage: Boolean,
private val vaultAvailabilityFlow: Flow<Boolean>,
private val resolveCreateVaultUuid: () -> UUID?,
private val removeStorageUseCase: RemoveStorageUseCase,
private val getOpenedStoragesUseCase: GetOpenedStoragesUseCase,
@@ -43,7 +44,7 @@ abstract class AbstractVaultBrowserViewModel(
private val taskOrchestrator: ITaskOrchestrator,
private val logger: ILogger,
) : ViewModelBase<VaultBrowserScreenState>(
VaultBrowserScreenState(storagesList = emptyList(), isLoading = true, canAddStorage = canAddStorage),
VaultBrowserScreenState(storagesList = emptyList(), isLoading = true, addStorageFabEnabled = false),
) {
private val _messages = MutableSharedFlow<String>()
@@ -63,6 +64,14 @@ abstract class AbstractVaultBrowserViewModel(
init {
collectFlows(storagesFlow)
viewModelScope.launch {
vaultAvailabilityFlow
.distinctUntilChanged()
.collect { available ->
updateState(state.value.copy(addStorageFabEnabled = available))
logger.debug(TAG, "vault availability → add FAB enabled=$available")
}
}
}
private fun updateStateLoading() {
@@ -125,20 +134,36 @@ abstract class AbstractVaultBrowserViewModel(
}
fun createStorage() {
if (!state.value.canAddStorage) return
if (!state.value.addStorageFabEnabled) {
logger.debug(TAG, "createStorage ignored (vault unavailable or FAB disabled)")
return
}
logger.debug(TAG, "createStorage: enqueue task")
taskOrchestrator.enqueue(
title = "Create storage",
dispatcher = Dispatchers.IO,
work = { ctx ->
ctx.log(TaskLogLevel.Info, "Creating storage…")
val uuid = resolveCreateVaultUuid()
?: throw IllegalStateException("Vault is not available")
manageVaultUseCase.createStorage(uuid)
ctx.log(TaskLogLevel.Info, "Storage created")
try {
ctx.log(TaskLogLevel.Info, "Creating storage…")
val uuid = resolveCreateVaultUuid()
?: throw IllegalStateException("Vault is not available")
logger.debug(TAG, "createStorage: vaultUuid=$uuid")
val storage = manageVaultUseCase.createStorage(uuid)
ctx.log(TaskLogLevel.Info, "Storage created")
logger.debug(TAG, "createStorage: done storageUuid=${storage.uuid}")
} catch (e: Exception) {
logger.debug(TAG, "createStorage failed: ${e.stackTraceToString()}")
ctx.log(TaskLogLevel.Error, e.message ?: e.toString())
throw e
}
},
)
}
private companion object {
private const val TAG = "VaultBrowser"
}
private val storageOpMutex = Any()
private val runningStorages = mutableSetOf<UUID>()

View File

@@ -34,7 +34,9 @@ class LocalVaultViewModel @Inject constructor(
storagesFlow = vaultsManager.vaults
.map { vaults -> vaults.described().locals.firstOrNull() }
.flatMapLatest { v -> v?.storages ?: flowOf(emptyList()) },
canAddStorage = true,
vaultAvailabilityFlow = vaultsManager.vaults
.map { vaults -> vaults.described().locals.firstOrNull() }
.flatMapLatest { v -> v?.isAvailable ?: flowOf(false) },
resolveCreateVaultUuid = { vaultsManager.vaults.value.described().locals.firstOrNull()?.uuid },
removeStorageUseCase = removeStorageUseCase,
getOpenedStoragesUseCase = getOpenedStoragesUseCase,

View File

@@ -11,6 +11,8 @@ import com.github.nullptroma.wallenc.domain.usecases.RenameStorageUseCase
import com.github.nullptroma.wallenc.domain.usecases.StorageFileManagementUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import java.util.UUID
import javax.inject.Inject
@@ -28,8 +30,9 @@ class RemoteVaultViewModel @Inject constructor(
logger: ILogger,
) : AbstractVaultBrowserViewModel(
storagesFlow = manageVaultUseCase.storagesOf(savedStateHandle.requireVaultUuid()),
canAddStorage = false,
resolveCreateVaultUuid = { null },
vaultAvailabilityFlow = manageVaultUseCase.observe(savedStateHandle.requireVaultUuid())
.flatMapLatest { v -> v?.isAvailable ?: flowOf(false) },
resolveCreateVaultUuid = { savedStateHandle.requireVaultUuid() },
removeStorageUseCase = removeStorageUseCase,
getOpenedStoragesUseCase = getOpenedStoragesUseCase,
storageFileManagementUseCase = storageFileManagementUseCase,

View File

@@ -50,12 +50,21 @@ fun VaultBrowserScreen(
modifier = modifier,
contentWindowInsets = WindowInsets(0.dp),
floatingActionButton = {
if (uiState.canAddStorage) {
FloatingActionButton(
onClick = { viewModel.createStorage() },
) {
Icon(Icons.Filled.Add, contentDescription = null)
}
val fabEnabled = uiState.addStorageFabEnabled
FloatingActionButton(
onClick = { if (fabEnabled) viewModel.createStorage() },
containerColor = if (fabEnabled) {
MaterialTheme.colorScheme.primaryContainer
} else {
MaterialTheme.colorScheme.surfaceVariant
},
contentColor = if (fabEnabled) {
MaterialTheme.colorScheme.onPrimaryContainer
} else {
MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.55f)
},
) {
Icon(Icons.Filled.Add, contentDescription = null)
}
},
) { innerPadding ->

View File

@@ -6,5 +6,6 @@ import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
data class VaultBrowserScreenState(
val storagesList: List<Tree<IStorageInfo>>,
val isLoading: Boolean,
val canAddStorage: Boolean = false,
/** FAB «добавить storage»: активна только когда vault доступен (сеть/API/путь). */
val addStorageFabEnabled: Boolean = false,
)