Add roomsWithUserDefinedRules data and render list

- get roomsWithUserDefinedRules from rust
- add to state in the presenter
- render in the edit defaults view as a list
This commit is contained in:
David Langley
2023-09-22 16:16:52 +01:00
parent ab33d064bb
commit a9d87da1ff
7 changed files with 147 additions and 0 deletions

View File

@@ -26,19 +26,25 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import io.element.android.libraries.architecture.Presenter
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.RoomNotificationMode
import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import java.text.Collator
import kotlin.time.Duration.Companion.seconds
class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
private val notificationSettingsService: NotificationSettingsService,
@Assisted private val isOneToOne: Boolean,
private val roomListService: RoomListService,
private val matrixClient: MatrixClient,
) : Presenter<EditDefaultNotificationSettingState> {
@AssistedFactory
interface Factory {
@@ -50,10 +56,16 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
val mode: MutableState<RoomNotificationMode?> = remember {
mutableStateOf(null)
}
val roomsWithUserDefinedMode: MutableState<List<RoomSummary.Filled>> = remember {
mutableStateOf(listOf())
}
val localCoroutineScope = rememberCoroutineScope()
LaunchedEffect(Unit) {
fetchSettings(mode)
observeNotificationSettings(mode)
observeRoomSummaries(roomsWithUserDefinedMode)
}
fun handleEvents(event: EditDefaultNotificationSettingStateEvents) {
@@ -65,6 +77,7 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
return EditDefaultNotificationSettingState(
isOneToOne = isOneToOne,
mode = mode.value,
roomsWithUserDefinedMode = roomsWithUserDefinedMode.value,
eventSink = ::handleEvents
)
}
@@ -83,6 +96,27 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
.launchIn(this)
}
private fun CoroutineScope.observeRoomSummaries(roomsWithUserDefinedMode: MutableState<List<RoomSummary.Filled>>) {
roomListService.allRooms()
.summaries
.onEach {
updateRoomsWithUserDefinedMode(it, roomsWithUserDefinedMode)
}
.launchIn(this)
}
private fun CoroutineScope.updateRoomsWithUserDefinedMode(summaries: List<RoomSummary>, roomsWithUserDefinedMode: MutableState<List<RoomSummary.Filled>>) = launch {
val roomWithUserDefinedRules = notificationSettingsService.getRoomsWithUserDefinedRules().getOrThrow().toSet()
roomsWithUserDefinedMode.value = summaries
.filterIsInstance<RoomSummary.Filled>()
.filter {
val room = matrixClient.getRoom(it.details.roomId) ?: return@filter false
roomWithUserDefinedRules.contains(it.identifier()) && isOneToOne == room.isOneToOne
}
// locale sensitive sorting
.sortedWith(compareBy(Collator.getInstance()){ it.details.name })
}
private fun CoroutineScope.setDefaultNotificationMode(mode: RoomNotificationMode) = launch {
// On modern clients, we don't have different settings for encrypted and non-encrypted rooms (Legacy clients did).
notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, mode = mode, isOneToOne = isOneToOne)

View File

@@ -17,9 +17,11 @@
package io.element.android.features.preferences.impl.notifications.edit
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
data class EditDefaultNotificationSettingState(
val isOneToOne: Boolean,
val mode: RoomNotificationMode?,
val roomsWithUserDefinedMode: List<RoomSummary.Filled>,
val eventSink: (EditDefaultNotificationSettingStateEvents) -> Unit,
)

View File

@@ -17,12 +17,26 @@
package io.element.android.features.preferences.impl.notifications.edit
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.features.preferences.impl.notifications.NotificationSettingsState
import io.element.android.features.preferences.impl.notifications.NotificationSettingsStateProvider
import io.element.android.features.preferences.impl.notifications.NotificationSettingsView
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.components.preferences.PreferenceView
import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.ListItem
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.ui.strings.CommonStrings
@@ -70,6 +84,46 @@ fun EditDefaultNotificationSettingView(
}
}
}
if(state.roomsWithUserDefinedMode.isNotEmpty()) {
PreferenceCategory(title = stringResource(id = CommonStrings.screen_notification_settings_edit_custom_settings_section_title)) {
LazyColumn {
items(state.roomsWithUserDefinedMode) { summary ->
val subtitle = when (summary.details.notificationMode) {
RoomNotificationMode.ALL_MESSAGES -> stringResource(id = CommonStrings.screen_notification_settings_edit_mode_all_messages)
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> stringResource(id = CommonStrings.screen_notification_settings_edit_mode_mentions_and_keywords)
RoomNotificationMode.MUTE -> stringResource(id = CommonStrings.common_mute)
null -> ""
}
val avatarData = AvatarData(
id = summary.identifier(),
name = summary.details.name,
url = summary.details.avatarURLString,
size = AvatarSize.CustomRoomNotificationSetting,
)
ListItem(
headlineContent = {
Text(text = summary.details.name)
},
supportingContent = {
Text(text = subtitle)
},
leadingContent = ListItemContent.Custom {
Avatar(avatarData = avatarData)
}
)
}
}
}
}
}
}
@DayNightPreviews
@Composable
internal fun EditDefaultNotificationSettingViewPreview(@PreviewParameter(EditDefaultNotificationSettingsStateProvider::class) state: EditDefaultNotificationSettingState) = ElementPreview {
EditDefaultNotificationSettingView(
state = state,
onBackPressed = {},
)
}

View File

@@ -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.preferences.impl.notifications.edit
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails
open class EditDefaultNotificationSettingsStateProvider: PreviewParameterProvider<EditDefaultNotificationSettingState> {
override val values: Sequence<EditDefaultNotificationSettingState>
get() = sequenceOf(
anEditDefaultNotificationSettingsState(),
)
}
fun anEditDefaultNotificationSettingsState() = EditDefaultNotificationSettingState(
isOneToOne = false,
mode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
roomsWithUserDefinedMode = listOf(aRoomSummary()),
eventSink = {}
)
private fun aRoomSummary() = RoomSummary.Filled(
RoomSummaryDetails(
roomId = RoomId("!roomId:domain"),
name = "Room",
avatarURLString = null,
isDirect = false,
lastMessage = null,
lastMessageTimestamp = null,
unreadNotificationCount = 0,
)
)

View File

@@ -46,4 +46,6 @@ enum class AvatarSize(val dp: Dp) {
EditRoomDetails(70.dp),
NotificationsOptIn(32.dp),
CustomRoomNotificationSetting(36.dp)
}

View File

@@ -38,4 +38,5 @@ interface NotificationSettingsService {
suspend fun setRoomMentionEnabled(enabled: Boolean): Result<Unit>
suspend fun isCallEnabled(): Result<Boolean>
suspend fun setCallEnabled(enabled: Boolean): Result<Unit>
suspend fun getRoomsWithUserDefinedRules(): Result<List<String>>
}

View File

@@ -110,4 +110,9 @@ class RustNotificationSettingsService(
notificationSettings.setCallEnabled(enabled)
}
}
override suspend fun getRoomsWithUserDefinedRules(): Result<List<String>> =
runCatching {
notificationSettings.getRoomsWithUserDefinedRules(enabled = true)
}
}