Общий VaultScreen
This commit is contained in:
@@ -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.NavBarItemData
|
||||||
import com.github.nullptroma.wallenc.presentation.navigation.NavigationState
|
import com.github.nullptroma.wallenc.presentation.navigation.NavigationState
|
||||||
import com.github.nullptroma.wallenc.presentation.navigation.rememberNavigationState
|
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.RemoteVaultsRoute
|
||||||
import com.github.nullptroma.wallenc.presentation.screens.main.screens.remotes.RemoteVaultsScreen
|
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.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.TextEditRoute
|
||||||
import com.github.nullptroma.wallenc.presentation.screens.shared.TextEditScreen
|
import com.github.nullptroma.wallenc.presentation.screens.shared.TextEditScreen
|
||||||
|
|
||||||
@@ -110,7 +113,24 @@ fun MainScreen(
|
|||||||
}) {
|
}) {
|
||||||
RemoteVaultsScreen(
|
RemoteVaultsScreen(
|
||||||
modifier = Modifier.padding(innerPaddings),
|
modifier = Modifier.padding(innerPaddings),
|
||||||
viewModel = remoteVaultsViewModel
|
viewModel = remoteVaultsViewModel,
|
||||||
|
onOpenVault = { item ->
|
||||||
|
navState.push(VaultBrowserRoute(item.uuid.toString()))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable<VaultBrowserRoute>(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<TextEditRoute> {
|
composable<TextEditRoute> {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import androidx.lifecycle.SavedStateHandle
|
|||||||
import androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi
|
import androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi
|
||||||
import androidx.lifecycle.viewmodel.compose.saveable
|
import androidx.lifecycle.viewmodel.compose.saveable
|
||||||
import com.github.nullptroma.wallenc.presentation.screens.ScreenRoute
|
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.remotes.RemoteVaultsRoute
|
||||||
|
import com.github.nullptroma.wallenc.presentation.screens.main.screens.vault.LocalVaultRoute
|
||||||
import com.github.nullptroma.wallenc.presentation.ViewModelBase
|
import com.github.nullptroma.wallenc.presentation.ViewModelBase
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ import kotlinx.serialization.Serializable
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@Parcelize
|
@Parcelize
|
||||||
class RemoteVaultsRoute: MainRoute()
|
class RemoteVaultsRoute : MainRoute()
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import androidx.compose.material3.Surface
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
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.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.github.nullptroma.wallenc.presentation.R
|
import com.github.nullptroma.wallenc.presentation.R
|
||||||
@@ -52,6 +52,7 @@ import com.github.nullptroma.wallenc.vaultapi.VaultLinkOutcome
|
|||||||
fun RemoteVaultsScreen(
|
fun RemoteVaultsScreen(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: RemoteVaultsViewModel = hiltViewModel(),
|
viewModel: RemoteVaultsViewModel = hiltViewModel(),
|
||||||
|
onOpenVault: (RemoteVaultListItem) -> Unit,
|
||||||
) {
|
) {
|
||||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -92,7 +93,13 @@ fun RemoteVaultsScreen(
|
|||||||
) {
|
) {
|
||||||
items(uiState.vaults, key = { it.uuid }) { item ->
|
items(uiState.vaults, key = { it.uuid }) { item ->
|
||||||
ElevatedCard(
|
ElevatedCard(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable(
|
||||||
|
enabled = !uiState.isBusy,
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = null,
|
||||||
|
) { onOpenVault(item) },
|
||||||
shape = RoundedCornerShape(16.dp),
|
shape = RoundedCornerShape(16.dp),
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 2.dp),
|
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 2.dp),
|
||||||
colors = CardDefaults.elevatedCardColors(
|
colors = CardDefaults.elevatedCardColors(
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import androidx.compose.foundation.clickable
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.github.nullptroma.wallenc.presentation.screens.main.screens.tasks
|
|||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.github.nullptroma.wallenc.domain.tasks.ITaskOrchestrator
|
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 com.github.nullptroma.wallenc.domain.tasks.TaskLogLevel
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|||||||
@@ -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 androidx.lifecycle.viewModelScope
|
||||||
import com.github.nullptroma.wallenc.domain.datatypes.EncryptKey
|
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.IDirectory
|
||||||
import com.github.nullptroma.wallenc.domain.interfaces.IFile
|
import com.github.nullptroma.wallenc.domain.interfaces.IFile
|
||||||
import com.github.nullptroma.wallenc.domain.interfaces.ILogger
|
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.IStorageInfo
|
||||||
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
|
|
||||||
import com.github.nullptroma.wallenc.domain.tasks.ITaskOrchestrator
|
import com.github.nullptroma.wallenc.domain.tasks.ITaskOrchestrator
|
||||||
import com.github.nullptroma.wallenc.domain.tasks.TaskLogLevel
|
import com.github.nullptroma.wallenc.domain.tasks.TaskLogLevel
|
||||||
import com.github.nullptroma.wallenc.domain.usecases.GetOpenedStoragesUseCase
|
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.domain.usecases.StorageFileManagementUseCase
|
||||||
import com.github.nullptroma.wallenc.presentation.ViewModelBase
|
import com.github.nullptroma.wallenc.presentation.ViewModelBase
|
||||||
import com.github.nullptroma.wallenc.presentation.extensions.toPrintable
|
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.Dispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
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 kotlinx.coroutines.launch
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
/**
|
||||||
@HiltViewModel
|
* Общая логика дерева storages для локального и удалённого vault (presentation).
|
||||||
class LocalVaultViewModel @Inject constructor(
|
*/
|
||||||
private val vaultsManager: IVaultsManager,
|
abstract class AbstractVaultBrowserViewModel(
|
||||||
private val manageVaultUseCase: ManageVaultUseCase,
|
storagesFlow: Flow<List<IStorage>>,
|
||||||
|
private val canAddStorage: Boolean,
|
||||||
|
private val resolveCreateVaultUuid: () -> UUID?,
|
||||||
private val removeStorageUseCase: RemoveStorageUseCase,
|
private val removeStorageUseCase: RemoveStorageUseCase,
|
||||||
private val getOpenedStoragesUseCase: GetOpenedStoragesUseCase,
|
private val getOpenedStoragesUseCase: GetOpenedStoragesUseCase,
|
||||||
private val storageFileManagementUseCase: StorageFileManagementUseCase,
|
private val storageFileManagementUseCase: StorageFileManagementUseCase,
|
||||||
private val manageStoragesEncryptionUseCase: ManageStoragesEncryptionUseCase,
|
private val manageStoragesEncryptionUseCase: ManageStoragesEncryptionUseCase,
|
||||||
private val renameStorageUseCase: RenameStorageUseCase,
|
private val renameStorageUseCase: RenameStorageUseCase,
|
||||||
|
private val manageVaultUseCase: ManageVaultUseCase,
|
||||||
private val taskOrchestrator: ITaskOrchestrator,
|
private val taskOrchestrator: ITaskOrchestrator,
|
||||||
private val logger: ILogger
|
private val logger: ILogger,
|
||||||
) : ViewModelBase<LocalVaultScreenState>(LocalVaultScreenState(listOf(), true)) {
|
) : ViewModelBase<VaultBrowserScreenState>(
|
||||||
|
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<String>()
|
private val _messages = MutableSharedFlow<String>()
|
||||||
val messages: SharedFlow<String> = _messages
|
val messages: SharedFlow<String> = _messages
|
||||||
|
|
||||||
private var _taskCount: Int = 0
|
private var taskCount: Int = 0
|
||||||
private var tasksCount
|
|
||||||
get() = _taskCount
|
|
||||||
set(value) {
|
set(value) {
|
||||||
_taskCount = value
|
field = value
|
||||||
updateStateLoading()
|
updateStateLoading()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var _isLoading: Boolean = false
|
private var storagesLoading: Boolean = false
|
||||||
private var isLoading
|
|
||||||
get() = _isLoading
|
|
||||||
set(value) {
|
set(value) {
|
||||||
_isLoading = value
|
field = value
|
||||||
updateStateLoading()
|
updateStateLoading()
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
collectFlows()
|
collectFlows(storagesFlow)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateStateLoading() {
|
private fun updateStateLoading() {
|
||||||
updateState(state.value.copy(
|
updateState(
|
||||||
isLoading = this.isLoading || this.tasksCount > 0
|
state.value.copy(
|
||||||
))
|
isLoading = storagesLoading || taskCount > 0,
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectFlows() {
|
private fun collectFlows(storagesFlow: Flow<List<IStorage>>) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
localStoragesFlow.combine(getOpenedStoragesUseCase.openedStorages) { local, opened ->
|
storagesFlow.combine(getOpenedStoragesUseCase.openedStorages) { storages, opened ->
|
||||||
val list = mutableListOf<Tree<IStorageInfo>>()
|
val list = mutableListOf<Tree<IStorageInfo>>()
|
||||||
for (storage in local) {
|
for (storage in storages) {
|
||||||
var tree = Tree<IStorageInfo>(storage)
|
var tree = Tree<IStorageInfo>(storage)
|
||||||
list.add(tree)
|
list.add(tree)
|
||||||
while (opened.containsKey(tree.value.uuid)) {
|
while (opened.containsKey(tree.value.uuid)) {
|
||||||
@@ -99,7 +89,7 @@ class LocalVaultViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
list
|
list
|
||||||
}.collect { trees ->
|
}.collect { trees ->
|
||||||
isLoading = false
|
storagesLoading = false
|
||||||
updateState(state.value.copy(storagesList = trees))
|
updateState(state.value.copy(storagesList = trees))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,13 +125,14 @@ class LocalVaultViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun createStorage() {
|
fun createStorage() {
|
||||||
|
if (!state.value.canAddStorage) return
|
||||||
taskOrchestrator.enqueue(
|
taskOrchestrator.enqueue(
|
||||||
title = "Create storage",
|
title = "Create storage",
|
||||||
dispatcher = Dispatchers.IO,
|
dispatcher = Dispatchers.IO,
|
||||||
work = { ctx ->
|
work = { ctx ->
|
||||||
ctx.log(TaskLogLevel.Info, "Creating storage…")
|
ctx.log(TaskLogLevel.Info, "Creating storage…")
|
||||||
val uuid = localVaultUuid
|
val uuid = resolveCreateVaultUuid()
|
||||||
?: throw IllegalStateException("Local vault is not registered")
|
?: throw IllegalStateException("Vault is not available")
|
||||||
manageVaultUseCase.createStorage(uuid)
|
manageVaultUseCase.createStorage(uuid)
|
||||||
ctx.log(TaskLogLevel.Info, "Storage created")
|
ctx.log(TaskLogLevel.Info, "Storage created")
|
||||||
},
|
},
|
||||||
@@ -156,7 +147,7 @@ class LocalVaultViewModel @Inject constructor(
|
|||||||
synchronized(storageOpMutex) {
|
synchronized(storageOpMutex) {
|
||||||
if (runningStorages.contains(id)) return
|
if (runningStorages.contains(id)) return
|
||||||
runningStorages.add(id)
|
runningStorages.add(id)
|
||||||
tasksCount++
|
taskCount++
|
||||||
}
|
}
|
||||||
val key = EncryptKey(password)
|
val key = EncryptKey(password)
|
||||||
taskOrchestrator.enqueue(
|
taskOrchestrator.enqueue(
|
||||||
@@ -196,7 +187,7 @@ class LocalVaultViewModel @Inject constructor(
|
|||||||
} finally {
|
} finally {
|
||||||
synchronized(storageOpMutex) {
|
synchronized(storageOpMutex) {
|
||||||
runningStorages.remove(id)
|
runningStorages.remove(id)
|
||||||
tasksCount--
|
taskCount--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -208,7 +199,7 @@ class LocalVaultViewModel @Inject constructor(
|
|||||||
synchronized(storageOpMutex) {
|
synchronized(storageOpMutex) {
|
||||||
if (runningStorages.contains(id)) return
|
if (runningStorages.contains(id)) return
|
||||||
runningStorages.add(id)
|
runningStorages.add(id)
|
||||||
tasksCount++
|
taskCount++
|
||||||
}
|
}
|
||||||
val key = EncryptKey(password)
|
val key = EncryptKey(password)
|
||||||
taskOrchestrator.enqueue(
|
taskOrchestrator.enqueue(
|
||||||
@@ -225,7 +216,7 @@ class LocalVaultViewModel @Inject constructor(
|
|||||||
} finally {
|
} finally {
|
||||||
synchronized(storageOpMutex) {
|
synchronized(storageOpMutex) {
|
||||||
runningStorages.remove(id)
|
runningStorages.remove(id)
|
||||||
tasksCount--
|
taskCount--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -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 com.github.nullptroma.wallenc.presentation.screens.main.MainRoute
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
@@ -6,4 +6,4 @@ import kotlinx.serialization.Serializable
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@Parcelize
|
@Parcelize
|
||||||
class LocalVaultRoute: MainRoute()
|
class LocalVaultRoute : MainRoute()
|
||||||
@@ -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,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
)
|
||||||
@@ -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<String>("vaultUuid") ?: error("Missing vault UUID in navigation arguments")
|
||||||
|
return UUID.fromString(raw)
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
@@ -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 android.widget.Toast
|
||||||
import androidx.compose.foundation.background
|
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.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.github.nullptroma.wallenc.presentation.elements.StorageTree
|
import com.github.nullptroma.wallenc.presentation.elements.StorageTree
|
||||||
import com.github.nullptroma.wallenc.presentation.extensions.gesturesDisabled
|
import com.github.nullptroma.wallenc.presentation.extensions.gesturesDisabled
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LocalVaultScreen(
|
fun VaultBrowserScreen(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: LocalVaultViewModel = hiltViewModel(),
|
viewModel: AbstractVaultBrowserViewModel,
|
||||||
openTextEdit: (String) -> Unit,
|
openTextEdit: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val uiState by viewModel.state.collectAsStateWithLifecycle()
|
val uiState by viewModel.state.collectAsStateWithLifecycle()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
@@ -52,55 +50,44 @@ fun LocalVaultScreen(
|
|||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
contentWindowInsets = WindowInsets(0.dp),
|
contentWindowInsets = WindowInsets(0.dp),
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
|
if (uiState.canAddStorage) {
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
onClick = {
|
onClick = { viewModel.createStorage() },
|
||||||
viewModel.createStorage()
|
|
||||||
},
|
|
||||||
) {
|
) {
|
||||||
Icon(Icons.Filled.Add, "Floating action button.")
|
Icon(Icons.Filled.Add, contentDescription = null)
|
||||||
}
|
}
|
||||||
}) { innerPadding ->
|
}
|
||||||
LazyColumn(modifier = Modifier.padding(innerPadding).gesturesDisabled(uiState.isLoading)) {
|
},
|
||||||
|
) { innerPadding ->
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(innerPadding)
|
||||||
|
.gesturesDisabled(uiState.isLoading),
|
||||||
|
) {
|
||||||
items(uiState.storagesList) { listItem ->
|
items(uiState.storagesList) { listItem ->
|
||||||
StorageTree(
|
StorageTree(
|
||||||
modifier = Modifier.padding(8.dp, 8.dp, 8.dp, 0.dp),
|
modifier = Modifier.padding(8.dp, 8.dp, 8.dp, 0.dp),
|
||||||
tree = listItem,
|
tree = listItem,
|
||||||
onClick = {
|
onClick = { openTextEdit(it.value.uuid.toString()) },
|
||||||
openTextEdit(it.value.uuid.toString())
|
onRename = { tree, newName -> viewModel.rename(tree.value, newName) },
|
||||||
},
|
onRemove = { tree -> viewModel.remove(tree.value) },
|
||||||
onRename = { tree, newName ->
|
|
||||||
viewModel.rename(tree.value, newName)
|
|
||||||
},
|
|
||||||
onRemove = { tree ->
|
|
||||||
viewModel.remove(tree.value)
|
|
||||||
},
|
|
||||||
onEncrypt = { tree, password, encryptPath ->
|
onEncrypt = { tree, password, encryptPath ->
|
||||||
viewModel.enableEncryption(tree.value, password, encryptPath)
|
viewModel.enableEncryption(tree.value, password, encryptPath)
|
||||||
},
|
},
|
||||||
onOpenEncrypted = { tree, password, remember ->
|
onOpenEncrypted = { tree, password, remember ->
|
||||||
viewModel.openEncryptedStorage(tree.value, password, remember)
|
viewModel.openEncryptedStorage(tree.value, password, remember)
|
||||||
},
|
},
|
||||||
onCloseEncrypted = { tree ->
|
onCloseEncrypted = { tree -> viewModel.closeEncryptedStorage(tree.value) },
|
||||||
viewModel.closeEncryptedStorage(tree.value)
|
onDisableEncryption = { tree -> viewModel.disableEncryption(tree.value) },
|
||||||
},
|
getStatusText = { tree -> viewModel.getStorageStatus(tree.value) },
|
||||||
onDisableEncryption = { tree ->
|
isEncryptionOpened = { tree -> viewModel.isEncryptionSessionOpen(tree.value) },
|
||||||
viewModel.disableEncryption(tree.value)
|
|
||||||
},
|
|
||||||
getStatusText = { tree ->
|
|
||||||
viewModel.getStorageStatus(tree.value)
|
|
||||||
},
|
|
||||||
isEncryptionOpened = { tree ->
|
|
||||||
viewModel.isEncryptionSessionOpen(tree.value)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item {
|
item { Spacer(modifier = Modifier.height(8.dp)) }
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(uiState.isLoading) {
|
if (uiState.isLoading) {
|
||||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
Box(modifier = Modifier.fillMaxSize().alpha(0.6f).background(Color.Black))
|
Box(modifier = Modifier.fillMaxSize().alpha(0.6f).background(Color.Black))
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
@@ -112,4 +99,3 @@ fun LocalVaultScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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.datatypes.Tree
|
||||||
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
|
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
|
||||||
|
|
||||||
data class LocalVaultScreenState(val storagesList: List<Tree<IStorageInfo>>, val isLoading: Boolean)
|
data class VaultBrowserScreenState(
|
||||||
|
val storagesList: List<Tree<IStorageInfo>>,
|
||||||
|
val isLoading: Boolean,
|
||||||
|
val canAddStorage: Boolean = false,
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user