From 78aa776adca2185e19531b81c3322659e5e596bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D1=8B=D1=82=D0=BA=D0=BE=D0=B2=20=D0=A0=D0=BE=D0=BC?= =?UTF-8?q?=D0=B0=D0=BD?= Date: Sun, 3 May 2026 19:47:18 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D1=89=D0=B8=D0=B9=20VaultScreen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/screens/main/MainScreen.kt | 28 ++++++- .../screens/main/MainViewModel.kt | 2 +- .../main/screens/remotes/RemoteVaultsRoute.kt | 2 +- .../screens/remotes/RemoteVaultsScreen.kt | 11 ++- .../main/screens/tasks/TaskPipelineScreen.kt | 2 +- .../screens/tasks/TaskPipelineViewModel.kt | 1 - .../AbstractVaultBrowserViewModel.kt} | 83 +++++++++---------- .../{local => }/vault/LocalVaultRoute.kt | 4 +- .../main/screens/vault/LocalVaultScreen.kt | 18 ++++ .../main/screens/vault/LocalVaultViewModel.kt | 47 +++++++++++ .../screens/vault/RemoteVaultViewModel.kt | 46 ++++++++++ .../main/screens/vault/VaultBrowserRoute.kt | 9 ++ .../VaultBrowserScreen.kt} | 64 ++++++-------- .../VaultBrowserScreenState.kt} | 8 +- 14 files changed, 226 insertions(+), 99 deletions(-) rename presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/{local/vault/LocalVaultViewModel.kt => vault/AbstractVaultBrowserViewModel.kt} (86%) rename presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/{local => }/vault/LocalVaultRoute.kt (81%) create mode 100644 presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/LocalVaultScreen.kt create mode 100644 presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/LocalVaultViewModel.kt create mode 100644 presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/RemoteVaultViewModel.kt create mode 100644 presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/VaultBrowserRoute.kt rename presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/{local/vault/LocalVaultScreen.kt => vault/VaultBrowserScreen.kt} (66%) rename presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/{local/vault/LocalVaultScreenState.kt => vault/VaultBrowserScreenState.kt} (53%) diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/MainScreen.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/MainScreen.kt index 5405fc5..b0e2b15 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/MainScreen.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/MainScreen.kt @@ -27,12 +27,15 @@ import com.github.nullptroma.wallenc.presentation.R import com.github.nullptroma.wallenc.presentation.navigation.NavBarItemData import com.github.nullptroma.wallenc.presentation.navigation.NavigationState import com.github.nullptroma.wallenc.presentation.navigation.rememberNavigationState -import com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault.LocalVaultRoute -import com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault.LocalVaultScreen -import com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault.LocalVaultViewModel import com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes.RemoteVaultsRoute import com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes.RemoteVaultsScreen import com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes.RemoteVaultsViewModel +import com.github.nullptroma.wallenc.presentation.screens.main.screens.vault.LocalVaultRoute +import com.github.nullptroma.wallenc.presentation.screens.main.screens.vault.LocalVaultScreen +import com.github.nullptroma.wallenc.presentation.screens.main.screens.vault.LocalVaultViewModel +import com.github.nullptroma.wallenc.presentation.screens.main.screens.vault.RemoteVaultViewModel +import com.github.nullptroma.wallenc.presentation.screens.main.screens.vault.VaultBrowserRoute +import com.github.nullptroma.wallenc.presentation.screens.main.screens.vault.VaultBrowserScreen import com.github.nullptroma.wallenc.presentation.screens.shared.TextEditRoute import com.github.nullptroma.wallenc.presentation.screens.shared.TextEditScreen @@ -110,7 +113,24 @@ fun MainScreen( }) { RemoteVaultsScreen( modifier = Modifier.padding(innerPaddings), - viewModel = remoteVaultsViewModel + viewModel = remoteVaultsViewModel, + onOpenVault = { item -> + navState.push(VaultBrowserRoute(item.uuid.toString())) + }, + ) + } + composable(enterTransition = { + fadeIn(tween(200)) + }, exitTransition = { + fadeOut(tween(200)) + }) { entry -> + val remoteVaultViewModel: RemoteVaultViewModel = hiltViewModel(entry) + VaultBrowserScreen( + modifier = Modifier.padding(innerPaddings), + viewModel = remoteVaultViewModel, + openTextEdit = { text -> + navState.push(TextEditRoute(text)) + }, ) } composable { diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/MainViewModel.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/MainViewModel.kt index 843191a..d51f26e 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/MainViewModel.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/MainViewModel.kt @@ -5,8 +5,8 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi import androidx.lifecycle.viewmodel.compose.saveable import com.github.nullptroma.wallenc.presentation.screens.ScreenRoute -import com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault.LocalVaultRoute import com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes.RemoteVaultsRoute +import com.github.nullptroma.wallenc.presentation.screens.main.screens.vault.LocalVaultRoute import com.github.nullptroma.wallenc.presentation.ViewModelBase import dagger.hilt.android.lifecycle.HiltViewModel diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/remotes/RemoteVaultsRoute.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/remotes/RemoteVaultsRoute.kt index db679a4..1ffea44 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/remotes/RemoteVaultsRoute.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/remotes/RemoteVaultsRoute.kt @@ -6,4 +6,4 @@ import kotlinx.serialization.Serializable @Serializable @Parcelize -class RemoteVaultsRoute: MainRoute() \ No newline at end of file +class RemoteVaultsRoute : MainRoute() diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/remotes/RemoteVaultsScreen.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/remotes/RemoteVaultsScreen.kt index 22604d2..8421380 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/remotes/RemoteVaultsScreen.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/remotes/RemoteVaultsScreen.kt @@ -34,6 +34,7 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -41,7 +42,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog -import androidx.compose.runtime.getValue import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.github.nullptroma.wallenc.presentation.R @@ -52,6 +52,7 @@ import com.github.nullptroma.wallenc.vaultapi.VaultLinkOutcome fun RemoteVaultsScreen( modifier: Modifier = Modifier, viewModel: RemoteVaultsViewModel = hiltViewModel(), + onOpenVault: (RemoteVaultListItem) -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() val context = LocalContext.current @@ -92,7 +93,13 @@ fun RemoteVaultsScreen( ) { items(uiState.vaults, key = { it.uuid }) { item -> ElevatedCard( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .clickable( + enabled = !uiState.isBusy, + interactionSource = remember { MutableInteractionSource() }, + indication = null, + ) { onOpenVault(item) }, shape = RoundedCornerShape(16.dp), elevation = CardDefaults.elevatedCardElevation(defaultElevation = 2.dp), colors = CardDefaults.elevatedCardColors( diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/tasks/TaskPipelineScreen.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/tasks/TaskPipelineScreen.kt index 83b8545..11a694f 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/tasks/TaskPipelineScreen.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/tasks/TaskPipelineScreen.kt @@ -4,10 +4,10 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.AlertDialog diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/tasks/TaskPipelineViewModel.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/tasks/TaskPipelineViewModel.kt index a3c5549..4d2de66 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/tasks/TaskPipelineViewModel.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/tasks/TaskPipelineViewModel.kt @@ -2,7 +2,6 @@ package com.github.nullptroma.wallenc.presentation.screens.main.screens.tasks import androidx.lifecycle.ViewModel import com.github.nullptroma.wallenc.domain.tasks.ITaskOrchestrator -import com.github.nullptroma.wallenc.domain.tasks.PipelineWork import com.github.nullptroma.wallenc.domain.tasks.TaskLogLevel import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultViewModel.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/AbstractVaultBrowserViewModel.kt similarity index 86% rename from presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultViewModel.kt rename to presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/AbstractVaultBrowserViewModel.kt index f0746a3..f46a332 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultViewModel.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/AbstractVaultBrowserViewModel.kt @@ -1,4 +1,4 @@ -package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault +package com.github.nullptroma.wallenc.presentation.screens.main.screens.vault import androidx.lifecycle.viewModelScope import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey @@ -6,8 +6,8 @@ import com.github.nullptroma.wallenc.domain.datatypes.Tree import com.github.nullptroma.wallenc.domain.interfaces.IDirectory import com.github.nullptroma.wallenc.domain.interfaces.IFile import com.github.nullptroma.wallenc.domain.interfaces.ILogger +import com.github.nullptroma.wallenc.domain.interfaces.IStorage import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo -import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager import com.github.nullptroma.wallenc.domain.tasks.ITaskOrchestrator import com.github.nullptroma.wallenc.domain.tasks.TaskLogLevel import com.github.nullptroma.wallenc.domain.usecases.GetOpenedStoragesUseCase @@ -18,76 +18,66 @@ import com.github.nullptroma.wallenc.domain.usecases.RenameStorageUseCase import com.github.nullptroma.wallenc.domain.usecases.StorageFileManagementUseCase import com.github.nullptroma.wallenc.presentation.ViewModelBase import com.github.nullptroma.wallenc.presentation.extensions.toPrintable -import com.github.nullptroma.wallenc.vaultapi.described -import com.github.nullptroma.wallenc.vaultapi.locals -import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import java.util.UUID -import javax.inject.Inject import kotlin.system.measureTimeMillis -@OptIn(ExperimentalCoroutinesApi::class) -@HiltViewModel -class LocalVaultViewModel @Inject constructor( - private val vaultsManager: IVaultsManager, - private val manageVaultUseCase: ManageVaultUseCase, +/** + * Общая логика дерева storages для локального и удалённого vault (presentation). + */ +abstract class AbstractVaultBrowserViewModel( + storagesFlow: Flow>, + private val canAddStorage: Boolean, + private val resolveCreateVaultUuid: () -> UUID?, private val removeStorageUseCase: RemoveStorageUseCase, private val getOpenedStoragesUseCase: GetOpenedStoragesUseCase, private val storageFileManagementUseCase: StorageFileManagementUseCase, private val manageStoragesEncryptionUseCase: ManageStoragesEncryptionUseCase, private val renameStorageUseCase: RenameStorageUseCase, + private val manageVaultUseCase: ManageVaultUseCase, private val taskOrchestrator: ITaskOrchestrator, - private val logger: ILogger -) : ViewModelBase(LocalVaultScreenState(listOf(), true)) { + private val logger: ILogger, +) : ViewModelBase( + VaultBrowserScreenState(storagesList = emptyList(), isLoading = true, canAddStorage = canAddStorage), +) { - private val localVaultUuid: UUID? - get() = vaultsManager.vaults.value.described().locals.firstOrNull()?.uuid - - private val localStoragesFlow = vaultsManager.vaults - .map { vaults -> vaults.described().locals.firstOrNull() } - .flatMapLatest { v -> v?.storages ?: flowOf(emptyList()) } private val _messages = MutableSharedFlow() val messages: SharedFlow = _messages - private var _taskCount: Int = 0 - private var tasksCount - get() = _taskCount + private var taskCount: Int = 0 set(value) { - _taskCount = value + field = value updateStateLoading() } - private var _isLoading: Boolean = false - private var isLoading - get() = _isLoading + private var storagesLoading: Boolean = false set(value) { - _isLoading = value + field = value updateStateLoading() } init { - collectFlows() + collectFlows(storagesFlow) } private fun updateStateLoading() { - updateState(state.value.copy( - isLoading = this.isLoading || this.tasksCount > 0 - )) + updateState( + state.value.copy( + isLoading = storagesLoading || taskCount > 0, + ), + ) } - private fun collectFlows() { + private fun collectFlows(storagesFlow: Flow>) { viewModelScope.launch { - localStoragesFlow.combine(getOpenedStoragesUseCase.openedStorages) { local, opened -> + storagesFlow.combine(getOpenedStoragesUseCase.openedStorages) { storages, opened -> val list = mutableListOf>() - for (storage in local) { + for (storage in storages) { var tree = Tree(storage) list.add(tree) while (opened.containsKey(tree.value.uuid)) { @@ -99,7 +89,7 @@ class LocalVaultViewModel @Inject constructor( } list }.collect { trees -> - isLoading = false + storagesLoading = false updateState(state.value.copy(storagesList = trees)) } } @@ -135,13 +125,14 @@ class LocalVaultViewModel @Inject constructor( } fun createStorage() { + if (!state.value.canAddStorage) return taskOrchestrator.enqueue( title = "Create storage", dispatcher = Dispatchers.IO, work = { ctx -> ctx.log(TaskLogLevel.Info, "Creating storage…") - val uuid = localVaultUuid - ?: throw IllegalStateException("Local vault is not registered") + val uuid = resolveCreateVaultUuid() + ?: throw IllegalStateException("Vault is not available") manageVaultUseCase.createStorage(uuid) ctx.log(TaskLogLevel.Info, "Storage created") }, @@ -156,7 +147,7 @@ class LocalVaultViewModel @Inject constructor( synchronized(storageOpMutex) { if (runningStorages.contains(id)) return runningStorages.add(id) - tasksCount++ + taskCount++ } val key = EncryptKey(password) taskOrchestrator.enqueue( @@ -196,7 +187,7 @@ class LocalVaultViewModel @Inject constructor( } finally { synchronized(storageOpMutex) { runningStorages.remove(id) - tasksCount-- + taskCount-- } } }, @@ -208,7 +199,7 @@ class LocalVaultViewModel @Inject constructor( synchronized(storageOpMutex) { if (runningStorages.contains(id)) return runningStorages.add(id) - tasksCount++ + taskCount++ } val key = EncryptKey(password) taskOrchestrator.enqueue( @@ -225,7 +216,7 @@ class LocalVaultViewModel @Inject constructor( } finally { synchronized(storageOpMutex) { runningStorages.remove(id) - tasksCount-- + taskCount-- } } }, @@ -312,4 +303,4 @@ class LocalVaultViewModel @Inject constructor( val openedMap = getOpenedStoragesUseCase.openedStorages.value return openedMap.containsKey(storage.uuid) } -} \ No newline at end of file +} diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultRoute.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/LocalVaultRoute.kt similarity index 81% rename from presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultRoute.kt rename to presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/LocalVaultRoute.kt index 6f29dab..3c4cd94 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultRoute.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/LocalVaultRoute.kt @@ -1,4 +1,4 @@ -package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault +package com.github.nullptroma.wallenc.presentation.screens.main.screens.vault import com.github.nullptroma.wallenc.presentation.screens.main.MainRoute import kotlinx.parcelize.Parcelize @@ -6,4 +6,4 @@ import kotlinx.serialization.Serializable @Serializable @Parcelize -class LocalVaultRoute: MainRoute() \ No newline at end of file +class LocalVaultRoute : MainRoute() diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/LocalVaultScreen.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/LocalVaultScreen.kt new file mode 100644 index 0000000..a11bb8e --- /dev/null +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/LocalVaultScreen.kt @@ -0,0 +1,18 @@ +package com.github.nullptroma.wallenc.presentation.screens.main.screens.vault + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.hilt.navigation.compose.hiltViewModel + +@Composable +fun LocalVaultScreen( + modifier: Modifier = Modifier, + viewModel: LocalVaultViewModel = hiltViewModel(), + openTextEdit: (String) -> Unit, +) { + VaultBrowserScreen( + modifier = modifier, + viewModel = viewModel, + openTextEdit = openTextEdit, + ) +} diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/LocalVaultViewModel.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/LocalVaultViewModel.kt new file mode 100644 index 0000000..67a8cf3 --- /dev/null +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/LocalVaultViewModel.kt @@ -0,0 +1,47 @@ +package com.github.nullptroma.wallenc.presentation.screens.main.screens.vault + +import com.github.nullptroma.wallenc.domain.interfaces.ILogger +import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager +import com.github.nullptroma.wallenc.domain.tasks.ITaskOrchestrator +import com.github.nullptroma.wallenc.domain.usecases.GetOpenedStoragesUseCase +import com.github.nullptroma.wallenc.domain.usecases.ManageStoragesEncryptionUseCase +import com.github.nullptroma.wallenc.domain.usecases.ManageVaultUseCase +import com.github.nullptroma.wallenc.domain.usecases.RemoveStorageUseCase +import com.github.nullptroma.wallenc.domain.usecases.RenameStorageUseCase +import com.github.nullptroma.wallenc.domain.usecases.StorageFileManagementUseCase +import com.github.nullptroma.wallenc.vaultapi.described +import com.github.nullptroma.wallenc.vaultapi.locals +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +@OptIn(ExperimentalCoroutinesApi::class) +@HiltViewModel +class LocalVaultViewModel @Inject constructor( + vaultsManager: IVaultsManager, + manageVaultUseCase: ManageVaultUseCase, + removeStorageUseCase: RemoveStorageUseCase, + getOpenedStoragesUseCase: GetOpenedStoragesUseCase, + storageFileManagementUseCase: StorageFileManagementUseCase, + manageStoragesEncryptionUseCase: ManageStoragesEncryptionUseCase, + renameStorageUseCase: RenameStorageUseCase, + taskOrchestrator: ITaskOrchestrator, + logger: ILogger, +) : AbstractVaultBrowserViewModel( + storagesFlow = vaultsManager.vaults + .map { vaults -> vaults.described().locals.firstOrNull() } + .flatMapLatest { v -> v?.storages ?: flowOf(emptyList()) }, + canAddStorage = true, + resolveCreateVaultUuid = { vaultsManager.vaults.value.described().locals.firstOrNull()?.uuid }, + removeStorageUseCase = removeStorageUseCase, + getOpenedStoragesUseCase = getOpenedStoragesUseCase, + storageFileManagementUseCase = storageFileManagementUseCase, + manageStoragesEncryptionUseCase = manageStoragesEncryptionUseCase, + renameStorageUseCase = renameStorageUseCase, + manageVaultUseCase = manageVaultUseCase, + taskOrchestrator = taskOrchestrator, + logger = logger, +) diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/RemoteVaultViewModel.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/RemoteVaultViewModel.kt new file mode 100644 index 0000000..9950008 --- /dev/null +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/RemoteVaultViewModel.kt @@ -0,0 +1,46 @@ +package com.github.nullptroma.wallenc.presentation.screens.main.screens.vault + +import androidx.lifecycle.SavedStateHandle +import com.github.nullptroma.wallenc.domain.interfaces.ILogger +import com.github.nullptroma.wallenc.domain.tasks.ITaskOrchestrator +import com.github.nullptroma.wallenc.domain.usecases.GetOpenedStoragesUseCase +import com.github.nullptroma.wallenc.domain.usecases.ManageStoragesEncryptionUseCase +import com.github.nullptroma.wallenc.domain.usecases.ManageVaultUseCase +import com.github.nullptroma.wallenc.domain.usecases.RemoveStorageUseCase +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 java.util.UUID +import javax.inject.Inject + +@OptIn(ExperimentalCoroutinesApi::class) +@HiltViewModel +class RemoteVaultViewModel @Inject constructor( + savedStateHandle: SavedStateHandle, + manageVaultUseCase: ManageVaultUseCase, + removeStorageUseCase: RemoveStorageUseCase, + getOpenedStoragesUseCase: GetOpenedStoragesUseCase, + storageFileManagementUseCase: StorageFileManagementUseCase, + manageStoragesEncryptionUseCase: ManageStoragesEncryptionUseCase, + renameStorageUseCase: RenameStorageUseCase, + taskOrchestrator: ITaskOrchestrator, + logger: ILogger, +) : AbstractVaultBrowserViewModel( + storagesFlow = manageVaultUseCase.storagesOf(savedStateHandle.requireVaultUuid()), + canAddStorage = false, + resolveCreateVaultUuid = { null }, + removeStorageUseCase = removeStorageUseCase, + getOpenedStoragesUseCase = getOpenedStoragesUseCase, + storageFileManagementUseCase = storageFileManagementUseCase, + manageStoragesEncryptionUseCase = manageStoragesEncryptionUseCase, + renameStorageUseCase = renameStorageUseCase, + manageVaultUseCase = manageVaultUseCase, + taskOrchestrator = taskOrchestrator, + logger = logger, +) + +private fun SavedStateHandle.requireVaultUuid(): UUID { + val raw = get("vaultUuid") ?: error("Missing vault UUID in navigation arguments") + return UUID.fromString(raw) +} diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/VaultBrowserRoute.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/VaultBrowserRoute.kt new file mode 100644 index 0000000..cc4deb2 --- /dev/null +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/VaultBrowserRoute.kt @@ -0,0 +1,9 @@ +package com.github.nullptroma.wallenc.presentation.screens.main.screens.vault + +import com.github.nullptroma.wallenc.presentation.screens.ScreenRoute +import kotlinx.parcelize.Parcelize +import kotlinx.serialization.Serializable + +@Serializable +@Parcelize +data class VaultBrowserRoute(val vaultUuid: String) : ScreenRoute() diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreen.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/VaultBrowserScreen.kt similarity index 66% rename from presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreen.kt rename to presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/VaultBrowserScreen.kt index 2e8a7b1..7ef6ba6 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreen.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/VaultBrowserScreen.kt @@ -1,4 +1,4 @@ -package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault +package com.github.nullptroma.wallenc.presentation.screens.main.screens.vault import android.widget.Toast import androidx.compose.foundation.background @@ -27,18 +27,16 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.github.nullptroma.wallenc.presentation.elements.StorageTree import com.github.nullptroma.wallenc.presentation.extensions.gesturesDisabled @Composable -fun LocalVaultScreen( +fun VaultBrowserScreen( modifier: Modifier = Modifier, - viewModel: LocalVaultViewModel = hiltViewModel(), + viewModel: AbstractVaultBrowserViewModel, openTextEdit: (String) -> Unit, ) { - val uiState by viewModel.state.collectAsStateWithLifecycle() val context = LocalContext.current LaunchedEffect(Unit) { @@ -52,55 +50,44 @@ fun LocalVaultScreen( modifier = modifier, contentWindowInsets = WindowInsets(0.dp), floatingActionButton = { - FloatingActionButton( - onClick = { - viewModel.createStorage() - }, + if (uiState.canAddStorage) { + FloatingActionButton( + onClick = { viewModel.createStorage() }, + ) { + Icon(Icons.Filled.Add, contentDescription = null) + } + } + }, + ) { innerPadding -> + LazyColumn( + modifier = Modifier + .padding(innerPadding) + .gesturesDisabled(uiState.isLoading), ) { - Icon(Icons.Filled.Add, "Floating action button.") - } - }) { innerPadding -> - LazyColumn(modifier = Modifier.padding(innerPadding).gesturesDisabled(uiState.isLoading)) { items(uiState.storagesList) { listItem -> StorageTree( modifier = Modifier.padding(8.dp, 8.dp, 8.dp, 0.dp), tree = listItem, - onClick = { - openTextEdit(it.value.uuid.toString()) - }, - onRename = { tree, newName -> - viewModel.rename(tree.value, newName) - }, - onRemove = { tree -> - viewModel.remove(tree.value) - }, + onClick = { openTextEdit(it.value.uuid.toString()) }, + onRename = { tree, newName -> viewModel.rename(tree.value, newName) }, + onRemove = { tree -> viewModel.remove(tree.value) }, onEncrypt = { tree, password, encryptPath -> viewModel.enableEncryption(tree.value, password, encryptPath) }, onOpenEncrypted = { tree, password, remember -> viewModel.openEncryptedStorage(tree.value, password, remember) }, - onCloseEncrypted = { tree -> - viewModel.closeEncryptedStorage(tree.value) - }, - onDisableEncryption = { tree -> - viewModel.disableEncryption(tree.value) - }, - getStatusText = { tree -> - viewModel.getStorageStatus(tree.value) - }, - isEncryptionOpened = { tree -> - viewModel.isEncryptionSessionOpen(tree.value) - }, + onCloseEncrypted = { tree -> viewModel.closeEncryptedStorage(tree.value) }, + onDisableEncryption = { tree -> viewModel.disableEncryption(tree.value) }, + getStatusText = { tree -> viewModel.getStorageStatus(tree.value) }, + isEncryptionOpened = { tree -> viewModel.isEncryptionSessionOpen(tree.value) }, ) } - item { - Spacer(modifier = Modifier.height(8.dp)) - } + item { Spacer(modifier = Modifier.height(8.dp)) } } } - if(uiState.isLoading) { + if (uiState.isLoading) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Box(modifier = Modifier.fillMaxSize().alpha(0.6f).background(Color.Black)) CircularProgressIndicator( @@ -112,4 +99,3 @@ fun LocalVaultScreen( } } } - diff --git a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreenState.kt b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/VaultBrowserScreenState.kt similarity index 53% rename from presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreenState.kt rename to presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/VaultBrowserScreenState.kt index 88e05ca..d4f88ac 100644 --- a/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/local/vault/LocalVaultScreenState.kt +++ b/presentation/src/main/java/com/github/nullptroma/wallenc/presentation/screens/main/screens/vault/VaultBrowserScreenState.kt @@ -1,6 +1,10 @@ -package com.github.nullptroma.wallenc.presentation.screens.main.screens.local.vault +package com.github.nullptroma.wallenc.presentation.screens.main.screens.vault import com.github.nullptroma.wallenc.domain.datatypes.Tree import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo -data class LocalVaultScreenState(val storagesList: List>, val isLoading: Boolean) \ No newline at end of file +data class VaultBrowserScreenState( + val storagesList: List>, + val isLoading: Boolean, + val canAddStorage: Boolean = false, +)