List user define room notification settings
- List user define room notification settings - Add new user defined style of the room notification settings view - Add navigation to expose room notification settings ui to the global settings - Add Progress indicators - Improve error handing
This commit is contained in:
parent
a9d87da1ff
commit
b634db1772
25 changed files with 375 additions and 70 deletions
|
|
@ -244,6 +244,10 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||||
override fun onVerifyClicked() {
|
override fun onVerifyClicked() {
|
||||||
backstack.push(NavTarget.VerifySession)
|
backstack.push(NavTarget.VerifySession)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onOpenRoomNotificationSettings(roomId: RoomId) {
|
||||||
|
backstack.push(NavTarget.Room(roomId, initialElement = RoomLoadedFlowNode.NavTarget.RoomNotificationSettings))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
preferencesEntryPoint.nodeBuilder(this, buildContext)
|
preferencesEntryPoint.nodeBuilder(this, buildContext)
|
||||||
.callback(callback)
|
.callback(callback)
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,10 @@ class RoomLoadedFlowNode @AssistedInject constructor(
|
||||||
val inputs = RoomDetailsEntryPoint.Inputs(RoomDetailsEntryPoint.InitialTarget.RoomMemberDetails(navTarget.userId))
|
val inputs = RoomDetailsEntryPoint.Inputs(RoomDetailsEntryPoint.InitialTarget.RoomMemberDetails(navTarget.userId))
|
||||||
roomDetailsEntryPoint.createNode(this, buildContext, inputs, emptyList())
|
roomDetailsEntryPoint.createNode(this, buildContext, inputs, emptyList())
|
||||||
}
|
}
|
||||||
|
NavTarget.RoomNotificationSettings -> {
|
||||||
|
val inputs = RoomDetailsEntryPoint.Inputs(RoomDetailsEntryPoint.InitialTarget.RoomNotificationSettings)
|
||||||
|
roomDetailsEntryPoint.createNode(this, buildContext, inputs, emptyList())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,6 +158,9 @@ class RoomLoadedFlowNode @AssistedInject constructor(
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class RoomMemberDetails(val userId: UserId) : NavTarget
|
data class RoomMemberDetails(val userId: UserId) : NavTarget
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data object RoomNotificationSettings : NavTarget
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import io.element.android.libraries.architecture.FeatureEntryPoint
|
import io.element.android.libraries.architecture.FeatureEntryPoint
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
||||||
interface PreferencesEntryPoint : FeatureEntryPoint {
|
interface PreferencesEntryPoint : FeatureEntryPoint {
|
||||||
|
|
||||||
|
|
@ -33,5 +34,6 @@ interface PreferencesEntryPoint : FeatureEntryPoint {
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onOpenBugReport()
|
fun onOpenBugReport()
|
||||||
fun onVerifyClicked()
|
fun onVerifyClicked()
|
||||||
|
fun onOpenRoomNotificationSettings(roomId: RoomId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ 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
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
|
@ -152,8 +153,13 @@ class PreferencesFlowNode @AssistedInject constructor(
|
||||||
createNode<NotificationSettingsNode>(buildContext, listOf(notificationSettingsCallback))
|
createNode<NotificationSettingsNode>(buildContext, listOf(notificationSettingsCallback))
|
||||||
}
|
}
|
||||||
is NavTarget.EditDefaultNotificationSetting -> {
|
is NavTarget.EditDefaultNotificationSetting -> {
|
||||||
|
val callback = object : EditDefaultNotificationSettingNode.Callback {
|
||||||
|
override fun openRoomNotificationSettings(roomId: RoomId) {
|
||||||
|
plugins<PreferencesEntryPoint.Callback>().forEach { it.onOpenRoomNotificationSettings(roomId) }
|
||||||
|
}
|
||||||
|
}
|
||||||
val input = EditDefaultNotificationSettingNode.Inputs(navTarget.isOneToOne)
|
val input = EditDefaultNotificationSettingNode.Inputs(navTarget.isOneToOne)
|
||||||
createNode<EditDefaultNotificationSettingNode>(buildContext, plugins = listOf(input))
|
createNode<EditDefaultNotificationSettingNode>(buildContext, plugins = listOf(input, callback))
|
||||||
}
|
}
|
||||||
NavTarget.AdvancedSettings -> {
|
NavTarget.AdvancedSettings -> {
|
||||||
createNode<AdvancedSettingsNode>(buildContext)
|
createNode<AdvancedSettingsNode>(buildContext)
|
||||||
|
|
|
||||||
|
|
@ -24,4 +24,5 @@ sealed interface NotificationSettingsEvents {
|
||||||
data class SetCallNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents
|
data class SetCallNotificationsEnabled(val enabled: Boolean) : NotificationSettingsEvents
|
||||||
data object FixConfigurationMismatch : NotificationSettingsEvents
|
data object FixConfigurationMismatch : NotificationSettingsEvents
|
||||||
data object ClearConfigurationMismatchError : NotificationSettingsEvents
|
data object ClearConfigurationMismatchError : NotificationSettingsEvents
|
||||||
|
data object ClearNotificationChangeError : NotificationSettingsEvents
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,9 @@ import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
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.RoomNotificationMode
|
||||||
|
|
@ -50,6 +52,7 @@ class NotificationSettingsPresenter @Inject constructor(
|
||||||
val systemNotificationsEnabled: MutableState<Boolean> = remember {
|
val systemNotificationsEnabled: MutableState<Boolean> = remember {
|
||||||
mutableStateOf(systemNotificationsEnabledProvider.notificationsEnabled())
|
mutableStateOf(systemNotificationsEnabledProvider.notificationsEnabled())
|
||||||
}
|
}
|
||||||
|
val changeNotificationSettingAction: MutableState<Async<Unit>> = remember { mutableStateOf(Async.Uninitialized) }
|
||||||
|
|
||||||
val localCoroutineScope = rememberCoroutineScope()
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
val appNotificationsEnabled = userPushStore
|
val appNotificationsEnabled = userPushStore
|
||||||
|
|
@ -67,8 +70,12 @@ class NotificationSettingsPresenter @Inject constructor(
|
||||||
|
|
||||||
fun handleEvents(event: NotificationSettingsEvents) {
|
fun handleEvents(event: NotificationSettingsEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is NotificationSettingsEvents.SetAtRoomNotificationsEnabled -> localCoroutineScope.setAtRoomNotificationsEnabled(event.enabled)
|
is NotificationSettingsEvents.SetAtRoomNotificationsEnabled -> {
|
||||||
is NotificationSettingsEvents.SetCallNotificationsEnabled -> localCoroutineScope.setCallNotificationsEnabled(event.enabled)
|
localCoroutineScope.setAtRoomNotificationsEnabled(event.enabled, changeNotificationSettingAction)
|
||||||
|
}
|
||||||
|
is NotificationSettingsEvents.SetCallNotificationsEnabled -> {
|
||||||
|
localCoroutineScope.setCallNotificationsEnabled(event.enabled, changeNotificationSettingAction)
|
||||||
|
}
|
||||||
is NotificationSettingsEvents.SetNotificationsEnabled -> localCoroutineScope.setNotificationsEnabled(userPushStore, event.enabled)
|
is NotificationSettingsEvents.SetNotificationsEnabled -> localCoroutineScope.setNotificationsEnabled(userPushStore, event.enabled)
|
||||||
NotificationSettingsEvents.ClearConfigurationMismatchError -> {
|
NotificationSettingsEvents.ClearConfigurationMismatchError -> {
|
||||||
matrixSettings.value = NotificationSettingsState.MatrixSettings.Invalid(fixFailed = false)
|
matrixSettings.value = NotificationSettingsState.MatrixSettings.Invalid(fixFailed = false)
|
||||||
|
|
@ -77,6 +84,7 @@ class NotificationSettingsPresenter @Inject constructor(
|
||||||
NotificationSettingsEvents.RefreshSystemNotificationsEnabled -> {
|
NotificationSettingsEvents.RefreshSystemNotificationsEnabled -> {
|
||||||
systemNotificationsEnabled.value = systemNotificationsEnabledProvider.notificationsEnabled()
|
systemNotificationsEnabled.value = systemNotificationsEnabledProvider.notificationsEnabled()
|
||||||
}
|
}
|
||||||
|
NotificationSettingsEvents.ClearNotificationChangeError -> changeNotificationSettingAction.value = Async.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,6 +94,7 @@ class NotificationSettingsPresenter @Inject constructor(
|
||||||
systemNotificationsEnabled = systemNotificationsEnabled.value,
|
systemNotificationsEnabled = systemNotificationsEnabled.value,
|
||||||
appNotificationsEnabled = appNotificationsEnabled.value
|
appNotificationsEnabled = appNotificationsEnabled.value
|
||||||
),
|
),
|
||||||
|
changeNotificationSettingAction = changeNotificationSettingAction.value,
|
||||||
eventSink = ::handleEvents
|
eventSink = ::handleEvents
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -154,12 +163,16 @@ class NotificationSettingsPresenter @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.setAtRoomNotificationsEnabled(enabled: Boolean) = launch {
|
private fun CoroutineScope.setAtRoomNotificationsEnabled(enabled: Boolean, action: MutableState<Async<Unit>>) = launch {
|
||||||
notificationSettingsService.setRoomMentionEnabled(enabled)
|
suspend {
|
||||||
|
notificationSettingsService.setRoomMentionEnabled(enabled).getOrThrow()
|
||||||
|
}.runCatchingUpdatingState(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.setCallNotificationsEnabled(enabled: Boolean) = launch {
|
private fun CoroutineScope.setCallNotificationsEnabled(enabled: Boolean, action: MutableState<Async<Unit>>) = launch {
|
||||||
notificationSettingsService.setCallEnabled(enabled)
|
suspend {
|
||||||
|
notificationSettingsService.setCallEnabled(enabled).getOrThrow()
|
||||||
|
}.runCatchingUpdatingState(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.setNotificationsEnabled(userPushStore: UserPushStore, enabled: Boolean) = launch {
|
private fun CoroutineScope.setNotificationsEnabled(userPushStore: UserPushStore, enabled: Boolean) = launch {
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,14 @@
|
||||||
package io.element.android.features.preferences.impl.notifications
|
package io.element.android.features.preferences.impl.notifications
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class NotificationSettingsState(
|
data class NotificationSettingsState(
|
||||||
val matrixSettings: MatrixSettings,
|
val matrixSettings: MatrixSettings,
|
||||||
val appSettings: AppSettings,
|
val appSettings: AppSettings,
|
||||||
|
val changeNotificationSettingAction: Async<Unit>,
|
||||||
val eventSink: (NotificationSettingsEvents) -> Unit,
|
val eventSink: (NotificationSettingsEvents) -> Unit,
|
||||||
) {
|
) {
|
||||||
sealed interface MatrixSettings {
|
sealed interface MatrixSettings {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package io.element.android.features.preferences.impl.notifications
|
package io.element.android.features.preferences.impl.notifications
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
|
||||||
open class NotificationSettingsStateProvider : PreviewParameterProvider<NotificationSettingsState> {
|
open class NotificationSettingsStateProvider : PreviewParameterProvider<NotificationSettingsState> {
|
||||||
|
|
@ -37,5 +38,6 @@ fun aNotificationSettingsState() = NotificationSettingsState(
|
||||||
systemNotificationsEnabled = false,
|
systemNotificationsEnabled = false,
|
||||||
appNotificationsEnabled = true,
|
appNotificationsEnabled = true,
|
||||||
),
|
),
|
||||||
|
changeNotificationSettingAction = Async.Uninitialized,
|
||||||
eventSink = {}
|
eventSink = {}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import io.element.android.libraries.androidutils.system.startNotificationSettingsIntent
|
import io.element.android.libraries.androidutils.system.startNotificationSettingsIntent
|
||||||
|
import io.element.android.libraries.architecture.Async
|
||||||
|
import io.element.android.libraries.designsystem.components.ProgressDialog
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
|
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.PreferenceSwitch
|
||||||
|
|
@ -91,6 +93,19 @@ fun NotificationSettingsView(
|
||||||
// onCallsNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(it)) },
|
// onCallsNotificationsChanged = { state.eventSink(NotificationSettingsEvents.SetCallNotificationsEnabled(it)) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
when (state.changeNotificationSettingAction) {
|
||||||
|
is Async.Loading -> {
|
||||||
|
ProgressDialog()
|
||||||
|
}
|
||||||
|
is Async.Failure -> {
|
||||||
|
ErrorDialog(
|
||||||
|
title = stringResource(CommonStrings.dialog_title_error),
|
||||||
|
content = stringResource(CommonStrings.screen_notification_settings_edit_failed_updating_default_mode),
|
||||||
|
onDismiss = { state.eventSink(NotificationSettingsEvents.ClearNotificationChangeError) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,14 @@ import androidx.compose.ui.Modifier
|
||||||
import com.bumble.appyx.core.modality.BuildContext
|
import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.core.node.Node
|
import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
|
import com.bumble.appyx.core.plugin.plugins
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.anvilannotations.ContributesNode
|
import io.element.android.anvilannotations.ContributesNode
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
import io.element.android.libraries.architecture.inputs
|
import io.element.android.libraries.architecture.inputs
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SessionScope::class)
|
||||||
class EditDefaultNotificationSettingNode @AssistedInject constructor(
|
class EditDefaultNotificationSettingNode @AssistedInject constructor(
|
||||||
|
|
@ -35,20 +37,30 @@ class EditDefaultNotificationSettingNode @AssistedInject constructor(
|
||||||
presenterFactory: EditDefaultNotificationSettingPresenter.Factory
|
presenterFactory: EditDefaultNotificationSettingPresenter.Factory
|
||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
|
|
||||||
|
interface Callback : Plugin {
|
||||||
|
fun openRoomNotificationSettings(roomId: RoomId)
|
||||||
|
}
|
||||||
|
|
||||||
data class Inputs(
|
data class Inputs(
|
||||||
val isOneToOne: Boolean
|
val isOneToOne: Boolean
|
||||||
) : NodeInputs
|
) : NodeInputs
|
||||||
|
|
||||||
private val inputs = inputs<Inputs>()
|
private val inputs = inputs<Inputs>()
|
||||||
|
private val callbacks = plugins<Callback>()
|
||||||
private val presenter = presenterFactory.create(inputs.isOneToOne)
|
private val presenter = presenterFactory.create(inputs.isOneToOne)
|
||||||
|
|
||||||
|
private fun openRoomNotificationSettings(roomId: RoomId) {
|
||||||
|
callbacks.forEach { it.openRoomNotificationSettings(roomId) }
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
val state = presenter.present()
|
val state = presenter.present()
|
||||||
EditDefaultNotificationSettingView(
|
EditDefaultNotificationSettingView(
|
||||||
state = state,
|
state = state,
|
||||||
|
openRoomNotificationSettings = { openRoomNotificationSettings(it) },
|
||||||
onBackPressed = ::navigateUp,
|
onBackPressed = ::navigateUp,
|
||||||
modifier = modifier
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,9 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
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.RoomNotificationMode
|
||||||
|
|
@ -57,6 +59,8 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
|
||||||
mutableStateOf(null)
|
mutableStateOf(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val changeNotificationSettingAction: MutableState<Async<Unit>> = remember { mutableStateOf(Async.Uninitialized) }
|
||||||
|
|
||||||
val roomsWithUserDefinedMode: MutableState<List<RoomSummary.Filled>> = remember {
|
val roomsWithUserDefinedMode: MutableState<List<RoomSummary.Filled>> = remember {
|
||||||
mutableStateOf(listOf())
|
mutableStateOf(listOf())
|
||||||
}
|
}
|
||||||
|
|
@ -70,7 +74,10 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
|
||||||
|
|
||||||
fun handleEvents(event: EditDefaultNotificationSettingStateEvents) {
|
fun handleEvents(event: EditDefaultNotificationSettingStateEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is EditDefaultNotificationSettingStateEvents.SetNotificationMode -> localCoroutineScope.setDefaultNotificationMode(event.mode)
|
is EditDefaultNotificationSettingStateEvents.SetNotificationMode -> {
|
||||||
|
localCoroutineScope.setDefaultNotificationMode(event.mode, changeNotificationSettingAction)
|
||||||
|
}
|
||||||
|
EditDefaultNotificationSettingStateEvents.ClearError -> changeNotificationSettingAction.value = Async.Uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,6 +85,7 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
|
||||||
isOneToOne = isOneToOne,
|
isOneToOne = isOneToOne,
|
||||||
mode = mode.value,
|
mode = mode.value,
|
||||||
roomsWithUserDefinedMode = roomsWithUserDefinedMode.value,
|
roomsWithUserDefinedMode = roomsWithUserDefinedMode.value,
|
||||||
|
changeNotificationSettingAction = changeNotificationSettingAction.value,
|
||||||
eventSink = ::handleEvents
|
eventSink = ::handleEvents
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -105,9 +113,13 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
|
||||||
.launchIn(this)
|
.launchIn(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.updateRoomsWithUserDefinedMode(summaries: List<RoomSummary>, roomsWithUserDefinedMode: MutableState<List<RoomSummary.Filled>>) = launch {
|
private fun CoroutineScope.updateRoomsWithUserDefinedMode(
|
||||||
val roomWithUserDefinedRules = notificationSettingsService.getRoomsWithUserDefinedRules().getOrThrow().toSet()
|
summaries: List<RoomSummary>,
|
||||||
roomsWithUserDefinedMode.value = summaries
|
roomsWithUserDefinedMode: MutableState<List<RoomSummary.Filled>>
|
||||||
|
) = launch {
|
||||||
|
val roomWithUserDefinedRules: Set<String> = notificationSettingsService.getRoomsWithUserDefinedRules().getOrThrow().toSet()
|
||||||
|
|
||||||
|
val sortedSummaries = summaries
|
||||||
.filterIsInstance<RoomSummary.Filled>()
|
.filterIsInstance<RoomSummary.Filled>()
|
||||||
.filter {
|
.filter {
|
||||||
val room = matrixClient.getRoom(it.details.roomId) ?: return@filter false
|
val room = matrixClient.getRoom(it.details.roomId) ?: return@filter false
|
||||||
|
|
@ -115,12 +127,16 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
// locale sensitive sorting
|
// locale sensitive sorting
|
||||||
.sortedWith(compareBy(Collator.getInstance()){ it.details.name })
|
.sortedWith(compareBy(Collator.getInstance()){ it.details.name })
|
||||||
|
|
||||||
|
roomsWithUserDefinedMode.value = sortedSummaries
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.setDefaultNotificationMode(mode: RoomNotificationMode) = launch {
|
private fun CoroutineScope.setDefaultNotificationMode(mode: RoomNotificationMode, action: MutableState<Async<Unit>>) = launch {
|
||||||
// On modern clients, we don't have different settings for encrypted and non-encrypted rooms (Legacy clients did).
|
suspend {
|
||||||
notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, mode = mode, isOneToOne = isOneToOne)
|
// On modern clients, we don't have different settings for encrypted and non-encrypted rooms (Legacy clients did).
|
||||||
notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = false, mode = mode, isOneToOne = isOneToOne)
|
notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = true, mode = mode, isOneToOne = isOneToOne).getOrThrow()
|
||||||
|
notificationSettingsService.setDefaultRoomNotificationMode(isEncrypted = false, mode = mode, isOneToOne = isOneToOne).getOrThrow()
|
||||||
|
}.runCatchingUpdatingState(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package io.element.android.features.preferences.impl.notifications.edit
|
package io.element.android.features.preferences.impl.notifications.edit
|
||||||
|
|
||||||
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
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.RoomSummary
|
||||||
|
|
||||||
|
|
@ -23,5 +24,6 @@ data class EditDefaultNotificationSettingState(
|
||||||
val isOneToOne: Boolean,
|
val isOneToOne: Boolean,
|
||||||
val mode: RoomNotificationMode?,
|
val mode: RoomNotificationMode?,
|
||||||
val roomsWithUserDefinedMode: List<RoomSummary.Filled>,
|
val roomsWithUserDefinedMode: List<RoomSummary.Filled>,
|
||||||
|
val changeNotificationSettingAction: Async<Unit>,
|
||||||
val eventSink: (EditDefaultNotificationSettingStateEvents) -> Unit,
|
val eventSink: (EditDefaultNotificationSettingStateEvents) -> Unit,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -20,4 +20,5 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
|
||||||
sealed interface EditDefaultNotificationSettingStateEvents {
|
sealed interface EditDefaultNotificationSettingStateEvents {
|
||||||
data class SetNotificationMode(val mode: RoomNotificationMode): EditDefaultNotificationSettingStateEvents
|
data class SetNotificationMode(val mode: RoomNotificationMode): EditDefaultNotificationSettingStateEvents
|
||||||
|
data object ClearError: EditDefaultNotificationSettingStateEvents
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,19 +17,17 @@
|
||||||
package io.element.android.features.preferences.impl.notifications.edit
|
package io.element.android.features.preferences.impl.notifications.edit
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
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.foundation.selection.selectableGroup
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import io.element.android.features.preferences.impl.notifications.NotificationSettingsState
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.features.preferences.impl.notifications.NotificationSettingsStateProvider
|
import io.element.android.libraries.designsystem.components.ProgressDialog
|
||||||
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.Avatar
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
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.avatar.AvatarSize
|
||||||
|
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
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.PreferenceCategory
|
||||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceView
|
import io.element.android.libraries.designsystem.components.preferences.PreferenceView
|
||||||
|
|
@ -37,6 +35,7 @@ import io.element.android.libraries.designsystem.preview.DayNightPreviews
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
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.ListItem
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
|
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.room.RoomNotificationMode
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
|
|
@ -47,11 +46,12 @@ import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
@Composable
|
@Composable
|
||||||
fun EditDefaultNotificationSettingView(
|
fun EditDefaultNotificationSettingView(
|
||||||
state: EditDefaultNotificationSettingState,
|
state: EditDefaultNotificationSettingState,
|
||||||
|
openRoomNotificationSettings:(roomId: RoomId) -> Unit,
|
||||||
onBackPressed: () -> Unit,
|
onBackPressed: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val title = if(state.isOneToOne) {
|
val title = if (state.isOneToOne) {
|
||||||
CommonStrings.screen_notification_settings_direct_chats
|
CommonStrings.screen_notification_settings_direct_chats
|
||||||
} else {
|
} else {
|
||||||
CommonStrings.screen_notification_settings_group_chats
|
CommonStrings.screen_notification_settings_group_chats
|
||||||
|
|
@ -65,7 +65,7 @@ fun EditDefaultNotificationSettingView(
|
||||||
// Only ALL_MESSAGES and MENTIONS_AND_KEYWORDS_ONLY are valid global defaults.
|
// Only ALL_MESSAGES and MENTIONS_AND_KEYWORDS_ONLY are valid global defaults.
|
||||||
val validModes = listOf(RoomNotificationMode.ALL_MESSAGES, RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
|
val validModes = listOf(RoomNotificationMode.ALL_MESSAGES, RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY)
|
||||||
|
|
||||||
val categoryTitle = if(state.isOneToOne) {
|
val categoryTitle = if (state.isOneToOne) {
|
||||||
CommonStrings.screen_notification_settings_edit_screen_direct_section_header
|
CommonStrings.screen_notification_settings_edit_screen_direct_section_header
|
||||||
} else {
|
} else {
|
||||||
CommonStrings.screen_notification_settings_edit_screen_group_section_header
|
CommonStrings.screen_notification_settings_edit_screen_group_section_header
|
||||||
|
|
@ -84,46 +84,64 @@ fun EditDefaultNotificationSettingView(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(state.roomsWithUserDefinedMode.isNotEmpty()) {
|
if (state.roomsWithUserDefinedMode.isNotEmpty()) {
|
||||||
PreferenceCategory(title = stringResource(id = CommonStrings.screen_notification_settings_edit_custom_settings_section_title)) {
|
PreferenceCategory(title = stringResource(id = CommonStrings.screen_notification_settings_edit_custom_settings_section_title)) {
|
||||||
LazyColumn {
|
state.roomsWithUserDefinedMode.forEach { summary ->
|
||||||
items(state.roomsWithUserDefinedMode) { summary ->
|
val subtitle = when (summary.details.notificationMode) {
|
||||||
val subtitle = when (summary.details.notificationMode) {
|
RoomNotificationMode.ALL_MESSAGES -> stringResource(id = CommonStrings.screen_notification_settings_edit_mode_all_messages)
|
||||||
RoomNotificationMode.ALL_MESSAGES -> stringResource(id = CommonStrings.screen_notification_settings_edit_mode_all_messages)
|
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> {
|
||||||
RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> stringResource(id = CommonStrings.screen_notification_settings_edit_mode_mentions_and_keywords)
|
stringResource(id = CommonStrings.screen_notification_settings_edit_mode_mentions_and_keywords)
|
||||||
RoomNotificationMode.MUTE -> stringResource(id = CommonStrings.common_mute)
|
|
||||||
null -> ""
|
|
||||||
}
|
}
|
||||||
val avatarData = AvatarData(
|
RoomNotificationMode.MUTE -> stringResource(id = CommonStrings.common_mute)
|
||||||
id = summary.identifier(),
|
null -> ""
|
||||||
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)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
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)
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
openRoomNotificationSettings(summary.details.roomId)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
when (state.changeNotificationSettingAction) {
|
||||||
|
is Async.Loading -> {
|
||||||
|
ProgressDialog()
|
||||||
|
}
|
||||||
|
is Async.Failure -> {
|
||||||
|
ErrorDialog(
|
||||||
|
title = stringResource(CommonStrings.dialog_title_error),
|
||||||
|
content = stringResource(CommonStrings.screen_notification_settings_edit_failed_updating_default_mode),
|
||||||
|
onDismiss = { state.eventSink(EditDefaultNotificationSettingStateEvents.ClearError) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@DayNightPreviews
|
@DayNightPreviews
|
||||||
@Composable
|
@Composable
|
||||||
internal fun EditDefaultNotificationSettingViewPreview(@PreviewParameter(EditDefaultNotificationSettingsStateProvider::class) state: EditDefaultNotificationSettingState) = ElementPreview {
|
internal fun EditDefaultNotificationSettingViewPreview(
|
||||||
|
@PreviewParameter(EditDefaultNotificationSettingsStateProvider::class) state: EditDefaultNotificationSettingState
|
||||||
|
) = ElementPreview {
|
||||||
EditDefaultNotificationSettingView(
|
EditDefaultNotificationSettingView(
|
||||||
state = state,
|
state = state,
|
||||||
|
openRoomNotificationSettings = {},
|
||||||
onBackPressed = {},
|
onBackPressed = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package io.element.android.features.preferences.impl.notifications.edit
|
package io.element.android.features.preferences.impl.notifications.edit
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
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.room.RoomNotificationMode
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
|
|
@ -33,6 +34,7 @@ fun anEditDefaultNotificationSettingsState() = EditDefaultNotificationSettingSta
|
||||||
isOneToOne = false,
|
isOneToOne = false,
|
||||||
mode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
|
mode = RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY,
|
||||||
roomsWithUserDefinedMode = listOf(aRoomSummary()),
|
roomsWithUserDefinedMode = listOf(aRoomSummary()),
|
||||||
|
changeNotificationSettingAction = Async.Uninitialized,
|
||||||
eventSink = {}
|
eventSink = {}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,9 @@ interface RoomDetailsEntryPoint : FeatureEntryPoint {
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class RoomMemberDetails(val roomMemberId: UserId) : InitialTarget
|
data class RoomMemberDetails(val roomMemberId: UserId) : InitialTarget
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data object RoomNotificationSettings : InitialTarget
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Inputs(val initialElement: InitialTarget) : NodeInputs
|
data class Inputs(val initialElement: InitialTarget) : NodeInputs
|
||||||
|
|
|
||||||
|
|
@ -42,4 +42,5 @@ class DefaultRoomDetailsEntryPoint @Inject constructor() : RoomDetailsEntryPoint
|
||||||
internal fun InitialTarget.toNavTarget() = when (this) {
|
internal fun InitialTarget.toNavTarget() = when (this) {
|
||||||
is InitialTarget.RoomDetails -> NavTarget.RoomDetails
|
is InitialTarget.RoomDetails -> NavTarget.RoomDetails
|
||||||
is InitialTarget.RoomMemberDetails -> NavTarget.RoomMemberDetails(roomMemberId)
|
is InitialTarget.RoomMemberDetails -> NavTarget.RoomMemberDetails(roomMemberId)
|
||||||
|
is InitialTarget.RoomNotificationSettings -> NavTarget.RoomNotificationSettings(showUserDefinedSettingStyle = true)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,13 @@ class RoomDetailsFlowNode @AssistedInject constructor(
|
||||||
data object InviteMembers : NavTarget
|
data object InviteMembers : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
object RoomNotificationSettings : NavTarget
|
data class RoomNotificationSettings(
|
||||||
|
/**
|
||||||
|
* When presented from oursite the context of the room, the rooms settings UI is different.
|
||||||
|
* Figma designs: https://www.figma.com/file/0MMNu7cTOzLOlWb7ctTkv3/Element-X?type=design&node-id=5199-198932&mode=design&t=fTTvpuxYFjewYQOe-0
|
||||||
|
*/
|
||||||
|
val showUserDefinedSettingStyle: Boolean
|
||||||
|
) : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class RoomMemberDetails(val roomMemberId: UserId) : NavTarget
|
data class RoomMemberDetails(val roomMemberId: UserId) : NavTarget
|
||||||
|
|
@ -91,7 +97,7 @@ class RoomDetailsFlowNode @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openRoomNotificationSettings() {
|
override fun openRoomNotificationSettings() {
|
||||||
backstack.push(NavTarget.RoomNotificationSettings)
|
backstack.push(NavTarget.RoomNotificationSettings(showUserDefinedSettingStyle = false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
createNode<RoomDetailsNode>(buildContext, listOf(roomDetailsCallback))
|
createNode<RoomDetailsNode>(buildContext, listOf(roomDetailsCallback))
|
||||||
|
|
@ -118,8 +124,9 @@ class RoomDetailsFlowNode @AssistedInject constructor(
|
||||||
createNode<RoomInviteMembersNode>(buildContext)
|
createNode<RoomInviteMembersNode>(buildContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
NavTarget.RoomNotificationSettings -> {
|
is NavTarget.RoomNotificationSettings -> {
|
||||||
createNode<RoomNotificationSettingsNode>(buildContext)
|
val plugins = listOf(RoomNotificationSettingsNode.RoomNotificationSettingInput(navTarget.showUserDefinedSettingStyle))
|
||||||
|
createNode<RoomNotificationSettingsNode>(buildContext, plugins)
|
||||||
}
|
}
|
||||||
|
|
||||||
is NavTarget.RoomMemberDetails -> {
|
is NavTarget.RoomMemberDetails -> {
|
||||||
|
|
|
||||||
|
|
@ -21,4 +21,6 @@ import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
sealed interface RoomNotificationSettingsEvents {
|
sealed interface RoomNotificationSettingsEvents {
|
||||||
data class RoomNotificationModeChanged(val mode: RoomNotificationMode) : RoomNotificationSettingsEvents
|
data class RoomNotificationModeChanged(val mode: RoomNotificationMode) : RoomNotificationSettingsEvents
|
||||||
data class SetNotificationMode(val isDefault: Boolean): RoomNotificationSettingsEvents
|
data class SetNotificationMode(val isDefault: Boolean): RoomNotificationSettingsEvents
|
||||||
|
data object DeleteCustomNotification: RoomNotificationSettingsEvents
|
||||||
|
data object ClearError: RoomNotificationSettingsEvents
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import im.vector.app.features.analytics.plan.MobileScreen
|
import im.vector.app.features.analytics.plan.MobileScreen
|
||||||
import io.element.android.anvilannotations.ContributesNode
|
import io.element.android.anvilannotations.ContributesNode
|
||||||
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
|
import io.element.android.libraries.architecture.inputs
|
||||||
import io.element.android.libraries.di.RoomScope
|
import io.element.android.libraries.di.RoomScope
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
|
|
||||||
|
|
@ -37,6 +39,12 @@ class RoomNotificationSettingsNode @AssistedInject constructor(
|
||||||
private val analyticsService: AnalyticsService,
|
private val analyticsService: AnalyticsService,
|
||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
|
|
||||||
|
data class RoomNotificationSettingInput(
|
||||||
|
val showUserDefinedSettingStyle: Boolean
|
||||||
|
) : NodeInputs
|
||||||
|
|
||||||
|
private val inputs = inputs<RoomNotificationSettingInput>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
lifecycle.subscribe(
|
lifecycle.subscribe(
|
||||||
onResume = {
|
onResume = {
|
||||||
|
|
@ -48,10 +56,18 @@ class RoomNotificationSettingsNode @AssistedInject constructor(
|
||||||
@Composable
|
@Composable
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
val state = presenter.present()
|
val state = presenter.present()
|
||||||
RoomNotificationSettingsView(
|
if(inputs.showUserDefinedSettingStyle) {
|
||||||
state = state,
|
UserDefinedRoomNotificationSettingsView(
|
||||||
modifier = modifier,
|
state = state,
|
||||||
onBackPressed = this::navigateUp,
|
modifier = modifier,
|
||||||
)
|
onBackPressed = this::navigateUp,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
RoomNotificationSettingsView(
|
||||||
|
state = state,
|
||||||
|
modifier = modifier,
|
||||||
|
onBackPressed = this::navigateUp,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,12 @@ import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
|
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
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.RoomNotificationMode
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
|
@ -42,16 +45,18 @@ class RoomNotificationSettingsPresenter @Inject constructor(
|
||||||
private val room: MatrixRoom,
|
private val room: MatrixRoom,
|
||||||
private val notificationSettingsService: NotificationSettingsService,
|
private val notificationSettingsService: NotificationSettingsService,
|
||||||
) : Presenter<RoomNotificationSettingsState> {
|
) : Presenter<RoomNotificationSettingsState> {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): RoomNotificationSettingsState {
|
override fun present(): RoomNotificationSettingsState {
|
||||||
val defaultRoomNotificationMode: MutableState<RoomNotificationMode?> = rememberSaveable {
|
val defaultRoomNotificationMode: MutableState<RoomNotificationMode?> = rememberSaveable {
|
||||||
mutableStateOf(null)
|
mutableStateOf(null)
|
||||||
}
|
}
|
||||||
val localCoroutineScope = rememberCoroutineScope()
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
|
val changeNotificationSettingAction: MutableState<Async<Unit>> = remember { mutableStateOf(Async.Uninitialized) }
|
||||||
|
val deleteCustomNotificationSettingAction: MutableState<Async<Unit>> = remember { mutableStateOf(Async.Uninitialized) }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
getDefaultRoomNotificationMode(defaultRoomNotificationMode)
|
getDefaultRoomNotificationMode(defaultRoomNotificationMode)
|
||||||
|
room.updateRoomNotificationSettings()
|
||||||
observeNotificationSettings()
|
observeNotificationSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,23 +65,32 @@ class RoomNotificationSettingsPresenter @Inject constructor(
|
||||||
fun handleEvents(event: RoomNotificationSettingsEvents) {
|
fun handleEvents(event: RoomNotificationSettingsEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is RoomNotificationSettingsEvents.RoomNotificationModeChanged -> {
|
is RoomNotificationSettingsEvents.RoomNotificationModeChanged -> {
|
||||||
localCoroutineScope.setRoomNotificationMode(event.mode)
|
localCoroutineScope.setRoomNotificationMode(event.mode, changeNotificationSettingAction)
|
||||||
}
|
}
|
||||||
is RoomNotificationSettingsEvents.SetNotificationMode -> {
|
is RoomNotificationSettingsEvents.SetNotificationMode -> {
|
||||||
if (event.isDefault) {
|
if (event.isDefault) {
|
||||||
localCoroutineScope.restoreDefaultRoomNotificationMode()
|
localCoroutineScope.restoreDefaultRoomNotificationMode(changeNotificationSettingAction)
|
||||||
} else {
|
} else {
|
||||||
defaultRoomNotificationMode.value?.let {
|
defaultRoomNotificationMode.value?.let {
|
||||||
localCoroutineScope.setRoomNotificationMode(it)
|
localCoroutineScope.setRoomNotificationMode(it, changeNotificationSettingAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is RoomNotificationSettingsEvents.DeleteCustomNotification -> {
|
||||||
|
localCoroutineScope.restoreDefaultRoomNotificationMode(deleteCustomNotificationSettingAction)
|
||||||
|
}
|
||||||
|
RoomNotificationSettingsEvents.ClearError -> {
|
||||||
|
changeNotificationSettingAction.value = Async.Uninitialized
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return RoomNotificationSettingsState(
|
return RoomNotificationSettingsState(
|
||||||
|
roomName = room.displayName,
|
||||||
roomNotificationSettings = roomNotificationSettingsState.roomNotificationSettings(),
|
roomNotificationSettings = roomNotificationSettingsState.roomNotificationSettings(),
|
||||||
defaultRoomNotificationMode = defaultRoomNotificationMode.value,
|
defaultRoomNotificationMode = defaultRoomNotificationMode.value,
|
||||||
|
changeNotificationSettingAction = changeNotificationSettingAction.value,
|
||||||
|
deleteCustomNotificationSettingAction = deleteCustomNotificationSettingAction.value,
|
||||||
eventSink = ::handleEvents,
|
eventSink = ::handleEvents,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -98,11 +112,15 @@ class RoomNotificationSettingsPresenter @Inject constructor(
|
||||||
).getOrThrow()
|
).getOrThrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.setRoomNotificationMode(mode: RoomNotificationMode) = launch {
|
private fun CoroutineScope.setRoomNotificationMode(mode: RoomNotificationMode, action: MutableState<Async<Unit>>) = launch {
|
||||||
notificationSettingsService.setRoomNotificationMode(room.roomId, mode)
|
suspend {
|
||||||
|
notificationSettingsService.setRoomNotificationMode(room.roomId, mode).getOrThrow()
|
||||||
|
}.runCatchingUpdatingState(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.restoreDefaultRoomNotificationMode() = launch {
|
private fun CoroutineScope.restoreDefaultRoomNotificationMode(action: MutableState<Async<Unit>>) = launch {
|
||||||
notificationSettingsService.restoreDefaultRoomNotificationMode(room.roomId)
|
suspend {
|
||||||
|
notificationSettingsService.restoreDefaultRoomNotificationMode(room.roomId).getOrThrow()
|
||||||
|
}.runCatchingUpdatingState(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,15 @@
|
||||||
|
|
||||||
package io.element.android.features.roomdetails.impl.notificationsettings
|
package io.element.android.features.roomdetails.impl.notificationsettings
|
||||||
|
|
||||||
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
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.room.RoomNotificationSettings
|
||||||
|
|
||||||
data class RoomNotificationSettingsState(
|
data class RoomNotificationSettingsState(
|
||||||
|
val roomName: String,
|
||||||
val roomNotificationSettings: RoomNotificationSettings?,
|
val roomNotificationSettings: RoomNotificationSettings?,
|
||||||
val defaultRoomNotificationMode: RoomNotificationMode?,
|
val defaultRoomNotificationMode: RoomNotificationMode?,
|
||||||
|
val changeNotificationSettingAction: Async<Unit>,
|
||||||
|
val deleteCustomNotificationSettingAction: Async<Unit>,
|
||||||
val eventSink: (RoomNotificationSettingsEvents) -> Unit
|
val eventSink: (RoomNotificationSettingsEvents) -> Unit
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package io.element.android.features.roomdetails.impl.notificationsettings
|
package io.element.android.features.roomdetails.impl.notificationsettings
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
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.room.RoomNotificationSettings
|
||||||
|
|
||||||
|
|
@ -24,10 +25,13 @@ internal class RoomNotificationSettingsStateProvider : PreviewParameterProvider<
|
||||||
override val values: Sequence<RoomNotificationSettingsState>
|
override val values: Sequence<RoomNotificationSettingsState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
RoomNotificationSettingsState(
|
RoomNotificationSettingsState(
|
||||||
|
roomName = "Room 1",
|
||||||
RoomNotificationSettings(
|
RoomNotificationSettings(
|
||||||
mode = RoomNotificationMode.MUTE,
|
mode = RoomNotificationMode.MUTE,
|
||||||
isDefault = true),
|
isDefault = true),
|
||||||
RoomNotificationMode.ALL_MESSAGES,
|
RoomNotificationMode.ALL_MESSAGES,
|
||||||
|
changeNotificationSettingAction = Async.Uninitialized,
|
||||||
|
deleteCustomNotificationSettingAction = Async.Uninitialized,
|
||||||
eventSink = { },
|
eventSink = { },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ package io.element.android.features.roomdetails.impl.notificationsettings
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|
||||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
|
@ -30,8 +29,11 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.features.roomdetails.impl.R
|
import io.element.android.features.roomdetails.impl.R
|
||||||
|
import io.element.android.libraries.architecture.Async
|
||||||
import io.element.android.libraries.core.bool.orTrue
|
import io.element.android.libraries.core.bool.orTrue
|
||||||
|
import io.element.android.libraries.designsystem.components.ProgressDialog
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
|
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
|
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.PreferenceSwitch
|
||||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
|
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
|
||||||
|
|
@ -45,7 +47,6 @@ 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
|
||||||
|
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RoomNotificationSettingsView(
|
fun RoomNotificationSettingsView(
|
||||||
state: RoomNotificationSettingsState,
|
state: RoomNotificationSettingsState,
|
||||||
|
|
@ -74,7 +75,6 @@ fun RoomNotificationSettingsView(
|
||||||
null -> ""
|
null -> ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PreferenceCategory(title = stringResource(id = R.string.screen_room_notification_settings_custom_settings_title)) {
|
PreferenceCategory(title = stringResource(id = R.string.screen_room_notification_settings_custom_settings_title)) {
|
||||||
PreferenceSwitch(
|
PreferenceSwitch(
|
||||||
isChecked = state.roomNotificationSettings?.isDefault.orTrue(),
|
isChecked = state.roomNotificationSettings?.isDefault.orTrue(),
|
||||||
|
|
@ -102,6 +102,16 @@ fun RoomNotificationSettingsView(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when (state.changeNotificationSettingAction) {
|
||||||
|
is Async.Loading -> {
|
||||||
|
ProgressDialog()
|
||||||
|
}
|
||||||
|
is Async.Failure -> {
|
||||||
|
ShowChangeNotificationSettingError(state)
|
||||||
|
}
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -144,6 +154,15 @@ fun RoomNotificationSettingsOptions(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ShowChangeNotificationSettingError(state: RoomNotificationSettingsState) {
|
||||||
|
ErrorDialog(
|
||||||
|
title = stringResource(CommonStrings.dialog_title_error),
|
||||||
|
content = stringResource(CommonStrings.screen_notification_settings_edit_failed_updating_default_mode),
|
||||||
|
onDismiss = { state.eventSink(RoomNotificationSettingsEvents.ClearError) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@DayNightPreviews
|
@DayNightPreviews
|
||||||
@Composable
|
@Composable
|
||||||
internal fun RoomNotificationSettingsPreview(
|
internal fun RoomNotificationSettingsPreview(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* 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.consumeWindowInsets
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.res.vectorResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.element.android.features.roomdetails.impl.R
|
||||||
|
import io.element.android.libraries.architecture.Async
|
||||||
|
import io.element.android.libraries.designsystem.VectorIcons
|
||||||
|
import io.element.android.libraries.designsystem.components.ProgressDialog
|
||||||
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
|
import io.element.android.libraries.designsystem.components.preferences.PreferenceText
|
||||||
|
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.TopAppBar
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun UserDefinedRoomNotificationSettingsView(
|
||||||
|
state: RoomNotificationSettingsState,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onBackPressed: () -> Unit = {},
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
|
modifier = modifier,
|
||||||
|
topBar = {
|
||||||
|
UserDefinedRoomNotificationSettingsTopBar(
|
||||||
|
roomName = state.roomName,
|
||||||
|
onBackPressed = { onBackPressed() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { padding ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(padding)
|
||||||
|
.consumeWindowInsets(padding),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
) {
|
||||||
|
if (state.roomNotificationSettings != null) {
|
||||||
|
RoomNotificationSettingsOptions(
|
||||||
|
selected = state.roomNotificationSettings.mode,
|
||||||
|
enabled = !state.roomNotificationSettings.isDefault,
|
||||||
|
onOptionSelected = {
|
||||||
|
state.eventSink(RoomNotificationSettingsEvents.RoomNotificationModeChanged(it.mode))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
PreferenceText(
|
||||||
|
title = stringResource(R.string.screen_room_notification_settings_edit_remove_setting),
|
||||||
|
icon = ImageVector.vectorResource(VectorIcons.Delete),
|
||||||
|
tintColor = MaterialTheme.colorScheme.error,
|
||||||
|
onClick = {
|
||||||
|
state.eventSink(RoomNotificationSettingsEvents.DeleteCustomNotification)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
when (state.changeNotificationSettingAction) {
|
||||||
|
is Async.Loading -> {
|
||||||
|
ProgressDialog()
|
||||||
|
}
|
||||||
|
is Async.Failure -> {
|
||||||
|
ShowChangeNotificationSettingError(state)
|
||||||
|
}
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
when (state.deleteCustomNotificationSettingAction) {
|
||||||
|
is Async.Loading -> {
|
||||||
|
ProgressDialog()
|
||||||
|
}
|
||||||
|
is Async.Failure -> {
|
||||||
|
ShowChangeNotificationSettingError(state)
|
||||||
|
}
|
||||||
|
is Async.Success -> {
|
||||||
|
LaunchedEffect(state.deleteCustomNotificationSettingAction) {
|
||||||
|
onBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun UserDefinedRoomNotificationSettingsTopBar(
|
||||||
|
roomName: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onBackPressed: () -> Unit = {},
|
||||||
|
) {
|
||||||
|
TopAppBar(
|
||||||
|
modifier = modifier,
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = roomName,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
navigationIcon = { BackButton(onClick = onBackPressed) },
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue