ux: pull-to-refresh на всех списках (Этап 2 п.12)
Раньше свайп-обновление было только в Файлах. Добавлено (Material3 PullToRefreshBox): Почта (входящие/исходящие, force-обновление), Конференции (список бесед — заменяет убранную кнопку ↻), Контакты (force + CTag — дёшево), Задачи (список), Карточки (доски и открытая доска). Контакты: refresh() получил параметр force (pull-to-refresh форсит sync мимо TTL; с CTag-проверкой это 1 лёгкий PROPFIND).
This commit is contained in:
+18
-15
@@ -21,6 +21,8 @@ import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -51,6 +53,7 @@ import ru.forbion.f7cloud.core.designsystem.F7Colors
|
||||
import ru.forbion.f7cloud.core.designsystem.F7CreateButton
|
||||
import ru.forbion.f7cloud.core.designsystem.F7ModuleScreen
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ContactsScreen(
|
||||
session: AuthSession,
|
||||
@@ -94,21 +97,21 @@ fun ContactsScreen(
|
||||
)
|
||||
F7CreateButton(onClick = vm::openAddSheet, size = 40.dp)
|
||||
}
|
||||
if (state.syncing && state.contacts.isNotEmpty()) {
|
||||
Text(
|
||||
"Обновление…",
|
||||
modifier = Modifier.padding(horizontal = 4.dp, vertical = 2.dp),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = F7Colors.TextSecondary,
|
||||
)
|
||||
}
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
items(state.filteredContacts, key = { "${it.uid}|${it.email}" }) { contact ->
|
||||
ContactListRow(
|
||||
contact = contact,
|
||||
onClick = { vm.openContact(contact) },
|
||||
)
|
||||
HorizontalDivider(color = F7Colors.Border.copy(alpha = 0.6f))
|
||||
PullToRefreshBox(
|
||||
isRefreshing = state.syncing && state.contacts.isNotEmpty(),
|
||||
onRefresh = { vm.refresh(session, force = true) },
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
items(state.filteredContacts, key = { "${it.uid}|${it.email}" }) { contact ->
|
||||
ContactListRow(
|
||||
contact = contact,
|
||||
onClick = { vm.openContact(contact) },
|
||||
)
|
||||
HorizontalDivider(color = F7Colors.Border.copy(alpha = 0.6f))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -99,14 +99,16 @@ class ContactsViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun refresh(session: AuthSession, showLoading: Boolean = false) {
|
||||
fun refresh(session: AuthSession, showLoading: Boolean = false, force: Boolean = false) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
if (showLoading && _state.value.contacts.isEmpty()) {
|
||||
_state.update { it.copy(loading = true, error = null) }
|
||||
} else {
|
||||
_state.update { it.copy(syncing = true, error = null) }
|
||||
}
|
||||
runCatching { repository.syncContacts(session) }
|
||||
// force (pull-to-refresh) с CTag-проверкой дешёвый: 1 лёгкий PROPFIND,
|
||||
// полная закачка vCard — только если адресные книги реально менялись
|
||||
runCatching { repository.syncContacts(session, force = force) }
|
||||
.onFailure { t ->
|
||||
_state.update {
|
||||
it.copy(
|
||||
|
||||
Reference in New Issue
Block a user