Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add setting to disable notification for invites #1955

Merged
merged 8 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/1944.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add toggle in the notification settings to disable notifications for room invites.
2 changes: 1 addition & 1 deletion docs/_developer_onboarding.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ You can also have access to the aars through the [release](https://github.com/ma

#### Build the SDK locally

Easiest way: run the script [./tools/sdk/build_rust_sdk.sh](./tools/sdk/build_rust_sdk.sh) and just answer the questions.
Easiest way: run the script [../tools/sdk/build_rust_sdk.sh](../tools/sdk/build_rust_sdk.sh) and just answer the questions.

Legacy way:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
package io.element.android.features.preferences.impl.notifications

sealed interface NotificationSettingsEvents {

data object RefreshSystemNotificationsEnabled : NotificationSettingsEvents
data class SetNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents
data class SetAtRoomNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents
data class SetCallNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents
data class SetInviteForMeNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents
data object FixConfigurationMismatch : NotificationSettingsEvents
data object ClearConfigurationMismatchError : NotificationSettingsEvents
data object ClearNotificationChangeError : NotificationSettingsEvents
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ class NotificationSettingsPresenter @Inject constructor(
is NotificationSettingsEvents.SetCallNotificationsEnabled -> {
localCoroutineScope.setCallNotificationsEnabled(event.enabled, changeNotificationSettingAction)
}
is NotificationSettingsEvents.SetInviteForMeNotificationsEnabled -> {
localCoroutineScope.setInviteForMeNotificationsEnabled(event.enabled, changeNotificationSettingAction)
}
is NotificationSettingsEvents.SetNotificationsEnabled -> localCoroutineScope.setNotificationsEnabled(userPushStore, event.enabled)
NotificationSettingsEvents.ClearConfigurationMismatchError -> {
matrixSettings.value = NotificationSettingsState.MatrixSettings.Invalid(fixFailed = false)
Expand Down Expand Up @@ -123,10 +126,12 @@ class NotificationSettingsPresenter @Inject constructor(

val callNotificationsEnabled = notificationSettingsService.isCallEnabled().getOrThrow()
val atRoomNotificationsEnabled = notificationSettingsService.isRoomMentionEnabled().getOrThrow()
val inviteForMeNotificationsEnabled = notificationSettingsService.isInviteForMeEnabled().getOrThrow()

target.value = NotificationSettingsState.MatrixSettings.Valid(
atRoomNotificationsEnabled = atRoomNotificationsEnabled,
callNotificationsEnabled = callNotificationsEnabled,
inviteForMeNotificationsEnabled = inviteForMeNotificationsEnabled,
defaultGroupNotificationMode = encryptedGroupDefaultMode,
defaultOneToOneNotificationMode = encryptedOneToOneDefaultMode,
)
Expand Down Expand Up @@ -175,6 +180,12 @@ class NotificationSettingsPresenter @Inject constructor(
}.runCatchingUpdatingState(action)
}

private fun CoroutineScope.setInviteForMeNotificationsEnabled(enabled: Boolean, action: MutableState<Async<Unit>>) = launch {
suspend {
notificationSettingsService.setInviteForMeEnabled(enabled).getOrThrow()
}.runCatchingUpdatingState(action)
}

private fun CoroutineScope.setNotificationsEnabled(userPushStore: UserPushStore, enabled: Boolean) = launch {
userPushStore.setNotificationEnabledForDevice(enabled)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ data class NotificationSettingsState(
data class Valid(
val atRoomNotificationsEnabled: Boolean,
val callNotificationsEnabled: Boolean,
val inviteForMeNotificationsEnabled: Boolean,
val defaultGroupNotificationMode: RoomNotificationMode?,
val defaultOneToOneNotificationMode: RoomNotificationMode?,
) : MatrixSettings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ fun aNotificationSettingsState(
matrixSettings = NotificationSettingsState.MatrixSettings.Valid(
atRoomNotificationsEnabled = true,
callNotificationsEnabled = true,
inviteForMeNotificationsEnabled = true,
defaultGroupNotificationMode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
defaultOneToOneNotificationMode = RoomNotificationMode.ALL_MESSAGES,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
onMentionNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(it)) },
// TODO We are removing the call notification toggle until support for call notifications has been added
// onCallsNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(it)) },
onInviteForMeNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetInviteForMeNotificationsEnabled(it)) },

Check warning on line 80 in features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt

View check run for this annotation

Codecov / codecov/patch

features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt#L80

Added line #L80 was not covered by tests
)
}
AsyncView(
Expand All @@ -98,6 +99,7 @@
onMentionNotificationsChanged: (Boolean) -> Unit,
// TODO We are removing the call notification toggle until support for call notifications has been added
// onCallsNotificationsChanged: (Boolean) -> Unit,
onInviteForMeNotificationsChanged: (Boolean) -> Unit,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
Expand Down Expand Up @@ -147,16 +149,23 @@
onCheckedChange = onMentionNotificationsChanged
)
}
// TODO We are removing the call notification toggle until support for call notifications has been added
// PreferenceCategory(title = stringResource(id = CommonStrings.screen_notification_settings_additional_settings_section_title)) {
PreferenceCategory(title = stringResource(id = R.string.screen_notification_settings_additional_settings_section_title)) {
// TODO We are removing the call notification toggle until support for call notifications has been added
// PreferenceSwitch(
// modifier = Modifier,
// title = stringResource(id = CommonStrings.screen_notification_settings_calls_label),
// isChecked = matrixSettings.callNotificationsEnabled,
// switchAlignment = Alignment.Top,
// onCheckedChange = onCallsNotificationsChanged
// )
// }
PreferenceSwitch(
modifier = Modifier,
title = stringResource(id = R.string.screen_notification_settings_invite_for_me_label),
isChecked = matrixSettings.inviteForMeNotificationsEnabled,
switchAlignment = Alignment.Top,
onCheckedChange = onInviteForMeNotificationsChanged
)

Check warning on line 167 in features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt

View check run for this annotation

Codecov / codecov/patch

features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt#L167

Added line #L167 was not covered by tests
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ If you proceed, some of your settings may change."</string>
<string name="screen_notification_settings_enable_notifications">"Enable notifications on this device"</string>
<string name="screen_notification_settings_failed_fixing_configuration">"The configuration has not been corrected, please try again."</string>
<string name="screen_notification_settings_group_chats">"Group chats"</string>
<string name="screen_notification_settings_invite_for_me_label">"Invitations"</string>
<string name="screen_notification_settings_mentions_only_disclaimer">"Your homeserver does not support this option in encrypted rooms, you may not get notified in some rooms."</string>
<string name="screen_notification_settings_mentions_section_title">"Mentions"</string>
<string name="screen_notification_settings_mode_all">"All"</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ class NotificationSettingsPresenterTests {
assertThat(initialState.appSettings.appNotificationsEnabled).isFalse()
assertThat(initialState.appSettings.systemNotificationsEnabled).isTrue()
assertThat(initialState.matrixSettings).isEqualTo(NotificationSettingsState.MatrixSettings.Uninitialized)

val loadedState = consumeItemsUntilPredicate {
it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid
}.last()
Expand All @@ -50,6 +49,7 @@ class NotificationSettingsPresenterTests {
val valid = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
assertThat(valid?.atRoomNotificationsEnabled).isFalse()
assertThat(valid?.callNotificationsEnabled).isFalse()
assertThat(valid?.inviteForMeNotificationsEnabled).isFalse()
assertThat(valid?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
assertThat(valid?.defaultOneToOneNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES)
cancelAndIgnoreRemainingEvents()
Expand All @@ -63,7 +63,6 @@ class NotificationSettingsPresenterTests {
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {

notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, isOneToOne = false, mode = RoomNotificationMode.ALL_MESSAGES)
notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = false, isOneToOne = false, mode = RoomNotificationMode.ALL_MESSAGES)
val updatedState = consumeItemsUntilPredicate {
Expand All @@ -82,7 +81,6 @@ class NotificationSettingsPresenterTests {
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {

notificationSettingsService.setDefaultRoomNotificationMode(
isEncrypted = true,
isOneToOne = false,
Expand Down Expand Up @@ -118,7 +116,6 @@ class NotificationSettingsPresenterTests {
val fixedState = consumeItemsUntilPredicate(timeout = 2000.milliseconds) {
it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid
}.last()

val fixedMatrixState = fixedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
assertThat(fixedMatrixState?.defaultGroupNotificationMode).isEqualTo(RoomNotificationMode.ALL_MESSAGES)
}
Expand All @@ -134,7 +131,6 @@ class NotificationSettingsPresenterTests {
it.matrixSettings is NotificationSettingsState.MatrixSettings.Valid
}.last()
assertThat(loadedState.appSettings.appNotificationsEnabled).isTrue()

loadedState.eventSink(NotificationSettingsEvents.SetNotificationsEnabled(false))
val updatedState = consumeItemsUntilPredicate {
!it.appSettings.appNotificationsEnabled
Expand All @@ -155,7 +151,6 @@ class NotificationSettingsPresenterTests {
}.last()
val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
assertThat(validMatrixState?.callNotificationsEnabled).isFalse()

loadedState.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(true))
val updatedState = consumeItemsUntilPredicate {
(it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.callNotificationsEnabled == true
Expand All @@ -166,6 +161,27 @@ class NotificationSettingsPresenterTests {
}
}

@Test
fun `present - set invite for me notifications enabled`() = runTest {
val presenter = createNotificationSettingsPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val loadedState = consumeItemsUntilPredicate {
(it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.inviteForMeNotificationsEnabled == false
}.last()
val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
assertThat(validMatrixState?.inviteForMeNotificationsEnabled).isFalse()
loadedState.eventSink(NotificationSettingsEvents.SetInviteForMeNotificationsEnabled(true))
val updatedState = consumeItemsUntilPredicate {
(it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.inviteForMeNotificationsEnabled == true
}.last()
val updatedMatrixState = updatedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
assertThat(updatedMatrixState?.inviteForMeNotificationsEnabled).isTrue()
cancelAndIgnoreRemainingEvents()
}
}

@Test
fun `present - set atRoom notifications enabled`() = runTest {
val presenter = createNotificationSettingsPresenter()
Expand All @@ -177,7 +193,6 @@ class NotificationSettingsPresenterTests {
}.last()
val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
assertThat(validMatrixState?.atRoomNotificationsEnabled).isFalse()

loadedState.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(true))
val updatedState = consumeItemsUntilPredicate {
(it.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid)?.atRoomNotificationsEnabled == true
Expand All @@ -201,14 +216,12 @@ class NotificationSettingsPresenterTests {
}.last()
val validMatrixState = loadedState.matrixSettings as? NotificationSettingsState.MatrixSettings.Valid
assertThat(validMatrixState?.atRoomNotificationsEnabled).isFalse()

loadedState.eventSink(NotificationSettingsEvents.SetAtRoomNotificationsEnabled(true))
val errorState = consumeItemsUntilPredicate {
it.changeNotificationSettingAction.isFailure()
}.last()
assertThat(errorState.changeNotificationSettingAction.isFailure()).isTrue()
errorState.eventSink(NotificationSettingsEvents.ClearNotificationChangeError)

val clearErrorState = consumeItemsUntilPredicate {
it.changeNotificationSettingAction.isUninitialized()
}.last()
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ jsoup = "org.jsoup:jsoup:1.17.1"
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
molecule-runtime = "app.cash.molecule:molecule-runtime:1.3.1"
timber = "com.jakewharton.timber:timber:5.0.1"
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.73"
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.74"
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,7 @@ interface NotificationSettingsService {
suspend fun setRoomMentionEnabled(enabled: Boolean): Result<Unit>
suspend fun isCallEnabled(): Result<Boolean>
suspend fun setCallEnabled(enabled: Boolean): Result<Unit>
suspend fun isInviteForMeEnabled(): Result<Boolean>
suspend fun setInviteForMeEnabled(enabled: Boolean): Result<Unit>
suspend fun getRoomsWithUserDefinedRules(): Result<List<String>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,9 @@ class RustMatrixClient constructor(
.filterByPushRules()
.finish()
}
private val notificationSettings = client.getNotificationSettings()

private val notificationService = RustNotificationService(sessionId, notificationClient, dispatchers, clock)
private val notificationSettingsService = RustNotificationSettingsService(notificationSettings, dispatchers)
private val notificationSettingsService = RustNotificationSettingsService(client, dispatchers)
.apply { start() }
private val roomSyncSubscriber = RoomSyncSubscriber(innerRoomListService, dispatchers)
private val encryptionService = RustEncryptionService(
client = client,
Expand Down Expand Up @@ -346,8 +345,7 @@ class RustMatrixClient constructor(
override fun close() {
sessionCoroutineScope.cancel()
clientDelegateTaskHandle?.cancelAndDestroy()
notificationSettings.setDelegate(null)
notificationSettings.destroy()
notificationSettingsService.destroy()
verificationService.destroy()
syncService.destroy()
innerRoomListService.destroy()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,16 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.NotificationSettings
import org.matrix.rustcomponents.sdk.Client
import org.matrix.rustcomponents.sdk.NotificationSettingsDelegate
import org.matrix.rustcomponents.sdk.NotificationSettingsException
import timber.log.Timber

class RustNotificationSettingsService(
private val notificationSettings: NotificationSettings,
client: Client,
private val dispatchers: CoroutineDispatchers,
) : NotificationSettingsService {

private val notificationSettings = client.getNotificationSettings()
private val _notificationSettingsChangeFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
override val notificationSettingsChangeFlow: SharedFlow<Unit> = _notificationSettingsChangeFlow.asSharedFlow()

Expand All @@ -45,10 +45,15 @@ class RustNotificationSettingsService(
}
}

init {
fun start() {
notificationSettings.setDelegate(notificationSettingsDelegate)
}

fun destroy() {
notificationSettings.setDelegate(null)
notificationSettings.destroy()
}

override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationSettings> =
runCatching {
notificationSettings.getRoomNotificationSettings(roomId.value, isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::map)
Expand Down Expand Up @@ -119,6 +124,18 @@ class RustNotificationSettingsService(
}
}

override suspend fun isInviteForMeEnabled(): Result<Boolean> = withContext(dispatchers.io) {
runCatching {
notificationSettings.isInviteForMeEnabled()
}
}

override suspend fun setInviteForMeEnabled(enabled: Boolean): Result<Unit> = withContext(dispatchers.io) {
runCatching {
notificationSettings.setInviteForMeEnabled(enabled)
}
}

override suspend fun getRoomsWithUserDefinedRules(): Result<List<String>> =
runCatching {
notificationSettings.getRoomsWithUserDefinedRules(enabled = true)
Expand Down
Loading
Loading