diff --git a/CollabTableAndroid/app/src/main/java/com/collabtable/app/data/preferences/PreferencesManager.kt b/CollabTableAndroid/app/src/main/java/com/collabtable/app/data/preferences/PreferencesManager.kt index 709b7f2..0ff5254 100644 --- a/CollabTableAndroid/app/src/main/java/com/collabtable/app/data/preferences/PreferencesManager.kt +++ b/CollabTableAndroid/app/src/main/java/com/collabtable/app/data/preferences/PreferencesManager.kt @@ -42,6 +42,9 @@ class PreferencesManager( private val _notifyListRemoved = MutableStateFlow(isNotifyListRemovedEnabled()) val notifyListRemoved: StateFlow = _notifyListRemoved.asStateFlow() + private val _notifyListContentUpdated = MutableStateFlow(isNotifyListContentUpdatedEnabled()) + val notifyListContentUpdated: StateFlow = _notifyListContentUpdated.asStateFlow() + fun getServerUrl(): String = prefs.getString(KEY_SERVER_URL, DEFAULT_SERVER_URL) ?: DEFAULT_SERVER_URL fun setServerUrl(url: String) { @@ -169,6 +172,13 @@ class PreferencesManager( _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) // Stored as a JSON object string in SharedPreferences under key: COLUMN_WIDTHS_PREFIX + listId fun getColumnWidths(listId: String): Map { @@ -261,6 +271,7 @@ class PreferencesManager( 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_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 COLUMN_WIDTHS_PREFIX = "column_widths_" // + listId private const val COLUMN_ALIGN_PREFIX = "column_align_" // + listId diff --git a/CollabTableAndroid/app/src/main/java/com/collabtable/app/notifications/NotificationHelper.kt b/CollabTableAndroid/app/src/main/java/com/collabtable/app/notifications/NotificationHelper.kt index 91c4088..c8070aa 100644 --- a/CollabTableAndroid/app/src/main/java/com/collabtable/app/notifications/NotificationHelper.kt +++ b/CollabTableAndroid/app/src/main/java/com/collabtable/app/notifications/NotificationHelper.kt @@ -57,6 +57,12 @@ object NotificationHelper { name: String, ) = 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( context: Context, listId: String, @@ -102,6 +108,7 @@ object NotificationHelper { "add" -> "Added • $text" "edit" -> "Edited • $text" "remove" -> "Removed • $text" + "content" -> "Updated • $text" else -> text } synchronized(recentEventLines) { diff --git a/CollabTableAndroid/app/src/main/java/com/collabtable/app/ui/screens/ListDetailScreen.kt b/CollabTableAndroid/app/src/main/java/com/collabtable/app/ui/screens/ListDetailScreen.kt index af8b2f1..e943633 100644 --- a/CollabTableAndroid/app/src/main/java/com/collabtable/app/ui/screens/ListDetailScreen.kt +++ b/CollabTableAndroid/app/src/main/java/com/collabtable/app/ui/screens/ListDetailScreen.kt @@ -563,8 +563,8 @@ fun ListDetailScreen( } } - // Loading overlay during initial content load - if (isLoading) { + // Loading overlay only while nothing is available to render yet + if (isLoading && stableFields.isEmpty() && stableItems.isEmpty()) { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center, diff --git a/CollabTableAndroid/app/src/main/java/com/collabtable/app/ui/screens/ListDetailViewModel.kt b/CollabTableAndroid/app/src/main/java/com/collabtable/app/ui/screens/ListDetailViewModel.kt index d760719..a85acd2 100644 --- a/CollabTableAndroid/app/src/main/java/com/collabtable/app/ui/screens/ListDetailViewModel.kt +++ b/CollabTableAndroid/app/src/main/java/com/collabtable/app/ui/screens/ListDetailViewModel.kt @@ -3,6 +3,8 @@ package com.collabtable.app.ui.screens import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.ProcessLifecycleOwner import androidx.room.withTransaction import com.collabtable.app.data.database.CollabTableDatabase import com.collabtable.app.data.model.CollabList @@ -120,6 +122,19 @@ class ListDetailViewModel( 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) { viewModelScope.launch { val currentList = _list.value @@ -184,6 +199,7 @@ class ListDetailViewModel( } performSync() + maybeNotifyListContentUpdated() } } @@ -197,6 +213,7 @@ class ListDetailViewModel( } } performSync() + maybeNotifyListContentUpdated() } } @@ -224,6 +241,7 @@ class ListDetailViewModel( } } performSync() + maybeNotifyListContentUpdated() } } } @@ -260,6 +278,7 @@ class ListDetailViewModel( } } performSync() + maybeNotifyListContentUpdated() } } @@ -295,6 +314,7 @@ class ListDetailViewModel( } } performSync() + maybeNotifyListContentUpdated() } } @@ -332,6 +352,7 @@ class ListDetailViewModel( } } performSync() + maybeNotifyListContentUpdated() } } diff --git a/CollabTableAndroid/app/src/main/java/com/collabtable/app/ui/screens/SettingsScreen.kt b/CollabTableAndroid/app/src/main/java/com/collabtable/app/ui/screens/SettingsScreen.kt index 93e0e32..8c82f5d 100644 --- a/CollabTableAndroid/app/src/main/java/com/collabtable/app/ui/screens/SettingsScreen.kt +++ b/CollabTableAndroid/app/src/main/java/com/collabtable/app/ui/screens/SettingsScreen.kt @@ -84,6 +84,7 @@ fun SettingsScreen( val notifyListAdded by preferencesManager.notifyListAdded.collectAsState() val notifyListEdited by preferencesManager.notifyListEdited.collectAsState() val notifyListRemoved by preferencesManager.notifyListRemoved.collectAsState() + val notifyListContentUpdated by preferencesManager.notifyListContentUpdated.collectAsState() val displayUrl = remember(serverUrl) { formatServerUrlForDisplay(serverUrl) } var showLeaveDialog 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( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, diff --git a/CollabTableAndroid/app/src/main/java/com/collabtable/app/work/ListChangeNotificationWorker.kt b/CollabTableAndroid/app/src/main/java/com/collabtable/app/work/ListChangeNotificationWorker.kt index 28df5b4..8202aff 100644 --- a/CollabTableAndroid/app/src/main/java/com/collabtable/app/work/ListChangeNotificationWorker.kt +++ b/CollabTableAndroid/app/src/main/java/com/collabtable/app/work/ListChangeNotificationWorker.kt @@ -50,8 +50,9 @@ class ListChangeNotificationWorker( } } else -> { - if (prefs.notifyListEdited.value) { - NotificationHelper.showListEdited(applicationContext, list.id, list.name) + // Background diff cannot distinguish rename vs content. Treat as content update here. + if (prefs.notifyListContentUpdated.value) { + NotificationHelper.showListContentUpdated(applicationContext, list.id, list.name) } } }