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:
@@ -7,8 +7,9 @@ import me.hgsky.synq.data.db.CaptureDao
|
|||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
import java.time.format.DateTimeFormatter
|
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()
|
val pending = dao.getPendingAndFailed()
|
||||||
|
var synced = 0
|
||||||
for (capture in pending) {
|
for (capture in pending) {
|
||||||
dao.updateStatus(capture.id, "syncing", null)
|
dao.updateStatus(capture.id, "syncing", null)
|
||||||
val result = client.postCapture(capture, settings)
|
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 -> {
|
is PostResult.Accepted, is PostResult.AlreadySeen -> {
|
||||||
val now = OffsetDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
val now = OffsetDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
||||||
dao.markSynced(capture.id, now)
|
dao.markSynced(capture.id, now)
|
||||||
|
synced++
|
||||||
}
|
}
|
||||||
is PostResult.Failed -> dao.updateStatus(capture.id, "failed", result.error)
|
is PostResult.Failed -> dao.updateStatus(capture.id, "failed", result.error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return synced
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SnackbarHost
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
@@ -62,9 +64,13 @@ fun CaptureScreen(
|
|||||||
val recentTags by vm.recentTags.collectAsState()
|
val recentTags by vm.recentTags.collectAsState()
|
||||||
val lastSyncedAt by vm.lastSyncedAt.collectAsState()
|
val lastSyncedAt by vm.lastSyncedAt.collectAsState()
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
val snackbarState = remember { SnackbarHostState() }
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
LaunchedEffect(Unit) { focusRequester.requestFocus() }
|
LaunchedEffect(Unit) { focusRequester.requestFocus() }
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
vm.snackbar.collect { msg -> snackbarState.showSnackbar(msg) }
|
||||||
|
}
|
||||||
LaunchedEffect(prefill) {
|
LaunchedEffect(prefill) {
|
||||||
if (!prefill.isNullOrEmpty()) vm.setBody(prefill)
|
if (!prefill.isNullOrEmpty()) vm.setBody(prefill)
|
||||||
}
|
}
|
||||||
@@ -83,6 +89,7 @@ fun CaptureScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
|
snackbarHost = { SnackbarHost(snackbarState) },
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text("synq") },
|
title = { Text("synq") },
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import android.app.Application
|
|||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
@@ -37,6 +39,9 @@ class CaptureViewModel(app: Application) : AndroidViewModel(app) {
|
|||||||
private val _state = MutableStateFlow(CaptureUiState())
|
private val _state = MutableStateFlow(CaptureUiState())
|
||||||
val state: StateFlow<CaptureUiState> = _state.asStateFlow()
|
val state: StateFlow<CaptureUiState> = _state.asStateFlow()
|
||||||
|
|
||||||
|
private val _snackbar = MutableSharedFlow<String>(extraBufferCapacity = 1)
|
||||||
|
val snackbar = _snackbar.asSharedFlow()
|
||||||
|
|
||||||
val recentTags: StateFlow<List<String>> = dao.getRecentTagsJson()
|
val recentTags: StateFlow<List<String>> = dao.getRecentTagsJson()
|
||||||
.map { jsonList ->
|
.map { jsonList ->
|
||||||
val seen = LinkedHashSet<String>()
|
val seen = LinkedHashSet<String>()
|
||||||
@@ -93,9 +98,12 @@ class CaptureViewModel(app: Application) : AndroidViewModel(app) {
|
|||||||
try {
|
try {
|
||||||
val settings = synqApp.settings.settings.first()
|
val settings = synqApp.settings.settings.first()
|
||||||
val client = SynqApiClient()
|
val client = SynqApiClient()
|
||||||
if (client.checkHealth(settings.serverUrl)) {
|
if (!client.checkHealth(settings.serverUrl)) {
|
||||||
syncPending(dao, client, settings)
|
_snackbar.tryEmit("server unreachable")
|
||||||
|
return@launch
|
||||||
}
|
}
|
||||||
|
val synced = syncPending(dao, client, settings)
|
||||||
|
_snackbar.tryEmit(if (synced == 0) "nothing to sync" else "synced $synced")
|
||||||
} finally {
|
} finally {
|
||||||
_state.value = _state.value.copy(isSyncing = false)
|
_state.value = _state.value.copy(isSyncing = false)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user