Add room notification settings
This commit is contained in:
parent
dd192cb647
commit
d930f4130e
31 changed files with 1037 additions and 7 deletions
2
changelog.d/506.feature
Normal file
2
changelog.d/506.feature
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
Add a "Mute" shortcut icon and a "Notifications" section in the room details screen
|
||||||
|
|
||||||
|
|
@ -18,4 +18,7 @@ package io.element.android.features.roomdetails.impl
|
||||||
|
|
||||||
sealed interface RoomDetailsEvent {
|
sealed interface RoomDetailsEvent {
|
||||||
object LeaveRoom : RoomDetailsEvent
|
object LeaveRoom : RoomDetailsEvent
|
||||||
|
object MuteNotification : RoomDetailsEvent
|
||||||
|
|
||||||
|
object UnmuteNotification : RoomDetailsEvent
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditNode
|
||||||
import io.element.android.features.roomdetails.impl.invite.RoomInviteMembersNode
|
import io.element.android.features.roomdetails.impl.invite.RoomInviteMembersNode
|
||||||
import io.element.android.features.roomdetails.impl.members.RoomMemberListNode
|
import io.element.android.features.roomdetails.impl.members.RoomMemberListNode
|
||||||
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsNode
|
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsNode
|
||||||
|
import io.element.android.features.roomdetails.impl.notificationsettings.RoomNotificationSettingsNode
|
||||||
import io.element.android.libraries.architecture.BackstackNode
|
import io.element.android.libraries.architecture.BackstackNode
|
||||||
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
|
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
|
||||||
import io.element.android.libraries.architecture.createNode
|
import io.element.android.libraries.architecture.createNode
|
||||||
|
|
@ -66,6 +67,9 @@ class RoomDetailsFlowNode @AssistedInject constructor(
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object InviteMembers : NavTarget
|
object InviteMembers : NavTarget
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
object RoomNotificationSettings : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class RoomMemberDetails(val roomMemberId: UserId) : NavTarget
|
data class RoomMemberDetails(val roomMemberId: UserId) : NavTarget
|
||||||
}
|
}
|
||||||
|
|
@ -85,6 +89,10 @@ class RoomDetailsFlowNode @AssistedInject constructor(
|
||||||
override fun openInviteMembers() {
|
override fun openInviteMembers() {
|
||||||
backstack.push(NavTarget.InviteMembers)
|
backstack.push(NavTarget.InviteMembers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun openRoomNotificationSettings() {
|
||||||
|
backstack.push(NavTarget.RoomNotificationSettings)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
createNode<RoomDetailsNode>(buildContext, listOf(roomDetailsCallback))
|
createNode<RoomDetailsNode>(buildContext, listOf(roomDetailsCallback))
|
||||||
}
|
}
|
||||||
|
|
@ -110,6 +118,10 @@ class RoomDetailsFlowNode @AssistedInject constructor(
|
||||||
createNode<RoomInviteMembersNode>(buildContext)
|
createNode<RoomInviteMembersNode>(buildContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NavTarget.RoomNotificationSettings -> {
|
||||||
|
createNode<RoomNotificationSettingsNode>(buildContext)
|
||||||
|
}
|
||||||
|
|
||||||
is NavTarget.RoomMemberDetails -> {
|
is NavTarget.RoomMemberDetails -> {
|
||||||
val plugins = listOf(RoomMemberDetailsNode.RoomMemberDetailsInput(navTarget.roomMemberId))
|
val plugins = listOf(RoomMemberDetailsNode.RoomMemberDetailsInput(navTarget.roomMemberId))
|
||||||
createNode<RoomMemberDetailsNode>(buildContext, plugins)
|
createNode<RoomMemberDetailsNode>(buildContext, plugins)
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ class RoomDetailsNode @AssistedInject constructor(
|
||||||
fun openRoomMemberList()
|
fun openRoomMemberList()
|
||||||
fun openInviteMembers()
|
fun openInviteMembers()
|
||||||
fun editRoomDetails()
|
fun editRoomDetails()
|
||||||
|
fun openRoomNotificationSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val callbacks = plugins<Callback>()
|
private val callbacks = plugins<Callback>()
|
||||||
|
|
@ -67,6 +68,10 @@ class RoomDetailsNode @AssistedInject constructor(
|
||||||
callbacks.forEach { it.openRoomMemberList() }
|
callbacks.forEach { it.openRoomMemberList() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun openRoomNotificationSettings() {
|
||||||
|
callbacks.forEach { it.openRoomNotificationSettings() }
|
||||||
|
}
|
||||||
|
|
||||||
private fun invitePeople() {
|
private fun invitePeople() {
|
||||||
callbacks.forEach { it.openInviteMembers() }
|
callbacks.forEach { it.openInviteMembers() }
|
||||||
}
|
}
|
||||||
|
|
@ -133,6 +138,7 @@ class RoomDetailsNode @AssistedInject constructor(
|
||||||
onShareRoom = ::onShareRoom,
|
onShareRoom = ::onShareRoom,
|
||||||
onShareMember = ::onShareMember,
|
onShareMember = ::onShareMember,
|
||||||
openRoomMemberList = ::openRoomMemberList,
|
openRoomMemberList = ::openRoomMemberList,
|
||||||
|
openRoomNotificationSettings = ::openRoomNotificationSettings,
|
||||||
invitePeople = ::invitePeople,
|
invitePeople = ::invitePeople,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,30 +24,45 @@ import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.produceState
|
import androidx.compose.runtime.produceState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import io.element.android.features.leaveroom.api.LeaveRoomEvent
|
import io.element.android.features.leaveroom.api.LeaveRoomEvent
|
||||||
import io.element.android.features.leaveroom.api.LeaveRoomPresenter
|
import io.element.android.features.leaveroom.api.LeaveRoomPresenter
|
||||||
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter
|
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
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.MatrixRoomMembersState
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||||
import io.element.android.libraries.matrix.api.room.powerlevels.canInvite
|
import io.element.android.libraries.matrix.api.room.powerlevels.canInvite
|
||||||
import io.element.android.libraries.matrix.api.room.powerlevels.canSendState
|
import io.element.android.libraries.matrix.api.room.powerlevels.canSendState
|
||||||
|
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
|
||||||
import io.element.android.libraries.matrix.ui.room.getDirectRoomMember
|
import io.element.android.libraries.matrix.ui.room.getDirectRoomMember
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class RoomDetailsPresenter @Inject constructor(
|
class RoomDetailsPresenter @Inject constructor(
|
||||||
|
private val client: MatrixClient,
|
||||||
private val room: MatrixRoom,
|
private val room: MatrixRoom,
|
||||||
|
private val notificationSettingsService: NotificationSettingsService,
|
||||||
private val roomMembersDetailsPresenterFactory: RoomMemberDetailsPresenter.Factory,
|
private val roomMembersDetailsPresenterFactory: RoomMemberDetailsPresenter.Factory,
|
||||||
private val leaveRoomPresenter: LeaveRoomPresenter,
|
private val leaveRoomPresenter: LeaveRoomPresenter,
|
||||||
|
private val dispatchers: CoroutineDispatchers,
|
||||||
) : Presenter<RoomDetailsState> {
|
) : Presenter<RoomDetailsState> {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): RoomDetailsState {
|
override fun present(): RoomDetailsState {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
val leaveRoomState = leaveRoomPresenter.present()
|
val leaveRoomState = leaveRoomPresenter.present()
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
room.updateMembers()
|
room.updateMembers()
|
||||||
|
room.updateRoomNotificationSettings()
|
||||||
|
observeNotificationSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
val membersState by room.membersStateFlow.collectAsState()
|
val membersState by room.membersStateFlow.collectAsState()
|
||||||
|
|
@ -69,10 +84,22 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val roomNotificationSettingsState by room.roomNotificationSettingsStateFlow.collectAsState()
|
||||||
|
|
||||||
fun handleEvents(event: RoomDetailsEvent) {
|
fun handleEvents(event: RoomDetailsEvent) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is RoomDetailsEvent.LeaveRoom ->
|
RoomDetailsEvent.LeaveRoom ->
|
||||||
leaveRoomState.eventSink(LeaveRoomEvent.ShowConfirmation(room.roomId))
|
leaveRoomState.eventSink(LeaveRoomEvent.ShowConfirmation(room.roomId))
|
||||||
|
RoomDetailsEvent.MuteNotification -> {
|
||||||
|
scope.launch(dispatchers.io) {
|
||||||
|
client.notificationSettingsService().muteRoom(room.roomId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RoomDetailsEvent.UnmuteNotification -> {
|
||||||
|
scope.launch(dispatchers.io) {
|
||||||
|
client.notificationSettingsService().unmuteRoom(room.roomId, room.isEncrypted, room.joinedMemberCount.toULong())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,6 +118,7 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
roomType = roomType,
|
roomType = roomType,
|
||||||
roomMemberDetailsState = roomMemberDetailsState,
|
roomMemberDetailsState = roomMemberDetailsState,
|
||||||
leaveRoomState = leaveRoomState,
|
leaveRoomState = leaveRoomState,
|
||||||
|
roomNotificationSettings = roomNotificationSettingsState.roomNotificationSettings(),
|
||||||
eventSink = ::handleEvents,
|
eventSink = ::handleEvents,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -122,4 +150,10 @@ class RoomDetailsPresenter @Inject constructor(
|
||||||
private fun getCanSendState(membersState: MatrixRoomMembersState, type: StateEventType) = produceState(false, membersState) {
|
private fun getCanSendState(membersState: MatrixRoomMembersState, type: StateEventType) = produceState(false, membersState) {
|
||||||
value = room.canSendState(type).getOrElse { false }
|
value = room.canSendState(type).getOrElse { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun CoroutineScope.observeNotificationSettings() {
|
||||||
|
notificationSettingsService.notificationSettingsChangeFlow.onEach {
|
||||||
|
room.updateRoomNotificationSettings()
|
||||||
|
}.launchIn(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package io.element.android.features.roomdetails.impl
|
||||||
import io.element.android.features.leaveroom.api.LeaveRoomState
|
import io.element.android.features.leaveroom.api.LeaveRoomState
|
||||||
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState
|
import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
|
||||||
|
|
||||||
data class RoomDetailsState(
|
data class RoomDetailsState(
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
|
|
@ -33,6 +34,7 @@ data class RoomDetailsState(
|
||||||
val canEdit: Boolean,
|
val canEdit: Boolean,
|
||||||
val canInvite: Boolean,
|
val canInvite: Boolean,
|
||||||
val leaveRoomState: LeaveRoomState,
|
val leaveRoomState: LeaveRoomState,
|
||||||
|
val roomNotificationSettings: RoomNotificationSettings?,
|
||||||
val eventSink: (RoomDetailsEvent) -> Unit
|
val eventSink: (RoomDetailsEvent) -> Unit
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ import io.element.android.features.roomdetails.impl.members.details.aRoomMemberD
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
|
||||||
|
|
||||||
open class RoomDetailsStateProvider : PreviewParameterProvider<RoomDetailsState> {
|
open class RoomDetailsStateProvider : PreviewParameterProvider<RoomDetailsState> {
|
||||||
override val values: Sequence<RoomDetailsState>
|
override val values: Sequence<RoomDetailsState>
|
||||||
|
|
@ -78,6 +80,7 @@ fun aRoomDetailsState() = RoomDetailsState(
|
||||||
roomType = RoomDetailsType.Room,
|
roomType = RoomDetailsType.Room,
|
||||||
roomMemberDetailsState = null,
|
roomMemberDetailsState = null,
|
||||||
leaveRoomState = LeaveRoomState(),
|
leaveRoomState = LeaveRoomState(),
|
||||||
|
roomNotificationSettings = RoomNotificationSettings(mode = RoomNotificationMode.MUTE, isDefault = false),
|
||||||
eventSink = {}
|
eventSink = {}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,15 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
import androidx.compose.material.icons.outlined.Add
|
import androidx.compose.material.icons.outlined.Add
|
||||||
import androidx.compose.material.icons.outlined.Lock
|
import androidx.compose.material.icons.outlined.Lock
|
||||||
|
import androidx.compose.material.icons.outlined.Notifications
|
||||||
|
import androidx.compose.material.icons.outlined.NotificationsOff
|
||||||
import androidx.compose.material.icons.outlined.Person
|
import androidx.compose.material.icons.outlined.Person
|
||||||
import androidx.compose.material.icons.outlined.PersonAddAlt
|
import androidx.compose.material.icons.outlined.PersonAddAlt
|
||||||
import androidx.compose.material.icons.outlined.Share
|
import androidx.compose.material.icons.outlined.Share
|
||||||
|
|
@ -73,6 +76,7 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
import io.element.android.libraries.theme.ElementTheme
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
|
|
@ -85,6 +89,7 @@ fun RoomDetailsView(
|
||||||
onShareRoom: () -> Unit,
|
onShareRoom: () -> Unit,
|
||||||
onShareMember: (RoomMember) -> Unit,
|
onShareMember: (RoomMember) -> Unit,
|
||||||
openRoomMemberList: () -> Unit,
|
openRoomMemberList: () -> Unit,
|
||||||
|
openRoomNotificationSettings: () -> Unit,
|
||||||
invitePeople: () -> Unit,
|
invitePeople: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
|
@ -118,7 +123,10 @@ fun RoomDetailsView(
|
||||||
roomName = state.roomName,
|
roomName = state.roomName,
|
||||||
roomAlias = state.roomAlias
|
roomAlias = state.roomAlias
|
||||||
)
|
)
|
||||||
MainActionsSection(onShareRoom = onShareRoom)
|
MainActionsSection(
|
||||||
|
state = state,
|
||||||
|
onShareRoom = onShareRoom
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is RoomDetailsType.Dm -> {
|
is RoomDetailsType.Dm -> {
|
||||||
|
|
@ -140,6 +148,12 @@ fun RoomDetailsView(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.roomNotificationSettings != null) {
|
||||||
|
NotificationSection(
|
||||||
|
state = state,
|
||||||
|
openRoomNotificationSettings = openRoomNotificationSettings)
|
||||||
|
}
|
||||||
|
|
||||||
if (state.roomType is RoomDetailsType.Room) {
|
if (state.roomType is RoomDetailsType.Room) {
|
||||||
MembersSection(
|
MembersSection(
|
||||||
memberCount = state.memberCount,
|
memberCount = state.memberCount,
|
||||||
|
|
@ -209,8 +223,21 @@ internal fun RoomDetailsTopBar(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun MainActionsSection(onShareRoom: () -> Unit, modifier: Modifier = Modifier) {
|
internal fun MainActionsSection(state: RoomDetailsState, onShareRoom: () -> Unit, modifier: Modifier = Modifier) {
|
||||||
Row(modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
Row(modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
||||||
|
val roomNotificationSettings = state.roomNotificationSettings
|
||||||
|
if (roomNotificationSettings != null) {
|
||||||
|
if (roomNotificationSettings.mode == RoomNotificationMode.MUTE) {
|
||||||
|
MainActionButton(title = stringResource(CommonStrings.common_unmute), icon = Icons.Outlined.NotificationsOff, onClick = {
|
||||||
|
state.eventSink(RoomDetailsEvent.UnmuteNotification)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
MainActionButton(title = stringResource(CommonStrings.common_mute), icon = Icons.Outlined.Notifications, onClick = {
|
||||||
|
state.eventSink(RoomDetailsEvent.MuteNotification)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.width(20.dp))
|
||||||
MainActionButton(title = stringResource(R.string.screen_room_details_share_room_title), icon = Icons.Outlined.Share, onClick = onShareRoom)
|
MainActionButton(title = stringResource(R.string.screen_room_details_share_room_title), icon = Icons.Outlined.Share, onClick = onShareRoom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -276,6 +303,21 @@ internal fun TopicSection(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun NotificationSection(state: RoomDetailsState, openRoomNotificationSettings: () -> Unit, modifier: Modifier = Modifier) {
|
||||||
|
state.roomNotificationSettings?.let {
|
||||||
|
val subtitle = if (it.isDefault) "Default" else "Custom"
|
||||||
|
PreferenceCategory(modifier = modifier) {
|
||||||
|
PreferenceText(
|
||||||
|
title = stringResource(R.string.screen_room_details_notification_title),
|
||||||
|
subtitle = subtitle,
|
||||||
|
icon = Icons.Outlined.Notifications,
|
||||||
|
onClick = openRoomNotificationSettings,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun MembersSection(
|
internal fun MembersSection(
|
||||||
memberCount: Long,
|
memberCount: Long,
|
||||||
|
|
@ -348,6 +390,7 @@ private fun ContentToPreview(state: RoomDetailsState) {
|
||||||
onShareRoom = {},
|
onShareRoom = {},
|
||||||
onShareMember = {},
|
onShareMember = {},
|
||||||
openRoomMemberList = {},
|
openRoomMemberList = {},
|
||||||
|
openRoomNotificationSettings = {},
|
||||||
invitePeople = {},
|
invitePeople = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* 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.features.roomdetails.impl.notificationsettings
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
|
||||||
|
sealed interface RoomNotificationSettingsEvents {
|
||||||
|
data class RoomNotificationModeChanged(val mode: RoomNotificationMode) : RoomNotificationSettingsEvents
|
||||||
|
object DefaultNotificationModeSelected: RoomNotificationSettingsEvents
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* 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.features.roomdetails.impl.notificationsettings
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
||||||
|
data class RoomNotificationSettingsItem(
|
||||||
|
val mode: RoomNotificationMode,
|
||||||
|
val title: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun roomNotificationSettingsItems(): ImmutableList<RoomNotificationSettingsItem> {
|
||||||
|
return RoomNotificationMode.values()
|
||||||
|
.map {
|
||||||
|
when (it) {
|
||||||
|
RoomNotificationMode.ALL_MESSAGES -> RoomNotificationSettingsItem(
|
||||||
|
mode = it,
|
||||||
|
title = "All messages",
|
||||||
|
)
|
||||||
|
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> RoomNotificationSettingsItem(
|
||||||
|
mode = it,
|
||||||
|
title = "Mentions and keywords",
|
||||||
|
)
|
||||||
|
RoomNotificationMode.MUTE -> RoomNotificationSettingsItem(
|
||||||
|
mode = it,
|
||||||
|
title = "Mute",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toImmutableList()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* 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.features.roomdetails.impl.notificationsettings
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
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 dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import im.vector.app.features.analytics.plan.MobileScreen
|
||||||
|
import io.element.android.anvilannotations.ContributesNode
|
||||||
|
import io.element.android.libraries.di.RoomScope
|
||||||
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
|
|
||||||
|
@ContributesNode(RoomScope::class)
|
||||||
|
class RoomNotificationSettingsNode @AssistedInject constructor(
|
||||||
|
@Assisted buildContext: BuildContext,
|
||||||
|
@Assisted plugins: List<Plugin>,
|
||||||
|
private val presenter: RoomNotificationSettingsPresenter,
|
||||||
|
private val analyticsService: AnalyticsService,
|
||||||
|
) : Node(buildContext, plugins = plugins) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
lifecycle.subscribe(
|
||||||
|
onResume = {
|
||||||
|
analyticsService.screen(MobileScreen(screenName = MobileScreen.ScreenName.RoomNotifications))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun View(modifier: Modifier) {
|
||||||
|
val state = presenter.present()
|
||||||
|
RoomNotificationSettingsView(
|
||||||
|
state = state,
|
||||||
|
modifier = modifier,
|
||||||
|
onBackPressed = { navigateUp() },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* 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.features.roomdetails.impl.notificationsettings
|
||||||
|
|
||||||
|
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.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.semantics.Role
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||||
|
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.RadioButton
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
|
import io.element.android.libraries.designsystem.toEnabledColor
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RoomNotificationSettingsOption(
|
||||||
|
roomNotificationSettingsItem: RoomNotificationSettingsItem,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
isSelected: Boolean = false,
|
||||||
|
onOptionSelected: (RoomNotificationSettingsItem) -> Unit = {},
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.selectable(
|
||||||
|
selected = isSelected,
|
||||||
|
onClick = { onOptionSelected(roomNotificationSettingsItem) },
|
||||||
|
role = Role.RadioButton,
|
||||||
|
)
|
||||||
|
.padding(8.dp),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(horizontal = 8.dp)
|
||||||
|
.align(Alignment.CenterVertically)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = roomNotificationSettingsItem.title,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
color = enabled.toEnabledColor(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
RadioButton(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.CenterVertically)
|
||||||
|
.size(48.dp),
|
||||||
|
selected = isSelected,
|
||||||
|
enabled = enabled,
|
||||||
|
onClick = null // null recommended for accessibility with screenreaders
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun RoomPrivacyOptionLightPreview() = ElementPreviewLight { ContentToPreview() }
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun RoomPrivacyOptionDarkPreview() = ElementPreviewDark { ContentToPreview() }
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ContentToPreview() {
|
||||||
|
Column {
|
||||||
|
RoomNotificationSettingsOption(
|
||||||
|
roomNotificationSettingsItem = roomNotificationSettingsItems().first(),
|
||||||
|
isSelected = true,
|
||||||
|
)
|
||||||
|
RoomNotificationSettingsOption(
|
||||||
|
roomNotificationSettingsItem = roomNotificationSettingsItems().last(),
|
||||||
|
isSelected = false,
|
||||||
|
enabled = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* 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.features.roomdetails.impl.notificationsettings
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import io.element.android.libraries.architecture.Presenter
|
||||||
|
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||||
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
import io.element.android.libraries.matrix.api.room.roomNotificationSettings
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class RoomNotificationSettingsPresenter @Inject constructor(
|
||||||
|
private val room: MatrixRoom,
|
||||||
|
private val notificationSettingsService: NotificationSettingsService,
|
||||||
|
) : Presenter<RoomNotificationSettingsState> {
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun present(): RoomNotificationSettingsState {
|
||||||
|
val defaultRoomNotificationMode: MutableState<RoomNotificationMode?> = rememberSaveable {
|
||||||
|
mutableStateOf(null)
|
||||||
|
}
|
||||||
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
getDefaultRoomNotificationMode(defaultRoomNotificationMode)
|
||||||
|
observeNotificationSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
val roomNotificationSettingsState by room.roomNotificationSettingsStateFlow.collectAsState()
|
||||||
|
|
||||||
|
fun handleEvents(event: RoomNotificationSettingsEvents) {
|
||||||
|
when (event) {
|
||||||
|
is RoomNotificationSettingsEvents.RoomNotificationModeChanged -> {
|
||||||
|
localCoroutineScope.setRoomNotificationMode(event.mode)
|
||||||
|
}
|
||||||
|
RoomNotificationSettingsEvents.DefaultNotificationModeSelected -> {
|
||||||
|
localCoroutineScope.restoreDefaultRoomNotificationMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RoomNotificationSettingsState(
|
||||||
|
roomNotificationSettings = roomNotificationSettingsState.roomNotificationSettings(),
|
||||||
|
defaultRoomNotificationMode = defaultRoomNotificationMode.value,
|
||||||
|
eventSink = ::handleEvents,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CoroutineScope.observeNotificationSettings() {
|
||||||
|
notificationSettingsService.notificationSettingsChangeFlow.onEach {
|
||||||
|
room.updateRoomNotificationSettings()
|
||||||
|
}.launchIn(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CoroutineScope.getDefaultRoomNotificationMode(defaultRoomNotificationMode: MutableState<RoomNotificationMode?>) = launch {
|
||||||
|
defaultRoomNotificationMode.value = notificationSettingsService.getDefaultRoomNotificationMode(
|
||||||
|
room.isEncrypted,
|
||||||
|
room.joinedMemberCount.toULong()
|
||||||
|
).getOrThrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CoroutineScope.setRoomNotificationMode(mode: RoomNotificationMode) = launch {
|
||||||
|
notificationSettingsService.setRoomNotificationMode(room.roomId, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CoroutineScope.restoreDefaultRoomNotificationMode() = launch {
|
||||||
|
notificationSettingsService.restoreDefaultRoomNotificationMode(room.roomId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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.features.roomdetails.impl.notificationsettings
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
|
||||||
|
|
||||||
|
data class RoomNotificationSettingsState(
|
||||||
|
val roomNotificationSettings: RoomNotificationSettings?,
|
||||||
|
val defaultRoomNotificationMode: RoomNotificationMode?,
|
||||||
|
val eventSink: (RoomNotificationSettingsEvents) -> Unit
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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.features.roomdetails.impl.notificationsettings
|
||||||
|
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
|
||||||
|
|
||||||
|
internal class RoomNotificationSettingsStateProvider : PreviewParameterProvider<RoomNotificationSettingsState> {
|
||||||
|
override val values: Sequence<RoomNotificationSettingsState>
|
||||||
|
get() = sequenceOf(
|
||||||
|
RoomNotificationSettingsState(
|
||||||
|
RoomNotificationSettings(
|
||||||
|
mode = RoomNotificationMode.MUTE,
|
||||||
|
isDefault = true),
|
||||||
|
RoomNotificationMode.ALL_MESSAGES,
|
||||||
|
eventSink = { },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* 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.features.roomdetails.impl.notificationsettings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||||
|
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.selection.selectableGroup
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import io.element.android.features.roomdetails.impl.R
|
||||||
|
import io.element.android.libraries.core.bool.orTrue
|
||||||
|
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.ElementPreviewDark
|
||||||
|
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.CenterAlignedTopAppBar
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
|
||||||
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
|
@Composable
|
||||||
|
fun RoomNotificationSettingsView(
|
||||||
|
state: RoomNotificationSettingsState,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onBackPressed: () -> Unit = {},
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
RoomNotificationSettingsTopBar(
|
||||||
|
onBackPressed = { onBackPressed() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { padding ->
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(padding)
|
||||||
|
.consumeWindowInsets(padding),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
) {
|
||||||
|
// PreferenceSwitch(
|
||||||
|
// isChecked = state.formState.sendLogs,
|
||||||
|
// onCheckedChange = { eventSink(BugReportEvents.SetSendLog(it)) },
|
||||||
|
// enabled = isFormEnabled,
|
||||||
|
// title = stringResource(id = R.string.screen_bug_report_include_logs),
|
||||||
|
// subtitle = stringResource(id = R.string.screen_bug_report_logs_description),
|
||||||
|
// )
|
||||||
|
val subtitle = when(state.defaultRoomNotificationMode) {
|
||||||
|
RoomNotificationMode.ALL_MESSAGES -> "All messages"
|
||||||
|
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> "Mentions and keywords"
|
||||||
|
RoomNotificationMode.MUTE -> "Mute"
|
||||||
|
null -> ""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PreferenceCategory(title = "Notify me in this chat for") {
|
||||||
|
PreferenceSwitch(
|
||||||
|
isChecked = state.roomNotificationSettings?.isDefault.orTrue(),
|
||||||
|
onCheckedChange = {
|
||||||
|
state.eventSink(RoomNotificationSettingsEvents.DefaultNotificationModeSelected)
|
||||||
|
},
|
||||||
|
title = "Match default setting",
|
||||||
|
subtitle = subtitle,
|
||||||
|
enabled = state.roomNotificationSettings != null
|
||||||
|
)
|
||||||
|
|
||||||
|
PreferenceText(
|
||||||
|
title = "Allow custom setting",
|
||||||
|
subtitle = "Turning this on will override yout default setting",
|
||||||
|
enabled = state.roomNotificationSettings != null && !state.roomNotificationSettings.isDefault,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (state.roomNotificationSettings != null) {
|
||||||
|
RoomNotificationSettingsOptions(
|
||||||
|
modifier = modifier,
|
||||||
|
selected = state.roomNotificationSettings.mode,
|
||||||
|
enabled = !state.roomNotificationSettings.isDefault,
|
||||||
|
onOptionSelected = {
|
||||||
|
state.eventSink(RoomNotificationSettingsEvents.RoomNotificationModeChanged(it.mode))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun RoomNotificationSettingsTopBar(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onBackPressed: () -> Unit = {},
|
||||||
|
) {
|
||||||
|
CenterAlignedTopAppBar(
|
||||||
|
modifier = modifier,
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.screen_room_details_notification_title),
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
navigationIcon = { BackButton(onClick = onBackPressed) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RoomNotificationSettingsOptions(
|
||||||
|
selected: RoomNotificationMode?,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean,
|
||||||
|
onOptionSelected: (RoomNotificationSettingsItem) -> Unit = {},
|
||||||
|
) {
|
||||||
|
val items = roomNotificationSettingsItems()
|
||||||
|
Column(modifier = modifier.selectableGroup()) {
|
||||||
|
items.forEach { item ->
|
||||||
|
RoomNotificationSettingsOption(
|
||||||
|
roomNotificationSettingsItem = item,
|
||||||
|
isSelected = selected == item.mode,
|
||||||
|
onOptionSelected = onOptionSelected,
|
||||||
|
enabled = enabled
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun RoomNotificationSettingsLightPreview(@PreviewParameter(RoomNotificationSettingsStateProvider::class) state: RoomNotificationSettingsState) =
|
||||||
|
ElementPreviewLight { ContentToPreview(state) }
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun RoomNotificationSettingsDarkPreview(@PreviewParameter(RoomNotificationSettingsStateProvider::class) state: RoomNotificationSettingsState) =
|
||||||
|
ElementPreviewDark { ContentToPreview(state) }
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ContentToPreview(state: RoomNotificationSettingsState) {
|
||||||
|
RoomNotificationSettingsView(state)
|
||||||
|
}
|
||||||
|
|
@ -28,17 +28,25 @@ import androidx.compose.foundation.progressSemantics
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.BugReport
|
import androidx.compose.material.icons.filled.BugReport
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.text.ExperimentalTextApi
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.rememberTextMeasurer
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.Constraints
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
import io.element.android.libraries.designsystem.components.preferences.components.PreferenceIcon
|
import io.element.android.libraries.designsystem.components.preferences.components.PreferenceIcon
|
||||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||||
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
|
import io.element.android.libraries.designsystem.toEnabledColor
|
||||||
|
import io.element.android.libraries.designsystem.toSecondaryEnabledColor
|
||||||
import io.element.android.libraries.theme.ElementTheme
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -48,6 +56,7 @@ import io.element.android.libraries.theme.ElementTheme
|
||||||
fun PreferenceText(
|
fun PreferenceText(
|
||||||
title: String,
|
title: String,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
subtitle: String? = null,
|
subtitle: String? = null,
|
||||||
currentValue: String? = null,
|
currentValue: String? = null,
|
||||||
loadingCurrentValue: Boolean = false,
|
loadingCurrentValue: Boolean = false,
|
||||||
|
|
@ -68,8 +77,9 @@ fun PreferenceText(
|
||||||
) {
|
) {
|
||||||
PreferenceIcon(
|
PreferenceIcon(
|
||||||
icon = icon,
|
icon = icon,
|
||||||
|
enabled = enabled,
|
||||||
isVisible = showIconAreaIfNoIcon,
|
isVisible = showIconAreaIfNoIcon,
|
||||||
tintColor = tintColor ?: ElementTheme.materialColors.secondary
|
tintColor = tintColor ?: enabled.toSecondaryEnabledColor(),
|
||||||
)
|
)
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -79,13 +89,13 @@ fun PreferenceText(
|
||||||
Text(
|
Text(
|
||||||
style = ElementTheme.typography.fontBodyLgRegular,
|
style = ElementTheme.typography.fontBodyLgRegular,
|
||||||
text = title,
|
text = title,
|
||||||
color = tintColor ?: ElementTheme.materialColors.primary,
|
color = tintColor ?: enabled.toEnabledColor(),
|
||||||
)
|
)
|
||||||
if (subtitle != null) {
|
if (subtitle != null) {
|
||||||
Text(
|
Text(
|
||||||
style = ElementTheme.typography.fontBodyMdRegular,
|
style = ElementTheme.typography.fontBodyMdRegular,
|
||||||
text = subtitle,
|
text = subtitle,
|
||||||
color = tintColor ?: ElementTheme.materialColors.secondary,
|
color = tintColor ?: enabled.toSecondaryEnabledColor(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +106,7 @@ fun PreferenceText(
|
||||||
.padding(start = 16.dp, end = 8.dp),
|
.padding(start = 16.dp, end = 8.dp),
|
||||||
text = currentValue,
|
text = currentValue,
|
||||||
style = ElementTheme.typography.fontBodyXsMedium,
|
style = ElementTheme.typography.fontBodyXsMedium,
|
||||||
color = ElementTheme.materialColors.secondary,
|
color = enabled.toSecondaryEnabledColor(),
|
||||||
)
|
)
|
||||||
} else if (loadingCurrentValue) {
|
} else if (loadingCurrentValue) {
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
|
|
@ -135,6 +145,13 @@ private fun ContentToPreview() {
|
||||||
icon = Icons.Default.BugReport,
|
icon = Icons.Default.BugReport,
|
||||||
currentValue = "123",
|
currentValue = "123",
|
||||||
)
|
)
|
||||||
|
PreferenceText(
|
||||||
|
title = "Title",
|
||||||
|
subtitle = "Some content",
|
||||||
|
icon = Icons.Default.BugReport,
|
||||||
|
currentValue = "123",
|
||||||
|
enabled = false,
|
||||||
|
)
|
||||||
PreferenceText(
|
PreferenceText(
|
||||||
title = "Title",
|
title = "Title",
|
||||||
subtitle = "Some content",
|
subtitle = "Some content",
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters
|
import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters
|
||||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
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.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.pusher.PushersService
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||||
|
|
@ -49,6 +50,7 @@ interface MatrixClient : Closeable {
|
||||||
fun sessionVerificationService(): SessionVerificationService
|
fun sessionVerificationService(): SessionVerificationService
|
||||||
fun pushersService(): PushersService
|
fun pushersService(): PushersService
|
||||||
fun notificationService(): NotificationService
|
fun notificationService(): NotificationService
|
||||||
|
fun notificationSettingsService(): NotificationSettingsService
|
||||||
suspend fun getCacheSize(): Long
|
suspend fun getCacheSize(): Long
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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.api.notificationsettings
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.room.MatrixRoomNotificationSettingsState
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
|
||||||
|
interface NotificationSettingsService {
|
||||||
|
/**
|
||||||
|
* State of the current room notification settings flow ([MatrixRoomNotificationSettingsState.Unknown] if not started).
|
||||||
|
*/
|
||||||
|
val notificationSettingsChangeFlow : SharedFlow<Unit>
|
||||||
|
suspend fun getRoomNotificationSettings(roomId: RoomId): Result<RoomNotificationSettings>
|
||||||
|
suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, membersCount: ULong): Result<RoomNotificationMode>
|
||||||
|
suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result<Unit>
|
||||||
|
suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result<Unit>
|
||||||
|
suspend fun muteRoom(roomId: RoomId): Result<Unit>
|
||||||
|
suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, membersCount: ULong): Result<Unit>
|
||||||
|
}
|
||||||
|
|
@ -30,6 +30,7 @@ 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.poll.PollKind
|
||||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
@ -56,11 +57,15 @@ interface MatrixRoom : Closeable {
|
||||||
*/
|
*/
|
||||||
val membersStateFlow: StateFlow<MatrixRoomMembersState>
|
val membersStateFlow: StateFlow<MatrixRoomMembersState>
|
||||||
|
|
||||||
|
val roomNotificationSettingsStateFlow: StateFlow<MatrixRoomNotificationSettingsState>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to load the room members and update the membersFlow.
|
* Try to load the room members and update the membersFlow.
|
||||||
*/
|
*/
|
||||||
suspend fun updateMembers(): Result<Unit>
|
suspend fun updateMembers(): Result<Unit>
|
||||||
|
|
||||||
|
suspend fun updateRoomNotificationSettings(): Result<Unit>
|
||||||
|
|
||||||
val syncUpdateFlow: StateFlow<Long>
|
val syncUpdateFlow: StateFlow<Long>
|
||||||
|
|
||||||
val timeline: MatrixTimeline
|
val timeline: MatrixTimeline
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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.api.room
|
||||||
|
|
||||||
|
sealed interface MatrixRoomNotificationSettingsState {
|
||||||
|
object Unknown : MatrixRoomNotificationSettingsState
|
||||||
|
data class Pending(val prevRoomNotificationSettings: RoomNotificationSettings? = null) : MatrixRoomNotificationSettingsState
|
||||||
|
data class Error(val failure: Throwable, val prevRoomNotificationSettings: RoomNotificationSettings? = null) : MatrixRoomNotificationSettingsState
|
||||||
|
data class Ready(val roomNotificationSettings: RoomNotificationSettings) : MatrixRoomNotificationSettingsState
|
||||||
|
}
|
||||||
|
|
||||||
|
fun MatrixRoomNotificationSettingsState.roomNotificationSettings(): RoomNotificationSettings? {
|
||||||
|
return when (this) {
|
||||||
|
is MatrixRoomNotificationSettingsState.Ready -> roomNotificationSettings
|
||||||
|
is MatrixRoomNotificationSettingsState.Pending -> prevRoomNotificationSettings
|
||||||
|
is MatrixRoomNotificationSettingsState.Error -> prevRoomNotificationSettings
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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.api.room
|
||||||
|
|
||||||
|
data class RoomNotificationSettings(
|
||||||
|
val mode: RoomNotificationMode,
|
||||||
|
val isDefault: Boolean,
|
||||||
|
)
|
||||||
|
|
||||||
|
enum class RoomNotificationMode {
|
||||||
|
ALL_MESSAGES, MENTIONS_AND_KEYWORDS_ONLY, MUTE
|
||||||
|
}
|
||||||
|
|
@ -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.createroom.RoomVisibility
|
||||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
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.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.pusher.PushersService
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
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.mapper.toSessionData
|
||||||
import io.element.android.libraries.matrix.impl.media.RustMediaLoader
|
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.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.pushers.RustPushersService
|
||||||
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
|
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
|
||||||
import io.element.android.libraries.matrix.impl.room.RustMatrixRoom
|
import io.element.android.libraries.matrix.impl.room.RustMatrixRoom
|
||||||
|
|
@ -103,6 +105,7 @@ class RustMatrixClient constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private val notificationService = RustNotificationService(sessionId, notificationClient, dispatchers, clock)
|
private val notificationService = RustNotificationService(sessionId, notificationClient, dispatchers, clock)
|
||||||
|
private val notificationSettingsService = RustNotificationSettingsService(client)
|
||||||
|
|
||||||
private val isLoggingOut = AtomicBoolean(false)
|
private val isLoggingOut = AtomicBoolean(false)
|
||||||
|
|
||||||
|
|
@ -168,6 +171,7 @@ class RustMatrixClient constructor(
|
||||||
sessionId = sessionId,
|
sessionId = sessionId,
|
||||||
roomListItem = roomListItem,
|
roomListItem = roomListItem,
|
||||||
innerRoom = fullRoom,
|
innerRoom = fullRoom,
|
||||||
|
roomNotificationSettingsService = notificationSettingsService,
|
||||||
sessionCoroutineScope = sessionCoroutineScope,
|
sessionCoroutineScope = sessionCoroutineScope,
|
||||||
coroutineDispatchers = dispatchers,
|
coroutineDispatchers = dispatchers,
|
||||||
systemClock = clock,
|
systemClock = clock,
|
||||||
|
|
@ -272,6 +276,8 @@ class RustMatrixClient constructor(
|
||||||
|
|
||||||
override fun notificationService(): NotificationService = notificationService
|
override fun notificationService(): NotificationService = notificationService
|
||||||
|
|
||||||
|
override fun notificationSettingsService(): NotificationSettingsService = notificationSettingsService
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
sessionCoroutineScope.cancel()
|
sessionCoroutineScope.cancel()
|
||||||
client.setDelegate(null)
|
client.setDelegate(null)
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import dagger.Provides
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
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.room.RoomMembershipObserver
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||||
|
|
@ -35,6 +36,13 @@ object SessionMatrixModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
@SingleIn(SessionScope::class)
|
||||||
|
fun providesNotificationSettingsService(matrixClient: MatrixClient): NotificationSettingsService {
|
||||||
|
return matrixClient.notificationSettingsService()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@SingleIn(SessionScope::class)
|
||||||
fun provideRoomMembershipObserver(matrixClient: MatrixClient): RoomMembershipObserver {
|
fun provideRoomMembershipObserver(matrixClient: MatrixClient): RoomMembershipObserver {
|
||||||
return 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,94 @@
|
||||||
|
/*
|
||||||
|
* 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.core.RoomId
|
||||||
|
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.room.RoomNotificationMode
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.channels.trySendBlocking
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.buffer
|
||||||
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
|
import org.matrix.rustcomponents.sdk.Client
|
||||||
|
import org.matrix.rustcomponents.sdk.NotificationSettings
|
||||||
|
import org.matrix.rustcomponents.sdk.NotificationSettingsDelegate
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomNotificationMode as RustRoomNotificationMode
|
||||||
|
|
||||||
|
class RustNotificationSettingsService(
|
||||||
|
private val client: Client,
|
||||||
|
) : NotificationSettingsService, NotificationSettingsDelegate {
|
||||||
|
|
||||||
|
private val notificationSettings: NotificationSettings = client.getNotificationSettings()
|
||||||
|
|
||||||
|
private val _notificationSettingsChangeFlow = MutableSharedFlow<Unit>()
|
||||||
|
override val notificationSettingsChangeFlow: SharedFlow<Unit> = _notificationSettingsChangeFlow.asSharedFlow()
|
||||||
|
|
||||||
|
// override val notificationSettingsChangeFlow = callbackFlow {
|
||||||
|
// val delegate = object:NotificationSettingsDelegate {
|
||||||
|
// override fun notificationSettingsDidChange() {
|
||||||
|
// trySendBlocking(Unit)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// send(Unit)
|
||||||
|
// notificationSettings.setDelegate(delegate)
|
||||||
|
// awaitClose {
|
||||||
|
// // notificationSettings.setDelegate(null)
|
||||||
|
// }
|
||||||
|
// }.buffer(Channel.UNLIMITED)
|
||||||
|
|
||||||
|
init {
|
||||||
|
notificationSettings.setDelegate(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getRoomNotificationSettings(roomId: RoomId): Result<RoomNotificationSettings> =
|
||||||
|
runCatching {
|
||||||
|
notificationSettings.getRoomNotificationMode(roomId.value).let(RoomNotificationSettingsMapper::map)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, membersCount: ULong): Result<RoomNotificationMode> =
|
||||||
|
runCatching {
|
||||||
|
notificationSettings.getDefaultRoomNotificationMode(isEncrypted, membersCount).let(RoomNotificationSettingsMapper::mapMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result<Unit> =
|
||||||
|
runCatching {
|
||||||
|
notificationSettings.setRoomNotificationMode(roomId.value, mode.let(RoomNotificationSettingsMapper::mapMode))
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result<Unit> =
|
||||||
|
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: ULong) =
|
||||||
|
runCatching {
|
||||||
|
notificationSettings.unmuteRoom(roomId.value, isEncrypted, membersCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun notificationSettingsDidChange() {
|
||||||
|
_notificationSettingsChangeFlow.tryEmit(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.poll.PollKind
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
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.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.MessageEventType
|
||||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
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.location.AssetType
|
||||||
import io.element.android.libraries.matrix.api.room.roomMembers
|
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.MatrixTimeline
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventType
|
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.core.toProgressWatcher
|
||||||
import io.element.android.libraries.matrix.impl.media.MediaUploadHandlerImpl
|
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.media.map
|
||||||
import io.element.android.libraries.matrix.impl.poll.toInner
|
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.room.location.toInner
|
||||||
import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline
|
import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline
|
||||||
import io.element.android.libraries.matrix.impl.util.destroyAll
|
import io.element.android.libraries.matrix.impl.util.destroyAll
|
||||||
|
|
@ -72,6 +75,7 @@ class RustMatrixRoom(
|
||||||
override val sessionId: SessionId,
|
override val sessionId: SessionId,
|
||||||
private val roomListItem: RoomListItem,
|
private val roomListItem: RoomListItem,
|
||||||
private val innerRoom: Room,
|
private val innerRoom: Room,
|
||||||
|
private val roomNotificationSettingsService: RustNotificationSettingsService,
|
||||||
sessionCoroutineScope: CoroutineScope,
|
sessionCoroutineScope: CoroutineScope,
|
||||||
private val coroutineDispatchers: CoroutineDispatchers,
|
private val coroutineDispatchers: CoroutineDispatchers,
|
||||||
private val systemClock: SystemClock,
|
private val systemClock: SystemClock,
|
||||||
|
|
@ -90,6 +94,10 @@ class RustMatrixRoom(
|
||||||
private val roomCoroutineScope = sessionCoroutineScope.childScope(coroutineDispatchers.main, "RoomScope-$roomId")
|
private val roomCoroutineScope = sessionCoroutineScope.childScope(coroutineDispatchers.main, "RoomScope-$roomId")
|
||||||
private val _membersStateFlow = MutableStateFlow<MatrixRoomMembersState>(MatrixRoomMembersState.Unknown)
|
private val _membersStateFlow = MutableStateFlow<MatrixRoomMembersState>(MatrixRoomMembersState.Unknown)
|
||||||
private val _syncUpdateFlow = MutableStateFlow(0L)
|
private val _syncUpdateFlow = MutableStateFlow(0L)
|
||||||
|
|
||||||
|
private val _roomNotificationSettingsStateFlow = MutableStateFlow<MatrixRoomNotificationSettingsState>(MatrixRoomNotificationSettingsState.Unknown)
|
||||||
|
override val roomNotificationSettingsStateFlow: StateFlow<MatrixRoomNotificationSettingsState> = _roomNotificationSettingsStateFlow
|
||||||
|
|
||||||
private val _timeline by lazy {
|
private val _timeline by lazy {
|
||||||
RustMatrixTimeline(
|
RustMatrixTimeline(
|
||||||
matrixRoom = this,
|
matrixRoom = this,
|
||||||
|
|
@ -197,6 +205,19 @@ 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).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) {
|
override suspend fun userAvatarUrl(userId: UserId): Result<String?> = withContext(roomDispatcher) {
|
||||||
runCatching {
|
runCatching {
|
||||||
innerRoom.memberAvatarUrl(userId.value)
|
innerRoom.memberAvatarUrl(userId.value)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters
|
import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters
|
||||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
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.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.pusher.PushersService
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||||
|
|
@ -33,6 +34,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||||
import io.element.android.libraries.matrix.test.media.FakeMediaLoader
|
import io.element.android.libraries.matrix.test.media.FakeMediaLoader
|
||||||
import io.element.android.libraries.matrix.test.notification.FakeNotificationService
|
import io.element.android.libraries.matrix.test.notification.FakeNotificationService
|
||||||
|
import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService
|
||||||
import io.element.android.libraries.matrix.test.pushers.FakePushersService
|
import io.element.android.libraries.matrix.test.pushers.FakePushersService
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||||
|
|
@ -50,6 +52,7 @@ class FakeMatrixClient(
|
||||||
private val sessionVerificationService: FakeSessionVerificationService = FakeSessionVerificationService(),
|
private val sessionVerificationService: FakeSessionVerificationService = FakeSessionVerificationService(),
|
||||||
private val pushersService: FakePushersService = FakePushersService(),
|
private val pushersService: FakePushersService = FakePushersService(),
|
||||||
private val notificationService: FakeNotificationService = FakeNotificationService(),
|
private val notificationService: FakeNotificationService = FakeNotificationService(),
|
||||||
|
private val notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(),
|
||||||
private val syncService: FakeSyncService = FakeSyncService(),
|
private val syncService: FakeSyncService = FakeSyncService(),
|
||||||
private val accountManagementUrlString: Result<String?> = Result.success(null),
|
private val accountManagementUrlString: Result<String?> = Result.success(null),
|
||||||
) : MatrixClient {
|
) : MatrixClient {
|
||||||
|
|
@ -142,6 +145,7 @@ class FakeMatrixClient(
|
||||||
override fun pushersService(): PushersService = pushersService
|
override fun pushersService(): PushersService = pushersService
|
||||||
|
|
||||||
override fun notificationService(): NotificationService = notificationService
|
override fun notificationService(): NotificationService = notificationService
|
||||||
|
override fun notificationSettingsService(): NotificationSettingsService = notificationSettingsService
|
||||||
|
|
||||||
override fun roomMembershipObserver(): RoomMembershipObserver {
|
override fun roomMembershipObserver(): RoomMembershipObserver {
|
||||||
return RoomMembershipObserver()
|
return RoomMembershipObserver()
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ import io.element.android.libraries.matrix.api.core.SpaceId
|
||||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomNotificationSettings
|
||||||
|
|
||||||
const val A_USER_NAME = "alice"
|
const val A_USER_NAME = "alice"
|
||||||
const val A_PASSWORD = "password"
|
const val A_PASSWORD = "password"
|
||||||
|
|
@ -51,6 +53,7 @@ const val A_HOMESERVER_URL_2 = "matrix-client.org"
|
||||||
|
|
||||||
val A_HOMESERVER = MatrixHomeServerDetails(A_HOMESERVER_URL, supportsPasswordLogin = true, supportsOidcLogin = false)
|
val A_HOMESERVER = MatrixHomeServerDetails(A_HOMESERVER_URL, supportsPasswordLogin = true, supportsOidcLogin = false)
|
||||||
val A_HOMESERVER_OIDC = MatrixHomeServerDetails(A_HOMESERVER_URL, supportsPasswordLogin = false, supportsOidcLogin = true)
|
val A_HOMESERVER_OIDC = MatrixHomeServerDetails(A_HOMESERVER_URL, supportsPasswordLogin = false, supportsOidcLogin = true)
|
||||||
|
val A_ROOM_NOTIFICATION_SETTINGS = RoomNotificationSettings(mode = RoomNotificationMode.MUTE, isDefault = false)
|
||||||
|
|
||||||
const val AN_AVATAR_URL = "mxc://data"
|
const val AN_AVATAR_URL = "mxc://data"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* 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.test.notificationsettings
|
||||||
|
|
||||||
|
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 io.element.android.libraries.matrix.test.A_ROOM_NOTIFICATION_SETTINGS
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
|
||||||
|
class FakeNotificationSettingsService : NotificationSettingsService {
|
||||||
|
private var _roomNotificationSettingsStateFlow = MutableStateFlow(Unit)
|
||||||
|
private val muteRoomResult: Result<Unit> = Result.success(Unit)
|
||||||
|
private val unmuteRoomResult: Result<Unit> = Result.success(Unit)
|
||||||
|
private val getRoomNotificationSettingsResult: Result<RoomNotificationSettings> = Result.success(A_ROOM_NOTIFICATION_SETTINGS)
|
||||||
|
override val notificationSettingsChangeFlow: SharedFlow<Unit>
|
||||||
|
get() = _roomNotificationSettingsStateFlow
|
||||||
|
|
||||||
|
override suspend fun getRoomNotificationSettings(roomId: RoomId): Result<RoomNotificationSettings> {
|
||||||
|
return getRoomNotificationSettingsResult
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, membersCount: ULong): Result<RoomNotificationMode> {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result<Unit> {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result<Unit> {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun muteRoom(roomId: RoomId): Result<Unit> {
|
||||||
|
return muteRoomResult
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, membersCount: ULong): Result<Unit> {
|
||||||
|
return unmuteRoomResult
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,6 +31,7 @@ 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.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||||
import io.element.android.libraries.matrix.api.room.MessageEventType
|
import io.element.android.libraries.matrix.api.room.MessageEventType
|
||||||
|
import io.element.android.libraries.matrix.api.room.MatrixRoomNotificationSettingsState
|
||||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
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.location.AssetType
|
||||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||||
|
|
@ -68,6 +69,9 @@ class FakeMatrixRoom(
|
||||||
private var userAvatarUrlResult = Result.success<String?>(null)
|
private var userAvatarUrlResult = Result.success<String?>(null)
|
||||||
private var updateMembersResult: Result<Unit> = Result.success(Unit)
|
private var updateMembersResult: Result<Unit> = Result.success(Unit)
|
||||||
private var joinRoomResult = Result.success(Unit)
|
private var joinRoomResult = Result.success(Unit)
|
||||||
|
private var updateRoomNotificationSettingsResult: Result<Unit> = Result.success(Unit)
|
||||||
|
private var acceptInviteResult = Result.success(Unit)
|
||||||
|
private var rejectInviteResult = Result.success(Unit)
|
||||||
private var inviteUserResult = Result.success(Unit)
|
private var inviteUserResult = Result.success(Unit)
|
||||||
private var canInviteResult = Result.success(true)
|
private var canInviteResult = Result.success(true)
|
||||||
private var canRedactResult = Result.success(canRedact)
|
private var canRedactResult = Result.success(canRedact)
|
||||||
|
|
@ -128,10 +132,17 @@ class FakeMatrixRoom(
|
||||||
|
|
||||||
override val membersStateFlow: MutableStateFlow<MatrixRoomMembersState> = MutableStateFlow(MatrixRoomMembersState.Unknown)
|
override val membersStateFlow: MutableStateFlow<MatrixRoomMembersState> = MutableStateFlow(MatrixRoomMembersState.Unknown)
|
||||||
|
|
||||||
|
override val roomNotificationSettingsStateFlow: MutableStateFlow<MatrixRoomNotificationSettingsState> =
|
||||||
|
MutableStateFlow(MatrixRoomNotificationSettingsState.Unknown)
|
||||||
|
|
||||||
override suspend fun updateMembers(): Result<Unit> = simulateLongTask {
|
override suspend fun updateMembers(): Result<Unit> = simulateLongTask {
|
||||||
updateMembersResult
|
updateMembersResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun updateRoomNotificationSettings(): Result<Unit> = simulateLongTask {
|
||||||
|
updateRoomNotificationSettingsResult
|
||||||
|
}
|
||||||
|
|
||||||
override val syncUpdateFlow: StateFlow<Long> = MutableStateFlow(0L)
|
override val syncUpdateFlow: StateFlow<Long> = MutableStateFlow(0L)
|
||||||
|
|
||||||
override val timeline: MatrixTimeline = matrixTimeline
|
override val timeline: MatrixTimeline = matrixTimeline
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue