feat(sync): добавлен механизм снятия блокировки синхронизации для хранилищ

This commit is contained in:
2026-05-13 14:43:27 +03:00
parent f38b3dfbb4
commit 6c18a1d741
9 changed files with 122 additions and 2 deletions

View File

@@ -29,6 +29,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -63,6 +64,8 @@ fun StorageTree(
onDisableEncryption: (Tree<IStorageInfo>) -> Unit,
getStatusText: (Tree<IStorageInfo>) -> String,
isEncryptionOpened: (Tree<IStorageInfo>) -> Boolean,
isStorageSyncLockHeld: suspend (IStorageInfo) -> Boolean,
onClearStorageSyncLock: (IStorageInfo) -> Unit,
) {
val cur = tree.value
val available by cur.isAvailable.collectAsStateWithLifecycle()
@@ -125,6 +128,15 @@ fun StorageTree(
horizontalAlignment = Alignment.End
) {
var expanded by remember { mutableStateOf(false) }
var syncLockHeld by remember { mutableStateOf<Boolean?>(null) }
LaunchedEffect(expanded, cur.uuid) {
if (expanded) {
syncLockHeld = null
syncLockHeld = isStorageSyncLockHeld(cur)
} else {
syncLockHeld = null
}
}
var showRenameDialog by remember { mutableStateOf(false) }
var showRemoveConfirmDialog by remember { mutableStateOf(false) }
var showLockDialog by remember { mutableStateOf(false) }
@@ -166,6 +178,25 @@ fun StorageTree(
text = { Text(stringResource(R.string.encrypt)) }
)
}
HorizontalDivider()
DropdownMenuItem(
enabled = syncLockHeld == true,
onClick = {
expanded = false
if (syncLockHeld == true) {
onClearStorageSyncLock(cur)
}
},
text = {
Text(
when (syncLockHeld) {
null -> stringResource(R.string.storage_sync_lock_checking)
true -> stringResource(R.string.storage_sync_unlock_action)
false -> stringResource(R.string.storage_sync_not_locked)
},
)
},
)
}
if (showRenameDialog) {
@@ -295,7 +326,9 @@ fun StorageTree(
onCloseEncrypted,
onDisableEncryption,
getStatusText,
isEncryptionOpened
isEncryptionOpened,
isStorageSyncLockHeld,
onClearStorageSyncLock,
)
}
}

View File

@@ -328,4 +328,37 @@ abstract class AbstractVaultBrowserViewModel(
val openedMap = getOpenedStoragesUseCase.openedStorages.value
return openedMap.containsKey(storage.uuid)
}
suspend fun isStorageSyncLockHeld(storage: IStorageInfo): Boolean {
val s = storage as? IStorage ?: return false
return try {
s.accessor.readSyncLock() != null
} catch (_: Exception) {
false
}
}
fun clearStorageSyncLock(storage: IStorageInfo) {
taskOrchestrator.enqueue(
title = "Снятие блокировки синхронизации",
dispatcher = Dispatchers.IO,
work = { ctx ->
try {
val s = storage as? IStorage
if (s == null) {
ctx.log(TaskLogLevel.Error, "Некорректное хранилище")
_messages.emit("Некорректное хранилище")
return@enqueue
}
ctx.log(TaskLogLevel.Info, "Снимаю блокировку синхронизации…")
s.accessor.forceClearSyncLock()
ctx.log(TaskLogLevel.Info, "Блокировка синхронизации снята")
_messages.emit("Блокировка синхронизации снята")
} catch (e: Exception) {
ctx.log(TaskLogLevel.Error, e.message ?: "Не удалось снять блокировку")
_messages.emit(e.message ?: "Не удалось снять блокировку синхронизации")
}
},
)
}
}

View File

@@ -90,6 +90,8 @@ fun VaultBrowserScreen(
onDisableEncryption = { tree -> viewModel.disableEncryption(tree.value) },
getStatusText = { tree -> viewModel.getStorageStatus(tree.value) },
isEncryptionOpened = { tree -> viewModel.isEncryptionSessionOpen(tree.value) },
isStorageSyncLockHeld = { info -> viewModel.isStorageSyncLockHeld(info) },
onClearStorageSyncLock = { info -> viewModel.clearStorageSyncLock(info) },
)
}
item { Spacer(modifier = Modifier.height(8.dp)) }

View File

@@ -34,6 +34,9 @@
<string name="new_name_title">New name</string>
<string name="remove_confirmation_dialog">Delete storage "%1$s"?</string>
<string name="storage_lock_actions">Storage encryption actions</string>
<string name="storage_sync_lock_checking">Проверка блокировки…</string>
<string name="storage_sync_unlock_action">Снять блокировку синхронизации</string>
<string name="storage_sync_not_locked">Синхронизация не заблокирована</string>
<string name="task_pipeline_title">Task pipeline</string>
<string name="task_pipeline_jobs">Jobs</string>