diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 891301532c..8d2edd0843 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -197,7 +197,9 @@ class LoggedInFlowNode @AssistedInject constructor( ) : NavTarget @Parcelize - data object Settings : NavTarget + data class Settings( + val initialElement: PreferencesEntryPoint.InitialTarget = PreferencesEntryPoint.InitialTarget.Root + ) : NavTarget @Parcelize data object CreateRoom : NavTarget @@ -227,7 +229,7 @@ class LoggedInFlowNode @AssistedInject constructor( } override fun onSettingsClicked() { - backstack.push(NavTarget.Settings) + backstack.push(NavTarget.Settings()) } override fun onCreateRoomClicked() { @@ -260,11 +262,15 @@ class LoggedInFlowNode @AssistedInject constructor( override fun onForwardedToSingleRoom(roomId: RoomId) { coroutineScope.launch { attachRoom(roomId) } } + + override fun onOpenGlobalNotificationSettings() { + backstack.push(NavTarget.Settings(PreferencesEntryPoint.InitialTarget.NotificationSettings)) + } } val inputs = RoomFlowNode.Inputs(roomId = navTarget.roomId, initialElement = navTarget.initialElement) createNode(buildContext, plugins = listOf(inputs, callback)) } - NavTarget.Settings -> { + is NavTarget.Settings -> { val callback = object : PreferencesEntryPoint.Callback { override fun onOpenBugReport() { plugins().forEach { it.onOpenBugReport() } @@ -278,7 +284,9 @@ class LoggedInFlowNode @AssistedInject constructor( backstack.push(NavTarget.Room(roomId, initialElement = RoomLoadedFlowNode.NavTarget.RoomNotificationSettings)) } } - preferencesEntryPoint.nodeBuilder(this, buildContext) + val inputs = PreferencesEntryPoint.Params(navTarget.initialElement) + return preferencesEntryPoint.nodeBuilder(this, buildContext) + .params(inputs) .callback(callback) .build() } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt index fa583645b2..613ed650c8 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt @@ -75,6 +75,7 @@ class RoomLoadedFlowNode @AssistedInject constructor( interface Callback : Plugin { fun onForwardedToSingleRoom(roomId: RoomId) + fun onOpenGlobalNotificationSettings() } data class Inputs( @@ -128,6 +129,18 @@ class RoomLoadedFlowNode @AssistedInject constructor( } } + private fun createRoomDetailsNode(buildContext: BuildContext, initialTarget: RoomDetailsEntryPoint.InitialTarget): Node { + val callback = object : RoomDetailsEntryPoint.Callback { + override fun onOpenGlobalNotificationSettings() { + callbacks.forEach { it.onOpenGlobalNotificationSettings() } + } + } + return roomDetailsEntryPoint.nodeBuilder(this, buildContext) + .params(RoomDetailsEntryPoint.Params(initialTarget)) + .callback(callback) + .build() + } + override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { NavTarget.Messages -> { @@ -147,16 +160,13 @@ class RoomLoadedFlowNode @AssistedInject constructor( messagesEntryPoint.createNode(this, buildContext, callback) } NavTarget.RoomDetails -> { - val inputs = RoomDetailsEntryPoint.Inputs(RoomDetailsEntryPoint.InitialTarget.RoomDetails) - roomDetailsEntryPoint.createNode(this, buildContext, inputs, emptyList()) + createRoomDetailsNode(buildContext, RoomDetailsEntryPoint.InitialTarget.RoomDetails) } is NavTarget.RoomMemberDetails -> { - val inputs = RoomDetailsEntryPoint.Inputs(RoomDetailsEntryPoint.InitialTarget.RoomMemberDetails(navTarget.userId)) - roomDetailsEntryPoint.createNode(this, buildContext, inputs, emptyList()) + createRoomDetailsNode(buildContext, RoomDetailsEntryPoint.InitialTarget.RoomMemberDetails(navTarget.userId)) } NavTarget.RoomNotificationSettings -> { - val inputs = RoomDetailsEntryPoint.Inputs(RoomDetailsEntryPoint.InitialTarget.RoomNotificationSettings) - roomDetailsEntryPoint.createNode(this, buildContext, inputs, emptyList()) + createRoomDetailsNode(buildContext, RoomDetailsEntryPoint.InitialTarget.RoomNotificationSettings) } } } diff --git a/appnav/src/test/kotlin/io/element/android/appnav/RoomFlowNodeTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/RoomFlowNodeTest.kt index a25e4d8134..c05ddb6c73 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/RoomFlowNodeTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/RoomFlowNodeTest.kt @@ -31,6 +31,7 @@ import io.element.android.appnav.room.RoomLoadedFlowNode import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint import io.element.android.libraries.architecture.childNode +import io.element.android.libraries.architecture.createNode import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.test.room.FakeMatrixRoom @@ -71,14 +72,22 @@ class RoomFlowNodeTest { var nodeId: String? = null - override fun createNode( - parentNode: Node, - buildContext: BuildContext, - inputs: RoomDetailsEntryPoint.Inputs, - plugins: List - ): Node { - return node(buildContext) {}.also { - nodeId = it.id + override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): RoomDetailsEntryPoint.NodeBuilder { + return object : RoomDetailsEntryPoint.NodeBuilder { + + override fun params(params: RoomDetailsEntryPoint.Params): RoomDetailsEntryPoint.NodeBuilder { + return this + } + + override fun callback(callback: RoomDetailsEntryPoint.Callback): RoomDetailsEntryPoint.NodeBuilder { + return this + } + + override fun build(): Node { + return node(buildContext) {}.also { + nodeId = it.id + } + } } } } diff --git a/features/preferences/api/build.gradle.kts b/features/preferences/api/build.gradle.kts index c20fe9aabb..0278385ab3 100644 --- a/features/preferences/api/build.gradle.kts +++ b/features/preferences/api/build.gradle.kts @@ -15,6 +15,7 @@ */ plugins { id("io.element.android-library") + id("kotlin-parcelize") } android { diff --git a/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/PreferencesEntryPoint.kt b/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/PreferencesEntryPoint.kt index 50a605efe4..a0d2b8e057 100644 --- a/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/PreferencesEntryPoint.kt +++ b/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/PreferencesEntryPoint.kt @@ -16,17 +16,30 @@ package io.element.android.features.preferences.api +import android.os.Parcelable import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.architecture.FeatureEntryPoint +import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.matrix.api.core.RoomId +import kotlinx.parcelize.Parcelize interface PreferencesEntryPoint : FeatureEntryPoint { + sealed interface InitialTarget : Parcelable { + @Parcelize + data object Root : InitialTarget + @Parcelize + data object NotificationSettings : InitialTarget + } + + data class Params(val initialElement: InitialTarget) : NodeInputs fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder interface NodeBuilder { + + fun params(params: Params): NodeBuilder fun callback(callback: Callback): NodeBuilder fun build(): Node } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/DefaultPreferencesEntryPoint.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/DefaultPreferencesEntryPoint.kt index aa286394dc..e551d9d8dc 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/DefaultPreferencesEntryPoint.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/DefaultPreferencesEntryPoint.kt @@ -31,6 +31,11 @@ class DefaultPreferencesEntryPoint @Inject constructor() : PreferencesEntryPoint return object : PreferencesEntryPoint.NodeBuilder { val plugins = ArrayList() + override fun params(params: PreferencesEntryPoint.Params): PreferencesEntryPoint.NodeBuilder { + plugins += params + return this + } + override fun callback(callback: PreferencesEntryPoint.Callback): PreferencesEntryPoint.NodeBuilder { plugins += callback return this @@ -42,3 +47,8 @@ class DefaultPreferencesEntryPoint @Inject constructor() : PreferencesEntryPoint } } } + +internal fun PreferencesEntryPoint.InitialTarget.toNavTarget() = when (this) { + is PreferencesEntryPoint.InitialTarget.Root -> PreferencesFlowNode.NavTarget.Root + is PreferencesEntryPoint.InitialTarget.NotificationSettings -> PreferencesFlowNode.NavTarget.NotificationSettings +} diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt index 8d77757527..0fcf04d1df 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt @@ -53,7 +53,7 @@ class PreferencesFlowNode @AssistedInject constructor( @Assisted plugins: List, ) : BackstackNode( backstack = BackStack( - initialElement = NavTarget.Root, + initialElement = plugins.filterIsInstance().first().initialElement.toNavTarget(), savedStateMap = buildContext.savedStateMap, ), buildContext = buildContext, diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt index 79201e27d3..5fb34687d8 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingPresenter.kt @@ -135,7 +135,13 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor( suspend { // On modern clients, we don't have different settings for encrypted and non-encrypted rooms (Legacy clients did). notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, mode = mode, isOneToOne = isOneToOne).getOrThrow() - notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = false, mode = mode, isOneToOne = isOneToOne).getOrThrow() + val result = notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = false, mode = mode, isOneToOne = isOneToOne) + + if (result.isFailure) { + result.exceptionOrNull()?.printStackTrace() + } + + result.getOrThrow() }.runCatchingUpdatingState(action) } diff --git a/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt b/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt index ed1e6f5c5a..0aaac324da 100644 --- a/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt +++ b/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt @@ -38,7 +38,17 @@ interface RoomDetailsEntryPoint : FeatureEntryPoint { data object RoomNotificationSettings : InitialTarget } - data class Inputs(val initialElement: InitialTarget) : NodeInputs + data class Params(val initialElement: InitialTarget) : NodeInputs - fun createNode(parentNode: Node, buildContext: BuildContext, inputs: Inputs, plugins: List): Node + interface Callback : Plugin { + fun onOpenGlobalNotificationSettings() + } + + interface NodeBuilder { + fun params(params: Params): NodeBuilder + fun callback(callback: Callback): NodeBuilder + fun build(): Node + } + + fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder } diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts index 3bf38d4fa7..a2fdfa18c1 100644 --- a/features/roomdetails/impl/build.gradle.kts +++ b/features/roomdetails/impl/build.gradle.kts @@ -44,6 +44,7 @@ dependencies { implementation(projects.libraries.mediaupload.api) implementation(projects.libraries.featureflag.api) implementation(projects.libraries.permissions.api) + implementation(projects.libraries.preferences.api) api(projects.features.roomdetails.api) api(projects.libraries.usersearch.api) api(projects.services.apperror.api) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPoint.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPoint.kt index 8cd6cb54d6..108ef088f0 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPoint.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPoint.kt @@ -29,13 +29,25 @@ import javax.inject.Inject @ContributesBinding(AppScope::class) class DefaultRoomDetailsEntryPoint @Inject constructor() : RoomDetailsEntryPoint { - override fun createNode( - parentNode: Node, - buildContext: BuildContext, - inputs: RoomDetailsEntryPoint.Inputs, - plugins: List - ): Node { - return parentNode.createNode(buildContext, plugins + inputs) + + override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): RoomDetailsEntryPoint.NodeBuilder { + return object : RoomDetailsEntryPoint.NodeBuilder { + val plugins = ArrayList() + + override fun params(params: RoomDetailsEntryPoint.Params): RoomDetailsEntryPoint.NodeBuilder { + plugins += params + return this + } + + override fun callback(callback: RoomDetailsEntryPoint.Callback): RoomDetailsEntryPoint.NodeBuilder { + plugins += callback + return this + } + + override fun build(): Node { + return parentNode.createNode(buildContext, plugins) + } + } } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index 6945ebb0ca..dba31ab223 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -23,6 +23,7 @@ import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin +import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.push import dagger.assisted.Assisted @@ -47,7 +48,7 @@ class RoomDetailsFlowNode @AssistedInject constructor( @Assisted plugins: List, ) : BackstackNode( backstack = BackStack( - initialElement = plugins.filterIsInstance().first().initialElement.toNavTarget(), + initialElement = plugins.filterIsInstance().first().initialElement.toNavTarget(), savedStateMap = buildContext.savedStateMap, ), buildContext = buildContext, @@ -125,8 +126,13 @@ class RoomDetailsFlowNode @AssistedInject constructor( } is NavTarget.RoomNotificationSettings -> { - val plugins = listOf(RoomNotificationSettingsNode.RoomNotificationSettingInput(navTarget.showUserDefinedSettingStyle)) - createNode(buildContext, plugins) + val input = RoomNotificationSettingsNode.RoomNotificationSettingInput(navTarget.showUserDefinedSettingStyle) + val callback = object : RoomNotificationSettingsNode.Callback { + override fun openGlobalNotificationSettings() { + plugins().forEach { it.onOpenGlobalNotificationSettings() } + } + } + createNode(buildContext, listOf(input, callback)) } is NavTarget.RoomMemberDetails -> { diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsNode.kt index cb0168a42b..173e3b6af1 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsNode.kt @@ -22,6 +22,7 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin +import com.bumble.appyx.core.plugin.plugins import dagger.assisted.Assisted import dagger.assisted.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen @@ -42,8 +43,15 @@ class RoomNotificationSettingsNode @AssistedInject constructor( data class RoomNotificationSettingInput( val showUserDefinedSettingStyle: Boolean ) : NodeInputs - + interface Callback : Plugin { + fun openGlobalNotificationSettings() + } private val inputs = inputs() + private val callbacks = plugins() + + private fun openGlobalNotificationSettings() { + callbacks.forEach { it.openGlobalNotificationSettings() } + } init { lifecycle.subscribe( @@ -66,6 +74,7 @@ class RoomNotificationSettingsNode @AssistedInject constructor( RoomNotificationSettingsView( state = state, modifier = modifier, + onShowGlobalNotifications = this::openGlobalNotificationSettings, onBackPressed = this::navigateUp, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStateProvider.kt index 961909f933..1a95530d29 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsStateProvider.kt @@ -36,5 +36,17 @@ internal class RoomNotificationSettingsStateProvider : PreviewParameterProvider< restoreDefaultAction = Async.Uninitialized, eventSink = { }, ), + RoomNotificationSettingsState( + roomName = "Room 1", + Async.Success(RoomNotificationSettings( + mode = RoomNotificationMode.MUTE, + isDefault = false)), + pendingRoomNotificationMode = null, + pendingSetDefault = null, + defaultRoomNotificationMode = RoomNotificationMode.ALL_MESSAGES, + setNotificationSettingAction = Async.Uninitialized, + restoreDefaultAction = Async.Uninitialized, + eventSink = { }, + ), ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt index 3d95e4ec20..21bc11781d 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt @@ -21,10 +21,14 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.ClickableText import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.roomdetails.impl.R @@ -34,9 +38,9 @@ import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch -import io.element.android.libraries.designsystem.components.preferences.PreferenceText import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.text.buildAnnotatedStringWithStyledPart import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text @@ -49,6 +53,7 @@ import io.element.android.libraries.ui.strings.CommonStrings fun RoomNotificationSettingsView( state: RoomNotificationSettingsState, modifier: Modifier = Modifier, + onShowGlobalNotifications: () -> Unit = {}, onBackPressed: () -> Unit = {}, ) { Scaffold( @@ -66,40 +71,62 @@ fun RoomNotificationSettingsView( .consumeWindowInsets(padding), verticalArrangement = Arrangement.spacedBy(16.dp), ) { - val subtitle = when (state.defaultRoomNotificationMode) { - RoomNotificationMode.ALL_MESSAGES -> stringResource(id = R.string.screen_room_notification_settings_mode_all_messages) - RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> stringResource(id = R.string.screen_room_notification_settings_mode_mentions_and_keywords) - RoomNotificationMode.MUTE -> stringResource(id = CommonStrings.common_mute) - null -> "" - } - val roomNotificationSettings = state.roomNotificationSettings.dataOrNull() - - PreferenceCategory(title = stringResource(id = R.string.screen_room_notification_settings_custom_settings_title)) { - PreferenceSwitch( - isChecked = state.displayIsDefault.orTrue(), - onCheckedChange = { - state.eventSink(RoomNotificationSettingsEvents.SetNotificationMode(it)) - }, - title = "Match default setting", - subtitle = subtitle, - enabled = roomNotificationSettings != null - ) - - PreferenceText( - title = stringResource(id = R.string.screen_room_notification_settings_allow_custom), - subtitle = stringResource(id = R.string.screen_room_notification_settings_allow_custom_footnote), - enabled = !state.displayIsDefault.orTrue(), - ) - - if (roomNotificationSettings != null && state.displayNotificationMode != null) { + PreferenceSwitch( + isChecked = !state.displayIsDefault.orTrue(), + onCheckedChange = { + state.eventSink(RoomNotificationSettingsEvents.SetNotificationMode(!it)) + }, + title = stringResource(id = R.string.screen_room_notification_settings_allow_custom), + subtitle = stringResource(id = R.string.screen_room_notification_settings_allow_custom_footnote), + enabled = roomNotificationSettings != null + ) + if (state.displayIsDefault.orTrue()) { + PreferenceCategory(title = stringResource(id = R.string.screen_room_notification_settings_default_setting_title)) { + val text = buildAnnotatedStringWithStyledPart( + R.string.screen_room_notification_settings_default_setting_footnote, + R.string.screen_room_notification_settings_default_setting_footnote_content_link, + color = Color.Unspecified, + underline = false, + bold = true, + ) + ClickableText( + text = text, + onClick = { + onShowGlobalNotifications() + }, + modifier = Modifier + .padding(start = 16.dp, bottom = 16.dp, end = 16.dp), + style = ElementTheme.typography.fontBodyMdRegular + .copy( + color = MaterialTheme.colorScheme.secondary, + textAlign = TextAlign.Center, + ) + ) + if(state.defaultRoomNotificationMode != null){ + val defaultModeTitle = when (state.defaultRoomNotificationMode) { + RoomNotificationMode.ALL_MESSAGES -> stringResource(id = R.string.screen_room_notification_settings_mode_all_messages) + RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> { + stringResource(id = R.string.screen_room_notification_settings_mode_mentions_and_keywords) + } + RoomNotificationMode.MUTE -> stringResource(id = CommonStrings.common_mute) + } + RoomNotificationSettingsOption( + roomNotificationSettingsItem = RoomNotificationSettingsItem(state.defaultRoomNotificationMode, defaultModeTitle), + isSelected = true, + onOptionSelected = { }, + enabled = true + ) + } + } + } else { + PreferenceCategory(title = stringResource(id = R.string.screen_room_notification_settings_custom_settings_title)) { RoomNotificationSettingsOptions( selected = state.displayNotificationMode, enabled = !state.displayIsDefault.orTrue(), onOptionSelected = { state.eventSink(RoomNotificationSettingsEvents.RoomNotificationModeChanged(it.mode)) - }, - ) + },) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt index df5c4d577f..42b2662a6d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt @@ -28,6 +28,8 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.NotificationSettings import org.matrix.rustcomponents.sdk.NotificationSettingsDelegate +import org.matrix.rustcomponents.sdk.NotificationSettingsException +import timber.log.Timber class RustNotificationSettingsService( private val notificationSettings: NotificationSettings, @@ -63,7 +65,13 @@ class RustNotificationSettingsService( isOneToOne: Boolean ): Result = withContext(dispatchers.io) { runCatching { - notificationSettings.setDefaultRoomNotificationMode(isEncrypted, isOneToOne, mode.let(RoomNotificationSettingsMapper::mapMode)) + try { + notificationSettings.setDefaultRoomNotificationMode(isEncrypted, isOneToOne, mode.let(RoomNotificationSettingsMapper::mapMode)) + } catch (exception: NotificationSettingsException.RuleNotFound) { + // `setDefaultRoomNotificationMode` updates multiple rules including unstable rules (e.g. the polls push rules defined in the MSC3930) + // since production home servers may not have these rules yet, we drop the RuleNotFound error + Timber.w("Unable to find the rule: ${exception.ruleId}") + } } }