Add room notification settings (#807)
* Add room notification settings --------- Co-authored-by: ElementBot <benoitm+elementbot@element.io> Co-authored-by: Jorge Martín <jorgem@element.io> Co-authored-by: Benoit Marty <benoit@matrix.org> Co-authored-by: David Langley <langley.dave@gmail.com>
This commit is contained in:
parent
a40c9ef002
commit
4a5a01d710
68 changed files with 1381 additions and 70 deletions
|
|
@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.api.createroom.RoomPreset
|
|||
import io.element.android.libraries.matrix.api.createroom.RoomVisibility
|
||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationService
|
||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
import io.element.android.libraries.matrix.api.pusher.PushersService
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
|
|
@ -42,6 +43,7 @@ import io.element.android.libraries.matrix.impl.core.toProgressWatcher
|
|||
import io.element.android.libraries.matrix.impl.mapper.toSessionData
|
||||
import io.element.android.libraries.matrix.impl.media.RustMediaLoader
|
||||
import io.element.android.libraries.matrix.impl.notification.RustNotificationService
|
||||
import io.element.android.libraries.matrix.impl.notificationsettings.RustNotificationSettingsService
|
||||
import io.element.android.libraries.matrix.impl.pushers.RustPushersService
|
||||
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
|
||||
import io.element.android.libraries.matrix.impl.room.RustMatrixRoom
|
||||
|
|
@ -99,6 +101,7 @@ class RustMatrixClient constructor(
|
|||
client = client,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
|
||||
private val notificationProcessSetup = NotificationProcessSetup.SingleProcess(syncService)
|
||||
private val notificationClient = client.notificationClient(notificationProcessSetup)
|
||||
.use { builder ->
|
||||
|
|
@ -106,8 +109,10 @@ 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 isLoggingOut = AtomicBoolean(false)
|
||||
|
||||
|
|
@ -173,6 +178,7 @@ class RustMatrixClient constructor(
|
|||
sessionId = sessionId,
|
||||
roomListItem = roomListItem,
|
||||
innerRoom = fullRoom,
|
||||
roomNotificationSettingsService = notificationSettingsService,
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
coroutineDispatchers = dispatchers,
|
||||
systemClock = clock,
|
||||
|
|
@ -277,9 +283,13 @@ class RustMatrixClient constructor(
|
|||
|
||||
override fun notificationService(): NotificationService = notificationService
|
||||
|
||||
override fun notificationSettingsService(): NotificationSettingsService = notificationSettingsService
|
||||
|
||||
override fun close() {
|
||||
sessionCoroutineScope.cancel()
|
||||
client.setDelegate(null)
|
||||
notificationSettings.setDelegate(null)
|
||||
notificationSettings.destroy()
|
||||
verificationService.destroy()
|
||||
syncService.destroy()
|
||||
innerRoomListService.destroy()
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import dagger.Provides
|
|||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
|
|
@ -34,6 +35,11 @@ object SessionMatrixModule {
|
|||
return matrixClient.sessionVerificationService()
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun providesNotificationSettingsService(matrixClient: MatrixClient): NotificationSettingsService {
|
||||
return matrixClient.notificationSettingsService()
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideRoomMembershipObserver(matrixClient: MatrixClient): RoomMembershipObserver {
|
||||
return matrixClient.roomMembershipObserver()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.notificationsettings
|
||||
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
|
||||
import org.matrix.rustcomponents.sdk.RoomNotificationMode as RustRoomNotificationMode
|
||||
import org.matrix.rustcomponents.sdk.RoomNotificationSettings as RustRoomNotificationSettings
|
||||
|
||||
object RoomNotificationSettingsMapper {
|
||||
fun map(roomNotificationSettings: RustRoomNotificationSettings): RoomNotificationSettings =
|
||||
RoomNotificationSettings(
|
||||
mode = mapMode(roomNotificationSettings.mode),
|
||||
isDefault = roomNotificationSettings.isDefault
|
||||
)
|
||||
|
||||
fun mapMode(mode: RustRoomNotificationMode): RoomNotificationMode =
|
||||
when (mode) {
|
||||
RustRoomNotificationMode.ALL_MESSAGES -> RoomNotificationMode.ALL_MESSAGES
|
||||
RustRoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
|
||||
RustRoomNotificationMode.MUTE -> RoomNotificationMode.MUTE
|
||||
}
|
||||
|
||||
fun mapMode(mode: RoomNotificationMode): RustRoomNotificationMode =
|
||||
when (mode) {
|
||||
RoomNotificationMode.ALL_MESSAGES -> RustRoomNotificationMode.ALL_MESSAGES
|
||||
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> RustRoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY
|
||||
RoomNotificationMode.MUTE -> RustRoomNotificationMode.MUTE
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.notificationsettings
|
||||
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
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.NotificationSettingsDelegate
|
||||
|
||||
class RustNotificationSettingsService(
|
||||
private val notificationSettings: NotificationSettings,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
) : NotificationSettingsService {
|
||||
|
||||
private val _notificationSettingsChangeFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||
override val notificationSettingsChangeFlow: SharedFlow<Unit> = _notificationSettingsChangeFlow.asSharedFlow()
|
||||
|
||||
private var notificationSettingsDelegate = object : NotificationSettingsDelegate {
|
||||
override fun settingsDidChange() {
|
||||
_notificationSettingsChangeFlow.tryEmit(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
notificationSettings.setDelegate(notificationSettingsDelegate)
|
||||
}
|
||||
|
||||
override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, membersCount: Long): Result<RoomNotificationSettings> =
|
||||
runCatching {
|
||||
notificationSettings.getRoomNotificationSettings(roomId.value, isEncrypted, isOneToOne(membersCount)).let(RoomNotificationSettingsMapper::map)
|
||||
}
|
||||
|
||||
override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, membersCount: Long): Result<RoomNotificationMode> =
|
||||
runCatching {
|
||||
notificationSettings.getDefaultRoomNotificationMode(isEncrypted, isOneToOne(membersCount)).let(RoomNotificationSettingsMapper::mapMode)
|
||||
}
|
||||
|
||||
override suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result<Unit> = withContext(dispatchers.io) {
|
||||
runCatching {
|
||||
notificationSettings.setRoomNotificationMode(roomId.value, mode.let(RoomNotificationSettingsMapper::mapMode))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result<Unit> = withContext(dispatchers.io) {
|
||||
runCatching {
|
||||
notificationSettings.restoreDefaultRoomNotificationMode(roomId.value)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun muteRoom(roomId: RoomId): Result<Unit> = setRoomNotificationMode(roomId, RoomNotificationMode.MUTE)
|
||||
|
||||
override suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, membersCount: Long) = withContext(dispatchers.io) {
|
||||
runCatching {
|
||||
notificationSettings.unmuteRoom(roomId.value, isEncrypted, isOneToOne(membersCount))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A one-to-one is a room with exactly 2 members.
|
||||
* See [the Matrix spec](https://spec.matrix.org/latest/client-server-api/#default-underride-rules).
|
||||
* @param membersCount The active members count in a room
|
||||
*/
|
||||
private fun isOneToOne(membersCount: Long) = membersCount == 2L
|
||||
}
|
||||
|
|
@ -33,16 +33,19 @@ import io.element.android.libraries.matrix.api.media.VideoInfo
|
|||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomNotificationSettingsState
|
||||
import io.element.android.libraries.matrix.api.room.MessageEventType
|
||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import io.element.android.libraries.matrix.api.room.roomMembers
|
||||
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventType
|
||||
import io.element.android.libraries.matrix.impl.core.toProgressWatcher
|
||||
import io.element.android.libraries.matrix.impl.media.MediaUploadHandlerImpl
|
||||
import io.element.android.libraries.matrix.impl.media.map
|
||||
import io.element.android.libraries.matrix.impl.poll.toInner
|
||||
import io.element.android.libraries.matrix.impl.notificationsettings.RustNotificationSettingsService
|
||||
import io.element.android.libraries.matrix.impl.room.location.toInner
|
||||
import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline
|
||||
import io.element.android.libraries.matrix.impl.util.destroyAll
|
||||
|
|
@ -72,6 +75,7 @@ class RustMatrixRoom(
|
|||
override val sessionId: SessionId,
|
||||
private val roomListItem: RoomListItem,
|
||||
private val innerRoom: Room,
|
||||
private val roomNotificationSettingsService: RustNotificationSettingsService,
|
||||
sessionCoroutineScope: CoroutineScope,
|
||||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
private val systemClock: SystemClock,
|
||||
|
|
@ -90,6 +94,10 @@ class RustMatrixRoom(
|
|||
private val roomCoroutineScope = sessionCoroutineScope.childScope(coroutineDispatchers.main, "RoomScope-$roomId")
|
||||
private val _membersStateFlow = MutableStateFlow<MatrixRoomMembersState>(MatrixRoomMembersState.Unknown)
|
||||
private val _syncUpdateFlow = MutableStateFlow(0L)
|
||||
|
||||
private val _roomNotificationSettingsStateFlow = MutableStateFlow<MatrixRoomNotificationSettingsState>(MatrixRoomNotificationSettingsState.Unknown)
|
||||
override val roomNotificationSettingsStateFlow: StateFlow<MatrixRoomNotificationSettingsState> = _roomNotificationSettingsStateFlow
|
||||
|
||||
private val _timeline by lazy {
|
||||
RustMatrixTimeline(
|
||||
matrixRoom = this,
|
||||
|
|
@ -197,6 +205,22 @@ class RustMatrixRoom(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun updateRoomNotificationSettings(): Result<Unit> = withContext(coroutineDispatchers.io) {
|
||||
val currentState = _roomNotificationSettingsStateFlow.value
|
||||
val currentRoomNotificationSettings = currentState.roomNotificationSettings()
|
||||
_roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Pending(prevRoomNotificationSettings = currentRoomNotificationSettings)
|
||||
runCatching {
|
||||
roomNotificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, activeMemberCount).getOrThrow()
|
||||
}.map {
|
||||
_roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Ready(it)
|
||||
}.onFailure {
|
||||
_roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Error(
|
||||
prevRoomNotificationSettings = currentRoomNotificationSettings,
|
||||
failure = it
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun userAvatarUrl(userId: UserId): Result<String?> = withContext(roomDispatcher) {
|
||||
runCatching {
|
||||
innerRoom.memberAvatarUrl(userId.value)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue