Add warning message to 'mentions and keywords only' notification option (#2028)

* Add warning message to 'mentions and keywords only'

 It should be displayed when it's not supported by the homeserver

* Only display disclaimer in the room notification settings if the room is encrypted

Co-authored-by: Benoit Marty <benoit@matrix.org>

* Fix test and add another one

---------

Co-authored-by: ElementBot <benoitm+elementbot@element.io>
Co-authored-by: Benoit Marty <benoit@matrix.org>
This commit is contained in:
Jorge Martin Espinosa 2023-12-14 17:15:39 +01:00 committed by GitHub
parent e725747ea3
commit d27e9e5265
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 215 additions and 149 deletions

View file

@ -16,67 +16,43 @@
package io.element.android.features.preferences.impl.notifications.edit
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.selection.selectable
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.dp
import io.element.android.features.preferences.impl.R
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.RadioButton
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.theme.components.ListItem
@Composable
fun DefaultNotificationSettingOption(
mode: RoomNotificationMode,
onOptionSelected: (RoomNotificationMode) -> Unit,
displayMentionsOnlyDisclaimer: Boolean,
modifier: Modifier = Modifier,
isSelected: Boolean = false,
) {
val subtitle = when(mode) {
val title = when (mode) {
RoomNotificationMode.ALL_MESSAGES -> stringResource(id = R.string.screen_notification_settings_edit_mode_all_messages)
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> stringResource(id = R.string.screen_notification_settings_edit_mode_mentions_and_keywords)
else -> ""
}
Row(
modifier
.fillMaxWidth()
.selectable(
selected = isSelected,
onClick = { onOptionSelected(mode) },
role = Role.RadioButton,
)
.padding(8.dp),
) {
Column(
Modifier
.weight(1f)
.padding(horizontal = 8.dp)
.align(Alignment.CenterVertically)
) {
Text(
text = subtitle,
style = ElementTheme.typography.fontBodyLgRegular,
)
val subtitle = when {
mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY && displayMentionsOnlyDisclaimer -> {
stringResource(id = R.string.screen_notification_settings_mentions_only_disclaimer)
}
RadioButton(
modifier = Modifier
.align(Alignment.CenterVertically)
.size(48.dp),
selected = isSelected,
onClick = null // null recommended for accessibility with screenreaders
)
else -> null
}
ListItem(
modifier = modifier,
headlineContent = { Text(title) },
supportingContent = subtitle?.let { { Text(it) } },
trailingContent = ListItemContent.RadioButton(selected = isSelected),
onClick = { onOptionSelected(mode) },
)
}
@PreviewsDayNight
@ -86,11 +62,19 @@ internal fun DefaultNotificationSettingOptionPreview() = ElementPreview {
DefaultNotificationSettingOption(
mode = RoomNotificationMode.ALL_MESSAGES,
isSelected = true,
displayMentionsOnlyDisclaimer = false,
onOptionSelected = {},
)
DefaultNotificationSettingOption(
mode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
isSelected = false,
displayMentionsOnlyDisclaimer = false,
onOptionSelected = {},
)
DefaultNotificationSettingOption(
mode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
isSelected = false,
displayMentionsOnlyDisclaimer = true,
onOptionSelected = {},
)
}

View file

@ -19,9 +19,11 @@ package io.element.android.features.preferences.impl.notifications.edit
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@ -55,6 +57,7 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
}
@Composable
override fun present(): EditDefaultNotificationSettingState {
var displayMentionsOnlyDisclaimer by remember { mutableStateOf(false) }
val mode: MutableState<RoomNotificationMode?> = remember {
mutableStateOf(null)
@ -71,6 +74,7 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
fetchSettings(mode)
observeNotificationSettings(mode)
observeRoomSummaries(roomsWithUserDefinedMode)
displayMentionsOnlyDisclaimer = !notificationSettingsService.canHomeServerPushEncryptedEventsToDevice().getOrDefault(true)
}
fun handleEvents(event: EditDefaultNotificationSettingStateEvents) {
@ -87,6 +91,7 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
mode = mode.value,
roomsWithUserDefinedMode = roomsWithUserDefinedMode.value.toImmutableList(),
changeNotificationSettingAction = changeNotificationSettingAction.value,
displayMentionsOnlyDisclaimer = displayMentionsOnlyDisclaimer,
eventSink = ::handleEvents
)
}

View file

@ -26,5 +26,6 @@ data class EditDefaultNotificationSettingState(
val mode: RoomNotificationMode?,
val roomsWithUserDefinedMode: ImmutableList<RoomSummary.Filled>,
val changeNotificationSettingAction: Async<Unit>,
val displayMentionsOnlyDisclaimer: Boolean,
val eventSink: (EditDefaultNotificationSettingStateEvents) -> Unit,
)

View file

@ -31,17 +31,20 @@ open class EditDefaultNotificationSettingStateProvider: PreviewParameterProvider
anEditDefaultNotificationSettingsState(isOneToOne = true),
anEditDefaultNotificationSettingsState(changeNotificationSettingAction = Async.Loading(Unit)),
anEditDefaultNotificationSettingsState(changeNotificationSettingAction = Async.Failure(Throwable("error"))),
anEditDefaultNotificationSettingsState(displayMentionsOnlyDisclaimer = true),
)
}
private fun anEditDefaultNotificationSettingsState(
isOneToOne: Boolean = false,
changeNotificationSettingAction: Async<Unit> = Async.Uninitialized
changeNotificationSettingAction: Async<Unit> = Async.Uninitialized,
displayMentionsOnlyDisclaimer: Boolean = false,
) = EditDefaultNotificationSettingState(
isOneToOne = isOneToOne,
mode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
roomsWithUserDefinedMode = persistentListOf(aRoomSummary()),
changeNotificationSettingAction = changeNotificationSettingAction,
displayMentionsOnlyDisclaimer = displayMentionsOnlyDisclaimer,
eventSink = {}
)

View file

@ -76,6 +76,7 @@ fun EditDefaultNotificationSettingView(
DefaultNotificationSettingOption(
mode = item,
isSelected = state.mode == item,
displayMentionsOnlyDisclaimer = state.displayMentionsOnlyDisclaimer,
onOptionSelected = { state.eventSink(EditDefaultNotificationSettingStateEvents.SetNotificationMode(it)) }
)
}

View file

@ -31,6 +31,7 @@ import io.element.android.libraries.matrix.test.notificationsettings.FakeNotific
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.test.room.aRoomSummaryDetail
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
import io.element.android.tests.testutils.awaitLastSequentialItem
import io.element.android.tests.testutils.consumeItemsUntilPredicate
import kotlinx.coroutines.test.runTest
import org.junit.Test
@ -51,6 +52,8 @@ class EditDefaultNotificationSettingsPresenterTests {
it.mode == RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
}.last()
assertThat(loadedState.mode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
assertThat(loadedState.displayMentionsOnlyDisclaimer).isFalse()
}
}
@ -112,6 +115,19 @@ class EditDefaultNotificationSettingsPresenterTests {
}
}
@Test
fun `present - display mentions only warning if homeserver does not support it`() = runTest {
val notificationSettingsService = FakeNotificationSettingsService().apply {
givenCanHomeServerPushEncryptedEventsToDeviceResult(Result.success(false))
}
val presenter = createEditDefaultNotificationSettingPresenter(notificationSettingsService)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
assertThat(awaitLastSequentialItem().displayMentionsOnlyDisclaimer).isTrue()
}
}
private fun createEditDefaultNotificationSettingPresenter(
notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
roomListService: FakeRoomListService = FakeRoomListService(),