feat: snackbar after sync (synced N / nothing to sync / server unreachable)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-18 21:02:51 -04:00
parent 415742b974
commit a37ef4a794
3 changed files with 21 additions and 3 deletions

View File

@@ -7,8 +7,9 @@ import me.hgsky.synq.data.db.CaptureDao
import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter
suspend fun syncPending(dao: CaptureDao, client: SynqApiClient, settings: SynqSettings) {
suspend fun syncPending(dao: CaptureDao, client: SynqApiClient, settings: SynqSettings): Int {
val pending = dao.getPendingAndFailed()
var synced = 0
for (capture in pending) {
dao.updateStatus(capture.id, "syncing", null)
val result = client.postCapture(capture, settings)
@@ -16,8 +17,10 @@ suspend fun syncPending(dao: CaptureDao, client: SynqApiClient, settings: SynqSe
is PostResult.Accepted, is PostResult.AlreadySeen -> {
val now = OffsetDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
dao.markSynced(capture.id, now)
synced++
}
is PostResult.Failed -> dao.updateStatus(capture.id, "failed", result.error)
}
}
return synced
}

View File

@@ -30,6 +30,8 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
@@ -62,9 +64,13 @@ fun CaptureScreen(
val recentTags by vm.recentTags.collectAsState()
val lastSyncedAt by vm.lastSyncedAt.collectAsState()
val focusRequester = remember { FocusRequester() }
val snackbarState = remember { SnackbarHostState() }
val context = LocalContext.current
LaunchedEffect(Unit) { focusRequester.requestFocus() }
LaunchedEffect(Unit) {
vm.snackbar.collect { msg -> snackbarState.showSnackbar(msg) }
}
LaunchedEffect(prefill) {
if (!prefill.isNullOrEmpty()) vm.setBody(prefill)
}
@@ -83,6 +89,7 @@ fun CaptureScreen(
}
Scaffold(
snackbarHost = { SnackbarHost(snackbarState) },
topBar = {
TopAppBar(
title = { Text("synq") },

View File

@@ -4,9 +4,11 @@ import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
@@ -37,6 +39,9 @@ class CaptureViewModel(app: Application) : AndroidViewModel(app) {
private val _state = MutableStateFlow(CaptureUiState())
val state: StateFlow<CaptureUiState> = _state.asStateFlow()
private val _snackbar = MutableSharedFlow<String>(extraBufferCapacity = 1)
val snackbar = _snackbar.asSharedFlow()
val recentTags: StateFlow<List<String>> = dao.getRecentTagsJson()
.map { jsonList ->
val seen = LinkedHashSet<String>()
@@ -93,9 +98,12 @@ class CaptureViewModel(app: Application) : AndroidViewModel(app) {
try {
val settings = synqApp.settings.settings.first()
val client = SynqApiClient()
if (client.checkHealth(settings.serverUrl)) {
syncPending(dao, client, settings)
if (!client.checkHealth(settings.serverUrl)) {
_snackbar.tryEmit("server unreachable")
return@launch
}
val synced = syncPending(dao, client, settings)
_snackbar.tryEmit(if (synced == 0) "nothing to sync" else "synced $synced")
} finally {
_state.value = _state.value.copy(isSyncing = false)
}