Room navigation : refactor Invites so we can use it in other places
This commit is contained in:
parent
28361be6e8
commit
5b8690d32e
14 changed files with 463 additions and 165 deletions
|
|
@ -21,6 +21,7 @@ import com.bumble.appyx.core.node.Node
|
||||||
import com.bumble.appyx.core.plugin.Plugin
|
import com.bumble.appyx.core.plugin.Plugin
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
import io.element.android.features.invite.api.InviteListEntryPoint
|
import io.element.android.features.invite.api.InviteListEntryPoint
|
||||||
|
import io.element.android.features.invite.impl.invitelist.InviteListNode
|
||||||
import io.element.android.libraries.architecture.createNode
|
import io.element.android.libraries.architecture.createNode
|
||||||
import io.element.android.libraries.di.AppScope
|
import io.element.android.libraries.di.AppScope
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023 New Vector Ltd
|
* Copyright (c) 2024 New Vector Ltd
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -14,17 +14,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.invite.impl
|
package io.element.android.features.invite.impl.invitelist
|
||||||
|
|
||||||
import io.element.android.features.invite.impl.model.InviteListInviteSummary
|
import io.element.android.features.invite.impl.model.InviteListInviteSummary
|
||||||
|
|
||||||
sealed interface InviteListEvents {
|
sealed interface InviteListEvents {
|
||||||
data class AcceptInvite(val invite: InviteListInviteSummary) : InviteListEvents
|
data class AcceptInvite(val invite: InviteListInviteSummary) : InviteListEvents
|
||||||
data class DeclineInvite(val invite: InviteListInviteSummary) : InviteListEvents
|
data class DeclineInvite(val invite: InviteListInviteSummary) : InviteListEvents
|
||||||
|
|
||||||
data object ConfirmDeclineInvite : InviteListEvents
|
|
||||||
data object CancelDeclineInvite : InviteListEvents
|
|
||||||
|
|
||||||
data object DismissAcceptError : InviteListEvents
|
|
||||||
data object DismissDeclineError : InviteListEvents
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023 New Vector Ltd
|
* Copyright (c) 2024 New Vector Ltd
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.invite.impl
|
package io.element.android.features.invite.impl.invitelist
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -50,6 +50,7 @@ class InviteListNode @AssistedInject constructor(
|
||||||
state = state,
|
state = state,
|
||||||
onBackClicked = ::onBackClicked,
|
onBackClicked = ::onBackClicked,
|
||||||
onInviteAccepted = ::onInviteAccepted,
|
onInviteAccepted = ::onInviteAccepted,
|
||||||
|
onInviteDeclined = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023 New Vector Ltd
|
* Copyright (c) 2024 New Vector Ltd
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -14,43 +14,35 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.invite.impl
|
package io.element.android.features.invite.impl.invitelist
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
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.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import im.vector.app.features.analytics.plan.JoinedRoom
|
|
||||||
import io.element.android.features.invite.api.SeenInvitesStore
|
import io.element.android.features.invite.api.SeenInvitesStore
|
||||||
import io.element.android.features.invite.impl.model.InviteListInviteSummary
|
import io.element.android.features.invite.impl.model.InviteListInviteSummary
|
||||||
import io.element.android.features.invite.impl.model.InviteSender
|
import io.element.android.features.invite.impl.model.InviteSender
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.features.invite.impl.response.AcceptDeclineInviteEvents
|
||||||
|
import io.element.android.features.invite.impl.response.AcceptDeclineInvitePresenter
|
||||||
|
import io.element.android.features.invite.impl.response.InviteData
|
||||||
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.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.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
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.roomlist.RoomSummary
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
|
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
|
||||||
import io.element.android.services.analytics.api.extensions.toAnalyticsJoinedRoom
|
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class InviteListPresenter @Inject constructor(
|
class InviteListPresenter @Inject constructor(
|
||||||
private val client: MatrixClient,
|
private val client: MatrixClient,
|
||||||
private val store: SeenInvitesStore,
|
private val store: SeenInvitesStore,
|
||||||
private val analyticsService: AnalyticsService,
|
private val acceptDeclineInvitePresenter: AcceptDeclineInvitePresenter,
|
||||||
private val notificationDrawerManager: NotificationDrawerManager,
|
|
||||||
) : Presenter<InviteListState> {
|
) : Presenter<InviteListState> {
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): InviteListState {
|
override fun present(): InviteListState {
|
||||||
|
|
@ -75,40 +67,20 @@ class InviteListPresenter @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val localCoroutineScope = rememberCoroutineScope()
|
val acceptDeclineInviteState = acceptDeclineInvitePresenter.present()
|
||||||
val acceptedAction: MutableState<AsyncData<RoomId>> = remember { mutableStateOf(AsyncData.Uninitialized) }
|
|
||||||
val declinedAction: MutableState<AsyncData<Unit>> = remember { mutableStateOf(AsyncData.Uninitialized) }
|
|
||||||
val decliningInvite: MutableState<InviteListInviteSummary?> = remember { mutableStateOf(null) }
|
|
||||||
|
|
||||||
fun handleEvent(event: InviteListEvents) {
|
fun handleEvent(event: InviteListEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is InviteListEvents.AcceptInvite -> {
|
is InviteListEvents.AcceptInvite -> {
|
||||||
acceptedAction.value = AsyncData.Uninitialized
|
acceptDeclineInviteState.eventSink(
|
||||||
localCoroutineScope.acceptInvite(event.invite.roomId, acceptedAction)
|
AcceptDeclineInviteEvents.AcceptInvite(event.invite.toInviteData())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is InviteListEvents.DeclineInvite -> {
|
is InviteListEvents.DeclineInvite -> {
|
||||||
decliningInvite.value = event.invite
|
acceptDeclineInviteState.eventSink(
|
||||||
}
|
AcceptDeclineInviteEvents.DeclineInvite(event.invite.toInviteData())
|
||||||
|
)
|
||||||
is InviteListEvents.ConfirmDeclineInvite -> {
|
|
||||||
declinedAction.value = AsyncData.Uninitialized
|
|
||||||
decliningInvite.value?.let {
|
|
||||||
localCoroutineScope.declineInvite(it.roomId, declinedAction)
|
|
||||||
}
|
|
||||||
decliningInvite.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
is InviteListEvents.CancelDeclineInvite -> {
|
|
||||||
decliningInvite.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
is InviteListEvents.DismissAcceptError -> {
|
|
||||||
acceptedAction.value = AsyncData.Uninitialized
|
|
||||||
}
|
|
||||||
|
|
||||||
is InviteListEvents.DismissDeclineError -> {
|
|
||||||
declinedAction.value = AsyncData.Uninitialized
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -124,38 +96,11 @@ class InviteListPresenter @Inject constructor(
|
||||||
|
|
||||||
return InviteListState(
|
return InviteListState(
|
||||||
inviteList = inviteList,
|
inviteList = inviteList,
|
||||||
declineConfirmationDialog = decliningInvite.value?.let {
|
acceptDeclineInviteState = acceptDeclineInviteState,
|
||||||
InviteDeclineConfirmationDialog.Visible(
|
|
||||||
isDirect = it.isDirect,
|
|
||||||
name = it.roomName,
|
|
||||||
)
|
|
||||||
} ?: InviteDeclineConfirmationDialog.Hidden,
|
|
||||||
acceptedAction = acceptedAction.value,
|
|
||||||
declinedAction = declinedAction.value,
|
|
||||||
eventSink = ::handleEvent
|
eventSink = ::handleEvent
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.acceptInvite(roomId: RoomId, acceptedAction: MutableState<AsyncData<RoomId>>) = launch {
|
|
||||||
suspend {
|
|
||||||
client.getRoom(roomId)?.use {
|
|
||||||
it.join().getOrThrow()
|
|
||||||
notificationDrawerManager.clearMembershipNotificationForRoom(client.sessionId, roomId, doRender = true)
|
|
||||||
analyticsService.capture(it.toAnalyticsJoinedRoom(JoinedRoom.Trigger.Invite))
|
|
||||||
}
|
|
||||||
roomId
|
|
||||||
}.runCatchingUpdatingState(acceptedAction)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun CoroutineScope.declineInvite(roomId: RoomId, declinedAction: MutableState<AsyncData<Unit>>) = launch {
|
|
||||||
suspend {
|
|
||||||
client.getRoom(roomId)?.use {
|
|
||||||
it.leave().getOrThrow()
|
|
||||||
notificationDrawerManager.clearMembershipNotificationForRoom(client.sessionId, roomId, doRender = true)
|
|
||||||
}.let { }
|
|
||||||
}.runCatchingUpdatingState(declinedAction)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun RoomSummary.Filled.toInviteSummary(seen: Boolean) = details.run {
|
private fun RoomSummary.Filled.toInviteSummary(seen: Boolean) = details.run {
|
||||||
val i = inviter
|
val i = inviter
|
||||||
val avatarData = if (isDirect && i != null) {
|
val avatarData = if (isDirect && i != null) {
|
||||||
|
|
@ -203,4 +148,10 @@ class InviteListPresenter @Inject constructor(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun InviteListInviteSummary.toInviteData() = InviteData(
|
||||||
|
roomId = roomId,
|
||||||
|
roomName = roomName,
|
||||||
|
isDirect = isDirect,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023 New Vector Ltd
|
* Copyright (c) 2024 New Vector Ltd
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -14,24 +14,16 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.invite.impl
|
package io.element.android.features.invite.impl.invitelist
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
|
import io.element.android.features.invite.impl.response.AcceptDeclineInviteState
|
||||||
import io.element.android.features.invite.impl.model.InviteListInviteSummary
|
import io.element.android.features.invite.impl.model.InviteListInviteSummary
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class InviteListState(
|
data class InviteListState(
|
||||||
val inviteList: ImmutableList<InviteListInviteSummary>,
|
val inviteList: ImmutableList<InviteListInviteSummary>,
|
||||||
val declineConfirmationDialog: InviteDeclineConfirmationDialog,
|
val acceptDeclineInviteState: AcceptDeclineInviteState,
|
||||||
val acceptedAction: AsyncData<RoomId>,
|
|
||||||
val declinedAction: AsyncData<Unit>,
|
|
||||||
val eventSink: (InviteListEvents) -> Unit
|
val eventSink: (InviteListEvents) -> Unit
|
||||||
)
|
)
|
||||||
|
|
||||||
sealed interface InviteDeclineConfirmationDialog {
|
|
||||||
data object Hidden : InviteDeclineConfirmationDialog
|
|
||||||
data class Visible(val isDirect: Boolean, val name: String) : InviteDeclineConfirmationDialog
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023 New Vector Ltd
|
* Copyright (c) 2024 New Vector Ltd
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -14,35 +14,40 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.invite.impl
|
package io.element.android.features.invite.impl.invitelist
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.invite.impl.model.InviteListInviteSummary
|
import io.element.android.features.invite.impl.model.InviteListInviteSummary
|
||||||
import io.element.android.features.invite.impl.model.InviteSender
|
import io.element.android.features.invite.impl.model.InviteSender
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.features.invite.impl.response.AcceptDeclineInviteState
|
||||||
|
import io.element.android.features.invite.impl.response.AcceptDeclineInviteStateProvider
|
||||||
|
import io.element.android.features.invite.impl.response.anAcceptDeclineInviteState
|
||||||
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.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
open class InviteListStateProvider : PreviewParameterProvider<InviteListState> {
|
open class InviteListStateProvider : PreviewParameterProvider<InviteListState> {
|
||||||
|
|
||||||
|
private val acceptDeclineInviteStateProvider = AcceptDeclineInviteStateProvider()
|
||||||
|
|
||||||
override val values: Sequence<InviteListState>
|
override val values: Sequence<InviteListState>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
aInviteListState(),
|
anInviteListState(),
|
||||||
aInviteListState().copy(inviteList = persistentListOf()),
|
anInviteListState(inviteList = persistentListOf()),
|
||||||
aInviteListState().copy(declineConfirmationDialog = InviteDeclineConfirmationDialog.Visible(true, "Alice")),
|
) + acceptDeclineInviteStateProvider.values.map { acceptDeclineInviteState ->
|
||||||
aInviteListState().copy(declineConfirmationDialog = InviteDeclineConfirmationDialog.Visible(false, "Some Room")),
|
anInviteListState(acceptDeclineInviteState = acceptDeclineInviteState)
|
||||||
aInviteListState().copy(acceptedAction = AsyncData.Failure(Throwable("Whoops"))),
|
}
|
||||||
aInviteListState().copy(declinedAction = AsyncData.Failure(Throwable("Whoops"))),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun aInviteListState() = InviteListState(
|
internal fun anInviteListState(
|
||||||
inviteList = aInviteListInviteSummaryList(),
|
inviteList: ImmutableList<InviteListInviteSummary> = aInviteListInviteSummaryList(),
|
||||||
declineConfirmationDialog = InviteDeclineConfirmationDialog.Hidden,
|
acceptDeclineInviteState: AcceptDeclineInviteState = anAcceptDeclineInviteState(),
|
||||||
acceptedAction = AsyncData.Uninitialized,
|
eventSink: (InviteListEvents) -> Unit = {}
|
||||||
declinedAction = AsyncData.Uninitialized,
|
) = InviteListState(
|
||||||
eventSink = {},
|
inviteList = inviteList,
|
||||||
|
acceptDeclineInviteState = acceptDeclineInviteState,
|
||||||
|
eventSink = eventSink,
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun aInviteListInviteSummaryList(): ImmutableList<InviteListInviteSummary> {
|
internal fun aInviteListInviteSummaryList(): ImmutableList<InviteListInviteSummary> {
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023 New Vector Ltd
|
* Copyright (c) 2024 New Vector Ltd
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.invite.impl
|
package io.element.android.features.invite.impl.invitelist
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
|
@ -27,20 +27,16 @@ import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.rememberUpdatedState
|
|
||||||
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.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
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.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
|
import io.element.android.features.invite.impl.R
|
||||||
import io.element.android.features.invite.impl.components.InviteSummaryRow
|
import io.element.android.features.invite.impl.components.InviteSummaryRow
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.features.invite.impl.response.AcceptDeclineInviteView
|
||||||
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.ConfirmationDialog
|
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
|
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
|
||||||
|
|
@ -56,61 +52,19 @@ fun InviteListView(
|
||||||
state: InviteListState,
|
state: InviteListState,
|
||||||
onBackClicked: () -> Unit,
|
onBackClicked: () -> Unit,
|
||||||
onInviteAccepted: (RoomId) -> Unit,
|
onInviteAccepted: (RoomId) -> Unit,
|
||||||
|
onInviteDeclined: (RoomId) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
if (state.acceptedAction is AsyncData.Success) {
|
|
||||||
val latestOnInviteAccepted by rememberUpdatedState(onInviteAccepted)
|
|
||||||
LaunchedEffect(state.acceptedAction) {
|
|
||||||
latestOnInviteAccepted(state.acceptedAction.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InviteListContent(
|
InviteListContent(
|
||||||
state = state,
|
state = state,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onBackClicked = onBackClicked,
|
onBackClicked = onBackClicked,
|
||||||
)
|
)
|
||||||
|
AcceptDeclineInviteView(
|
||||||
if (state.declineConfirmationDialog is InviteDeclineConfirmationDialog.Visible) {
|
state = state.acceptDeclineInviteState,
|
||||||
val contentResource = if (state.declineConfirmationDialog.isDirect) {
|
onInviteAccepted = onInviteAccepted,
|
||||||
R.string.screen_invites_decline_direct_chat_message
|
onInviteDeclined = onInviteDeclined,
|
||||||
} else {
|
)
|
||||||
R.string.screen_invites_decline_chat_message
|
|
||||||
}
|
|
||||||
|
|
||||||
val titleResource = if (state.declineConfirmationDialog.isDirect) {
|
|
||||||
R.string.screen_invites_decline_direct_chat_title
|
|
||||||
} else {
|
|
||||||
R.string.screen_invites_decline_chat_title
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfirmationDialog(
|
|
||||||
content = stringResource(contentResource, state.declineConfirmationDialog.name),
|
|
||||||
title = stringResource(titleResource),
|
|
||||||
submitText = stringResource(CommonStrings.action_decline),
|
|
||||||
cancelText = stringResource(CommonStrings.action_cancel),
|
|
||||||
onSubmitClicked = { state.eventSink(InviteListEvents.ConfirmDeclineInvite) },
|
|
||||||
onDismiss = { state.eventSink(InviteListEvents.CancelDeclineInvite) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.acceptedAction is AsyncData.Failure) {
|
|
||||||
ErrorDialog(
|
|
||||||
content = stringResource(CommonStrings.error_unknown),
|
|
||||||
title = stringResource(CommonStrings.common_error),
|
|
||||||
submitText = stringResource(CommonStrings.action_ok),
|
|
||||||
onDismiss = { state.eventSink(InviteListEvents.DismissAcceptError) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.declinedAction is AsyncData.Failure) {
|
|
||||||
ErrorDialog(
|
|
||||||
content = stringResource(CommonStrings.error_unknown),
|
|
||||||
title = stringResource(CommonStrings.common_error),
|
|
||||||
submitText = stringResource(CommonStrings.action_ok),
|
|
||||||
onDismiss = { state.eventSink(InviteListEvents.DismissDeclineError) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
|
@ -138,8 +92,8 @@ private fun InviteListContent(
|
||||||
content = { padding ->
|
content = { padding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(padding)
|
.padding(padding)
|
||||||
.consumeWindowInsets(padding)
|
.consumeWindowInsets(padding)
|
||||||
) {
|
) {
|
||||||
if (state.inviteList.isEmpty()) {
|
if (state.inviteList.isEmpty()) {
|
||||||
Spacer(Modifier.size(80.dp))
|
Spacer(Modifier.size(80.dp))
|
||||||
|
|
@ -181,5 +135,6 @@ internal fun InviteListViewPreview(@PreviewParameter(InviteListStateProvider::cl
|
||||||
state = state,
|
state = state,
|
||||||
onBackClicked = {},
|
onBackClicked = {},
|
||||||
onInviteAccepted = {},
|
onInviteAccepted = {},
|
||||||
|
onInviteDeclined = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 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.invite.impl.response
|
||||||
|
|
||||||
|
sealed interface AcceptDeclineInviteEvents {
|
||||||
|
data class AcceptInvite(val invite: InviteData) : AcceptDeclineInviteEvents
|
||||||
|
data class DeclineInvite(val invite: InviteData) : AcceptDeclineInviteEvents
|
||||||
|
data object ConfirmDeclineInvite : AcceptDeclineInviteEvents
|
||||||
|
data object CancelDeclineInvite : AcceptDeclineInviteEvents
|
||||||
|
data object DismissAcceptError : AcceptDeclineInviteEvents
|
||||||
|
data object DismissDeclineError : AcceptDeclineInviteEvents
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 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.invite.impl.response
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
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 io.element.android.anvilannotations.ContributesNode
|
||||||
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
|
||||||
|
@ContributesNode(SessionScope::class)
|
||||||
|
class AcceptDeclineInviteNode @AssistedInject constructor(
|
||||||
|
@Assisted buildContext: BuildContext,
|
||||||
|
@Assisted plugins: List<Plugin>,
|
||||||
|
private val presenter: AcceptDeclineInvitePresenter,
|
||||||
|
) : Node(buildContext, plugins = plugins) {
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun View(modifier: Modifier) {
|
||||||
|
val state = presenter.present()
|
||||||
|
AcceptDeclineInviteView(
|
||||||
|
state = state,
|
||||||
|
onInviteAccepted = {},
|
||||||
|
onInviteDeclined = {},
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 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.invite.impl.response
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import im.vector.app.features.analytics.plan.JoinedRoom
|
||||||
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
|
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.core.RoomId
|
||||||
|
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
|
||||||
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
|
import io.element.android.services.analytics.api.extensions.toAnalyticsJoinedRoom
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.Optional
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.jvm.optionals.getOrNull
|
||||||
|
|
||||||
|
class AcceptDeclineInvitePresenter @Inject constructor(
|
||||||
|
private val client: MatrixClient,
|
||||||
|
private val analyticsService: AnalyticsService,
|
||||||
|
private val notificationDrawerManager: NotificationDrawerManager,
|
||||||
|
) : Presenter<AcceptDeclineInviteState> {
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun present(): AcceptDeclineInviteState {
|
||||||
|
|
||||||
|
val localCoroutineScope = rememberCoroutineScope()
|
||||||
|
val acceptedAction: MutableState<AsyncAction<RoomId>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||||
|
val declinedAction: MutableState<AsyncAction<RoomId>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||||
|
var currentInvite by remember {
|
||||||
|
mutableStateOf<Optional<InviteData>>(Optional.empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleEvents(event: AcceptDeclineInviteEvents) {
|
||||||
|
when (event) {
|
||||||
|
is AcceptDeclineInviteEvents.AcceptInvite -> {
|
||||||
|
currentInvite = Optional.of(event.invite)
|
||||||
|
localCoroutineScope.acceptInvite(event.invite.roomId, acceptedAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
is AcceptDeclineInviteEvents.DeclineInvite -> {
|
||||||
|
currentInvite = Optional.of(event.invite)
|
||||||
|
declinedAction.value = AsyncAction.Confirming
|
||||||
|
}
|
||||||
|
|
||||||
|
is AcceptDeclineInviteEvents.ConfirmDeclineInvite -> {
|
||||||
|
declinedAction.value = AsyncAction.Uninitialized
|
||||||
|
currentInvite.getOrNull()?.let {
|
||||||
|
localCoroutineScope.declineInvite(it.roomId, declinedAction)
|
||||||
|
}
|
||||||
|
currentInvite = Optional.empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
is AcceptDeclineInviteEvents.CancelDeclineInvite -> {
|
||||||
|
currentInvite = Optional.empty()
|
||||||
|
declinedAction.value = AsyncAction.Uninitialized
|
||||||
|
}
|
||||||
|
|
||||||
|
is AcceptDeclineInviteEvents.DismissAcceptError -> {
|
||||||
|
acceptedAction.value = AsyncAction.Uninitialized
|
||||||
|
}
|
||||||
|
|
||||||
|
is AcceptDeclineInviteEvents.DismissDeclineError -> {
|
||||||
|
declinedAction.value = AsyncAction.Uninitialized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AcceptDeclineInviteState(
|
||||||
|
invite = currentInvite,
|
||||||
|
acceptAction = acceptedAction.value,
|
||||||
|
declineAction = declinedAction.value,
|
||||||
|
eventSink = ::handleEvents
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CoroutineScope.acceptInvite(roomId: RoomId, acceptedAction: MutableState<AsyncAction<RoomId>>) = launch {
|
||||||
|
suspend {
|
||||||
|
client.getRoom(roomId)?.use {
|
||||||
|
it.join().getOrThrow()
|
||||||
|
notificationDrawerManager.clearMembershipNotificationForRoom(client.sessionId, roomId, doRender = true)
|
||||||
|
analyticsService.capture(it.toAnalyticsJoinedRoom(JoinedRoom.Trigger.Invite))
|
||||||
|
}
|
||||||
|
roomId
|
||||||
|
}.runCatchingUpdatingState(acceptedAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CoroutineScope.declineInvite(roomId: RoomId, declinedAction: MutableState<AsyncAction<RoomId>>) = launch {
|
||||||
|
suspend {
|
||||||
|
client.getRoom(roomId)?.use {
|
||||||
|
it.leave().getOrThrow()
|
||||||
|
notificationDrawerManager.clearMembershipNotificationForRoom(client.sessionId, roomId, doRender = true)
|
||||||
|
}
|
||||||
|
roomId
|
||||||
|
}.runCatchingUpdatingState(declinedAction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 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.invite.impl.response
|
||||||
|
|
||||||
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import java.util.Optional
|
||||||
|
|
||||||
|
data class AcceptDeclineInviteState(
|
||||||
|
val invite: Optional<InviteData>,
|
||||||
|
val acceptAction: AsyncAction<RoomId>,
|
||||||
|
val declineAction: AsyncAction<RoomId>,
|
||||||
|
val eventSink: (AcceptDeclineInviteEvents) -> Unit
|
||||||
|
)
|
||||||
|
|
||||||
|
data class InviteData(
|
||||||
|
val roomId: RoomId,
|
||||||
|
val roomName: String,
|
||||||
|
val isDirect: Boolean,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 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.invite.impl.response
|
||||||
|
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import java.util.Optional
|
||||||
|
|
||||||
|
open class AcceptDeclineInviteStateProvider : PreviewParameterProvider<AcceptDeclineInviteState> {
|
||||||
|
override val values: Sequence<AcceptDeclineInviteState>
|
||||||
|
get() = sequenceOf(
|
||||||
|
anAcceptDeclineInviteState(),
|
||||||
|
anAcceptDeclineInviteState(
|
||||||
|
invite = Optional.of(
|
||||||
|
InviteData(RoomId(""), isDirect = true, roomName = "Alice"),
|
||||||
|
),
|
||||||
|
declineAction = AsyncAction.Confirming,
|
||||||
|
),
|
||||||
|
anAcceptDeclineInviteState(
|
||||||
|
invite = Optional.of(
|
||||||
|
InviteData(RoomId(""), isDirect = false, roomName = "Some room"),
|
||||||
|
),
|
||||||
|
declineAction = AsyncAction.Confirming,
|
||||||
|
),
|
||||||
|
anAcceptDeclineInviteState(
|
||||||
|
acceptAction = AsyncAction.Failure(Throwable("Whoops")),
|
||||||
|
),
|
||||||
|
anAcceptDeclineInviteState(
|
||||||
|
declineAction = AsyncAction.Failure(Throwable("Whoops")),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun anAcceptDeclineInviteState(
|
||||||
|
invite: Optional<InviteData> = Optional.empty(),
|
||||||
|
acceptAction: AsyncAction<RoomId> = AsyncAction.Uninitialized,
|
||||||
|
declineAction: AsyncAction<RoomId> = AsyncAction.Uninitialized,
|
||||||
|
eventSink: (AcceptDeclineInviteEvents) -> Unit = {}
|
||||||
|
) = AcceptDeclineInviteState(
|
||||||
|
invite = invite,
|
||||||
|
acceptAction = acceptAction,
|
||||||
|
declineAction = declineAction,
|
||||||
|
eventSink = eventSink,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 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.invite.impl.response
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
|
import io.element.android.features.invite.impl.R
|
||||||
|
import io.element.android.libraries.designsystem.components.async.AsyncActionView
|
||||||
|
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
||||||
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
import kotlin.jvm.optionals.getOrNull
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AcceptDeclineInviteView(
|
||||||
|
state: AcceptDeclineInviteState,
|
||||||
|
onInviteAccepted: (RoomId) -> Unit,
|
||||||
|
onInviteDeclined: (RoomId) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Box(modifier = modifier) {
|
||||||
|
AsyncActionView(
|
||||||
|
async = state.acceptAction,
|
||||||
|
onSuccess = onInviteAccepted,
|
||||||
|
onErrorDismiss = {
|
||||||
|
state.eventSink(AcceptDeclineInviteEvents.DismissAcceptError)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
AsyncActionView(
|
||||||
|
async = state.declineAction,
|
||||||
|
onSuccess = onInviteDeclined,
|
||||||
|
onErrorDismiss = {
|
||||||
|
state.eventSink(AcceptDeclineInviteEvents.DismissDeclineError)
|
||||||
|
},
|
||||||
|
confirmationDialog = {
|
||||||
|
val invite = state.invite.getOrNull()
|
||||||
|
if (invite != null) {
|
||||||
|
DeclineConfirmationDialog(
|
||||||
|
invite = invite,
|
||||||
|
onConfirmClicked = {
|
||||||
|
state.eventSink(AcceptDeclineInviteEvents.ConfirmDeclineInvite)
|
||||||
|
},
|
||||||
|
onDismissClicked = {
|
||||||
|
state.eventSink(AcceptDeclineInviteEvents.CancelDeclineInvite)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun DeclineConfirmationDialog(
|
||||||
|
invite: InviteData,
|
||||||
|
onConfirmClicked: () -> Unit,
|
||||||
|
onDismissClicked: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val contentResource = if (invite.isDirect) {
|
||||||
|
R.string.screen_invites_decline_direct_chat_message
|
||||||
|
} else {
|
||||||
|
R.string.screen_invites_decline_chat_message
|
||||||
|
}
|
||||||
|
|
||||||
|
val titleResource = if (invite.isDirect) {
|
||||||
|
R.string.screen_invites_decline_direct_chat_title
|
||||||
|
} else {
|
||||||
|
R.string.screen_invites_decline_chat_title
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfirmationDialog(
|
||||||
|
modifier = modifier,
|
||||||
|
content = stringResource(contentResource, invite.roomName),
|
||||||
|
title = stringResource(titleResource),
|
||||||
|
submitText = stringResource(CommonStrings.action_decline),
|
||||||
|
cancelText = stringResource(CommonStrings.action_cancel),
|
||||||
|
onSubmitClicked = onConfirmClicked,
|
||||||
|
onDismiss = onDismissClicked,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreviewLightDark
|
||||||
|
@Composable
|
||||||
|
internal fun AcceptDeclineInviteViewLightPreview(@PreviewParameter(AcceptDeclineInviteStateProvider::class) state: AcceptDeclineInviteState) =
|
||||||
|
ElementPreview {
|
||||||
|
AcceptDeclineInviteView(
|
||||||
|
state = state,
|
||||||
|
onInviteAccepted = {},
|
||||||
|
onInviteDeclined = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,9 @@ import app.cash.turbine.TurbineTestContext
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.invite.api.SeenInvitesStore
|
import io.element.android.features.invite.api.SeenInvitesStore
|
||||||
|
import io.element.android.features.invite.impl.invitelist.InviteListEvents
|
||||||
|
import io.element.android.features.invite.impl.invitelist.InviteListPresenter
|
||||||
|
import io.element.android.features.invite.impl.invitelist.InviteListState
|
||||||
import io.element.android.features.invite.test.FakeSeenInvitesStore
|
import io.element.android.features.invite.test.FakeSeenInvitesStore
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue