feat: add notification for list content updates and manage preferences in SettingsScreen
This commit is contained in:
+11
@@ -42,6 +42,9 @@ class PreferencesManager(
|
|||||||
private val _notifyListRemoved = MutableStateFlow(isNotifyListRemovedEnabled())
|
private val _notifyListRemoved = MutableStateFlow(isNotifyListRemovedEnabled())
|
||||||
val notifyListRemoved: StateFlow<Boolean> = _notifyListRemoved.asStateFlow()
|
val notifyListRemoved: StateFlow<Boolean> = _notifyListRemoved.asStateFlow()
|
||||||
|
|
||||||
|
private val _notifyListContentUpdated = MutableStateFlow(isNotifyListContentUpdatedEnabled())
|
||||||
|
val notifyListContentUpdated: StateFlow<Boolean> = _notifyListContentUpdated.asStateFlow()
|
||||||
|
|
||||||
fun getServerUrl(): String = prefs.getString(KEY_SERVER_URL, DEFAULT_SERVER_URL) ?: DEFAULT_SERVER_URL
|
fun getServerUrl(): String = prefs.getString(KEY_SERVER_URL, DEFAULT_SERVER_URL) ?: DEFAULT_SERVER_URL
|
||||||
|
|
||||||
fun setServerUrl(url: String) {
|
fun setServerUrl(url: String) {
|
||||||
@@ -169,6 +172,13 @@ class PreferencesManager(
|
|||||||
_notifyListRemoved.value = enabled
|
_notifyListRemoved.value = enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isNotifyListContentUpdatedEnabled(): Boolean = prefs.getBoolean(KEY_NOTIFY_LIST_CONTENT_UPDATED, true)
|
||||||
|
|
||||||
|
fun setNotifyListContentUpdatedEnabled(enabled: Boolean) {
|
||||||
|
prefs.edit().putBoolean(KEY_NOTIFY_LIST_CONTENT_UPDATED, enabled).apply()
|
||||||
|
_notifyListContentUpdated.value = enabled
|
||||||
|
}
|
||||||
|
|
||||||
// Persist per-list column widths (fieldId -> widthDp)
|
// Persist per-list column widths (fieldId -> widthDp)
|
||||||
// Stored as a JSON object string in SharedPreferences under key: COLUMN_WIDTHS_PREFIX + listId
|
// Stored as a JSON object string in SharedPreferences under key: COLUMN_WIDTHS_PREFIX + listId
|
||||||
fun getColumnWidths(listId: String): Map<String, Float> {
|
fun getColumnWidths(listId: String): Map<String, Float> {
|
||||||
@@ -261,6 +271,7 @@ class PreferencesManager(
|
|||||||
private const val KEY_NOTIFY_LIST_ADDED = "notify_list_added"
|
private const val KEY_NOTIFY_LIST_ADDED = "notify_list_added"
|
||||||
private const val KEY_NOTIFY_LIST_EDITED = "notify_list_edited"
|
private const val KEY_NOTIFY_LIST_EDITED = "notify_list_edited"
|
||||||
private const val KEY_NOTIFY_LIST_REMOVED = "notify_list_removed"
|
private const val KEY_NOTIFY_LIST_REMOVED = "notify_list_removed"
|
||||||
|
private const val KEY_NOTIFY_LIST_CONTENT_UPDATED = "notify_list_content_updated"
|
||||||
private const val KEY_LAST_LIST_NOTIFY_CHECK_TS = "last_list_notify_check_ts"
|
private const val KEY_LAST_LIST_NOTIFY_CHECK_TS = "last_list_notify_check_ts"
|
||||||
private const val COLUMN_WIDTHS_PREFIX = "column_widths_" // + listId
|
private const val COLUMN_WIDTHS_PREFIX = "column_widths_" // + listId
|
||||||
private const val COLUMN_ALIGN_PREFIX = "column_align_" // + listId
|
private const val COLUMN_ALIGN_PREFIX = "column_align_" // + listId
|
||||||
|
|||||||
+7
@@ -57,6 +57,12 @@ object NotificationHelper {
|
|||||||
name: String,
|
name: String,
|
||||||
) = showEvent(context, listId, "remove", "List removed", "\"$name\" was deleted")
|
) = showEvent(context, listId, "remove", "List removed", "\"$name\" was deleted")
|
||||||
|
|
||||||
|
fun showListContentUpdated(
|
||||||
|
context: Context,
|
||||||
|
listId: String,
|
||||||
|
name: String,
|
||||||
|
) = showEvent(context, listId, "content", "List updated", "\"$name\" content changed")
|
||||||
|
|
||||||
private fun showEvent(
|
private fun showEvent(
|
||||||
context: Context,
|
context: Context,
|
||||||
listId: String,
|
listId: String,
|
||||||
@@ -102,6 +108,7 @@ object NotificationHelper {
|
|||||||
"add" -> "Added • $text"
|
"add" -> "Added • $text"
|
||||||
"edit" -> "Edited • $text"
|
"edit" -> "Edited • $text"
|
||||||
"remove" -> "Removed • $text"
|
"remove" -> "Removed • $text"
|
||||||
|
"content" -> "Updated • $text"
|
||||||
else -> text
|
else -> text
|
||||||
}
|
}
|
||||||
synchronized(recentEventLines) {
|
synchronized(recentEventLines) {
|
||||||
|
|||||||
+2
-2
@@ -563,8 +563,8 @@ fun ListDetailScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loading overlay during initial content load
|
// Loading overlay only while nothing is available to render yet
|
||||||
if (isLoading) {
|
if (isLoading && stableFields.isEmpty() && stableItems.isEmpty()) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
|
|||||||
+21
@@ -3,6 +3,8 @@ package com.collabtable.app.ui.screens
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.ProcessLifecycleOwner
|
||||||
import androidx.room.withTransaction
|
import androidx.room.withTransaction
|
||||||
import com.collabtable.app.data.database.CollabTableDatabase
|
import com.collabtable.app.data.database.CollabTableDatabase
|
||||||
import com.collabtable.app.data.model.CollabList
|
import com.collabtable.app.data.model.CollabList
|
||||||
@@ -120,6 +122,19 @@ class ListDetailViewModel(
|
|||||||
syncRepository.performSync()
|
syncRepository.performSync()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isInForeground(): Boolean {
|
||||||
|
val state = ProcessLifecycleOwner.get().lifecycle.currentState
|
||||||
|
return state.isAtLeast(Lifecycle.State.STARTED)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun maybeNotifyListContentUpdated() {
|
||||||
|
val prefs = com.collabtable.app.data.preferences.PreferencesManager.getInstance(context)
|
||||||
|
if (prefs.notifyListContentUpdated.value && !isInForeground()) {
|
||||||
|
val name = _list.value?.name ?: "Table"
|
||||||
|
com.collabtable.app.notifications.NotificationHelper.showListContentUpdated(context, listId, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun renameList(newName: String) {
|
fun renameList(newName: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val currentList = _list.value
|
val currentList = _list.value
|
||||||
@@ -184,6 +199,7 @@ class ListDetailViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
performSync()
|
performSync()
|
||||||
|
maybeNotifyListContentUpdated()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +213,7 @@ class ListDetailViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
performSync()
|
performSync()
|
||||||
|
maybeNotifyListContentUpdated()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,6 +241,7 @@ class ListDetailViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
performSync()
|
performSync()
|
||||||
|
maybeNotifyListContentUpdated()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,6 +278,7 @@ class ListDetailViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
performSync()
|
performSync()
|
||||||
|
maybeNotifyListContentUpdated()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,6 +314,7 @@ class ListDetailViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
performSync()
|
performSync()
|
||||||
|
maybeNotifyListContentUpdated()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,6 +352,7 @@ class ListDetailViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
performSync()
|
performSync()
|
||||||
|
maybeNotifyListContentUpdated()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ fun SettingsScreen(
|
|||||||
val notifyListAdded by preferencesManager.notifyListAdded.collectAsState()
|
val notifyListAdded by preferencesManager.notifyListAdded.collectAsState()
|
||||||
val notifyListEdited by preferencesManager.notifyListEdited.collectAsState()
|
val notifyListEdited by preferencesManager.notifyListEdited.collectAsState()
|
||||||
val notifyListRemoved by preferencesManager.notifyListRemoved.collectAsState()
|
val notifyListRemoved by preferencesManager.notifyListRemoved.collectAsState()
|
||||||
|
val notifyListContentUpdated by preferencesManager.notifyListContentUpdated.collectAsState()
|
||||||
val displayUrl = remember(serverUrl) { formatServerUrlForDisplay(serverUrl) }
|
val displayUrl = remember(serverUrl) { formatServerUrlForDisplay(serverUrl) }
|
||||||
var showLeaveDialog by remember { mutableStateOf(false) }
|
var showLeaveDialog by remember { mutableStateOf(false) }
|
||||||
var isLeaving by remember { mutableStateOf(false) }
|
var isLeaving by remember { mutableStateOf(false) }
|
||||||
@@ -379,6 +380,32 @@ fun SettingsScreen(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
|
Text("List content updated")
|
||||||
|
Text(
|
||||||
|
text = "Notify when fields or items change (add/edit/delete)",
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Switch(
|
||||||
|
checked = notifyListContentUpdated,
|
||||||
|
onCheckedChange = { checked ->
|
||||||
|
if (checked) {
|
||||||
|
ensureNotificationPermission {
|
||||||
|
preferencesManager.setNotifyListContentUpdatedEnabled(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
preferencesManager.setNotifyListContentUpdatedEnabled(false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
|||||||
+3
-2
@@ -50,8 +50,9 @@ class ListChangeNotificationWorker(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if (prefs.notifyListEdited.value) {
|
// Background diff cannot distinguish rename vs content. Treat as content update here.
|
||||||
NotificationHelper.showListEdited(applicationContext, list.id, list.name)
|
if (prefs.notifyListContentUpdated.value) {
|
||||||
|
NotificationHelper.showListContentUpdated(applicationContext, list.id, list.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user