Большой рефакторинг

Из domain выкинуты типы vault, теперь он ничего не знает о Yandex. Объявления провайдеров вынесены в vault-api, а реализации в data
This commit is contained in:
2026-04-27 02:47:02 +03:00
parent 2b1be58a8e
commit 1034e134c2
37 changed files with 527 additions and 219 deletions

View File

@@ -1,17 +0,0 @@
package com.github.nullptroma.wallenc.domain.auth
/**
* Результат входа через Яндекс (без зависимости от Yandex SDK).
*/
sealed interface RemoteYandexAuthResult {
data class Success(val accessToken: String) : RemoteYandexAuthResult
data class Failure(val message: String) : RemoteYandexAuthResult
data object Cancelled : RemoteYandexAuthResult
}
/**
* Запуск OAuth Яндекса. Реализация только в модуле app (Yandex Auth SDK).
*/
fun interface RemoteYandexSignInLauncher {
fun launch(onResult: (RemoteYandexAuthResult) -> Unit)
}

View File

@@ -1,7 +0,0 @@
package com.github.nullptroma.wallenc.domain.enums
enum class VaultType {
LOCAL,
DECRYPTED,
YANDEX
}

View File

@@ -3,10 +3,18 @@ package com.github.nullptroma.wallenc.domain.interfaces
import com.github.nullptroma.wallenc.domain.datatypes.StorageEncryptionInfo
import kotlinx.coroutines.flow.StateFlow
/**
* Контракт vault'а: коллекция [IStorage] с реактивным состоянием.
*
* domain не различает локальные/удалённые/Yandex/etc. — это общий порт.
*/
interface IVault : IVaultInfo {
override val storages: StateFlow<List<IStorage>?>
val storages: StateFlow<List<IStorage>>
val isAvailable: StateFlow<Boolean>
val totalSpace: StateFlow<Int?>
val availableSpace: StateFlow<Int?>
suspend fun createStorage(): IStorage
suspend fun createStorage(enc: StorageEncryptionInfo): IStorage
suspend fun remove(storage: IStorage)
}
}

View File

@@ -1,14 +1,13 @@
package com.github.nullptroma.wallenc.domain.interfaces
import com.github.nullptroma.wallenc.domain.enums.VaultType
import kotlinx.coroutines.flow.StateFlow
import java.util.UUID
sealed interface IVaultInfo {
val type: VaultType
/**
* Минимальная идентификация vault'а.
*
* Намеренно «голая»: domain ничего не знает о брендах, локальности или статусе —
* вся категоризация лежит во внешнем кольце (`:vault-api: VaultDescriptor`).
*/
interface IVaultInfo {
val uuid: UUID
val storages: StateFlow<List<IStorageInfo>?>
val isAvailable: StateFlow<Boolean>
val totalSpace: StateFlow<Int?>
val availableSpace: StateFlow<Int?>
}
}

View File

@@ -1,15 +1,15 @@
package com.github.nullptroma.wallenc.domain.interfaces
import kotlinx.coroutines.flow.StateFlow
import java.util.UUID
/**
* Единая точка доступа ко всем vault'ам приложения.
*
* domain не различает категории vault'ов — потребители (presentation) фильтруют
* [vaults] через `:vault-api` (`VaultDescriptor`/`DescribedVault`).
*/
interface IVaultsManager {
val localVault: IVault
val unlockManager: IUnlockManager
val remoteVaults: StateFlow<List<IVault>>
val vaults: StateFlow<List<IVault>>
val allStorages: StateFlow<List<IStorage>>
val allVaults: StateFlow<List<IVault>>
suspend fun addYandexVault(accessToken: String)
suspend fun removeRemoteVault(vaultUuid: UUID)
}
val unlockManager: IUnlockManager
}

View File

@@ -1,8 +0,0 @@
package com.github.nullptroma.wallenc.domain.interfaces
/**
* Удалённый vault Яндекс с привязанным аккаунтом (почта для UI).
*/
interface IYandexVault : IVault {
val accountEmail: String
}

View File

@@ -1,14 +0,0 @@
package com.github.nullptroma.wallenc.domain.usecases
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
import kotlinx.coroutines.flow.StateFlow
class ManageLocalVaultUseCase(private val manager: IVaultsManager) {
val localStorages: StateFlow<List<IStorageInfo>?>
get() = manager.localVault.storages
suspend fun createStorage() {
manager.localVault.createStorage()
}
}

View File

@@ -0,0 +1,34 @@
package com.github.nullptroma.wallenc.domain.usecases
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
import com.github.nullptroma.wallenc.domain.interfaces.IVault
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import java.util.UUID
@OptIn(ExperimentalCoroutinesApi::class)
class ManageVaultUseCase(private val manager: IVaultsManager) {
/** Найти vault по идентификатору в текущем состоянии. */
fun find(vaultUuid: UUID): IVault? =
manager.vaults.value.firstOrNull { it.uuid == vaultUuid }
/** Реактивно отдаёт сам vault, либо null если он отсутствует. */
fun observe(vaultUuid: UUID): Flow<IVault?> =
manager.vaults.map { list -> list.firstOrNull { it.uuid == vaultUuid } }
/** Реактивно отдаёт storages указанного vault'а; пустой список, если vault не найден. */
fun storagesOf(vaultUuid: UUID): Flow<List<IStorage>> =
observe(vaultUuid).flatMapLatest { vault -> vault?.storages ?: flowOf(emptyList()) }
/** Создать новое хранилище в указанном vault'е. */
suspend fun createStorage(vaultUuid: UUID): IStorage {
val vault = find(vaultUuid)
?: throw IllegalStateException("Vault $vaultUuid is not registered")
return vault.createStorage()
}
}

View File

@@ -3,7 +3,9 @@ package com.github.nullptroma.wallenc.domain.usecases
import com.github.nullptroma.wallenc.domain.interfaces.IStorage
import com.github.nullptroma.wallenc.domain.interfaces.IStorageInfo
import com.github.nullptroma.wallenc.domain.interfaces.IUnlockManager
import com.github.nullptroma.wallenc.domain.interfaces.IVault
import com.github.nullptroma.wallenc.domain.interfaces.IVaultsManager
import java.util.UUID
class RemoveStorageUseCase(
private val vaultsManager: IVaultsManager,
@@ -12,12 +14,11 @@ class RemoveStorageUseCase(
) {
suspend fun remove(storage: IStorageInfo) {
if (storage !is IStorage) return
if (!storage.isVirtualStorage) {
unlockManager.close(storage)
vaultsManager.localVault.remove(storage)
findOwningVault(storage.uuid)?.remove(storage)
return
}
@@ -25,13 +26,18 @@ class RemoveStorageUseCase(
manageStoragesEncryptionUseCase.clearAndDisableEncryption(parent)
}
private fun findOwningVault(storageUuid: UUID): IVault? =
vaultsManager.vaults.value.firstOrNull { v ->
v.storages.value.any { it.uuid == storageUuid }
}
private fun findParentStorage(storage: IStorage): IStorage? {
val opened = unlockManager.openedStorages.value
val parentUuid = opened.entries.firstOrNull { it.value.uuid == storage.uuid }?.key ?: return null
val locals = vaultsManager.localVault.storages.value.orEmpty()
return locals.firstOrNull { it.uuid == parentUuid }
?: opened[parentUuid]
val parentUuid = opened.entries.firstOrNull { it.value.uuid == storage.uuid }?.key
?: return null
val realParent = vaultsManager.vaults.value
.flatMap { it.storages.value }
.firstOrNull { it.uuid == parentUuid }
return realParent ?: opened[parentUuid]
}
}