Message queuing : remove RetrySendMenu classes and usages
This commit is contained in:
parent
ba4abe3de7
commit
ed9420fbac
13 changed files with 1 additions and 593 deletions
|
|
@ -46,7 +46,6 @@ import io.element.android.features.messages.impl.timeline.TimelineState
|
||||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter
|
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter
|
||||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter
|
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter
|
||||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetPresenter
|
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetPresenter
|
||||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuPresenter
|
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent
|
||||||
|
|
@ -106,7 +105,6 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
private val actionListPresenter: ActionListPresenter,
|
private val actionListPresenter: ActionListPresenter,
|
||||||
private val customReactionPresenter: CustomReactionPresenter,
|
private val customReactionPresenter: CustomReactionPresenter,
|
||||||
private val reactionSummaryPresenter: ReactionSummaryPresenter,
|
private val reactionSummaryPresenter: ReactionSummaryPresenter,
|
||||||
private val retrySendMenuPresenter: RetrySendMenuPresenter,
|
|
||||||
private val readReceiptBottomSheetPresenter: ReadReceiptBottomSheetPresenter,
|
private val readReceiptBottomSheetPresenter: ReadReceiptBottomSheetPresenter,
|
||||||
private val networkMonitor: NetworkMonitor,
|
private val networkMonitor: NetworkMonitor,
|
||||||
private val snackbarDispatcher: SnackbarDispatcher,
|
private val snackbarDispatcher: SnackbarDispatcher,
|
||||||
|
|
@ -139,7 +137,6 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
val actionListState = actionListPresenter.present()
|
val actionListState = actionListPresenter.present()
|
||||||
val customReactionState = customReactionPresenter.present()
|
val customReactionState = customReactionPresenter.present()
|
||||||
val reactionSummaryState = reactionSummaryPresenter.present()
|
val reactionSummaryState = reactionSummaryPresenter.present()
|
||||||
val retryState = retrySendMenuPresenter.present()
|
|
||||||
val readReceiptBottomSheetState = readReceiptBottomSheetPresenter.present()
|
val readReceiptBottomSheetState = readReceiptBottomSheetPresenter.present()
|
||||||
|
|
||||||
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||||
|
|
@ -230,7 +227,6 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
actionListState = actionListState,
|
actionListState = actionListState,
|
||||||
customReactionState = customReactionState,
|
customReactionState = customReactionState,
|
||||||
reactionSummaryState = reactionSummaryState,
|
reactionSummaryState = reactionSummaryState,
|
||||||
retrySendMenuState = retryState,
|
|
||||||
readReceiptBottomSheetState = readReceiptBottomSheetState,
|
readReceiptBottomSheetState = readReceiptBottomSheetState,
|
||||||
hasNetworkConnection = networkConnectionStatus == NetworkStatus.Online,
|
hasNetworkConnection = networkConnectionStatus == NetworkStatus.Online,
|
||||||
snackbarMessage = snackbarMessage,
|
snackbarMessage = snackbarMessage,
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import io.element.android.features.messages.impl.timeline.TimelineState
|
||||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState
|
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState
|
||||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState
|
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState
|
||||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState
|
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState
|
||||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState
|
|
||||||
import io.element.android.features.messages.impl.typing.TypingNotificationState
|
import io.element.android.features.messages.impl.typing.TypingNotificationState
|
||||||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
|
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
|
|
@ -47,7 +46,6 @@ data class MessagesState(
|
||||||
val actionListState: ActionListState,
|
val actionListState: ActionListState,
|
||||||
val customReactionState: CustomReactionState,
|
val customReactionState: CustomReactionState,
|
||||||
val reactionSummaryState: ReactionSummaryState,
|
val reactionSummaryState: ReactionSummaryState,
|
||||||
val retrySendMenuState: RetrySendMenuState,
|
|
||||||
val readReceiptBottomSheetState: ReadReceiptBottomSheetState,
|
val readReceiptBottomSheetState: ReadReceiptBottomSheetState,
|
||||||
val hasNetworkConnection: Boolean,
|
val hasNetworkConnection: Boolean,
|
||||||
val snackbarMessage: SnackbarMessage?,
|
val snackbarMessage: SnackbarMessage?,
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,6 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum
|
||||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState
|
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState
|
||||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents
|
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents
|
||||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState
|
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState
|
||||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState
|
|
||||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.aRetrySendMenuState
|
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
||||||
import io.element.android.features.messages.impl.typing.aTypingNotificationState
|
import io.element.android.features.messages.impl.typing.aTypingNotificationState
|
||||||
|
|
@ -110,7 +108,6 @@ fun aMessagesState(
|
||||||
// Render a focused event for an event with sender information displayed
|
// Render a focused event for an event with sender information displayed
|
||||||
focusedEventIndex = 2,
|
focusedEventIndex = 2,
|
||||||
),
|
),
|
||||||
retrySendMenuState: RetrySendMenuState = aRetrySendMenuState(),
|
|
||||||
readReceiptBottomSheetState: ReadReceiptBottomSheetState = aReadReceiptBottomSheetState(),
|
readReceiptBottomSheetState: ReadReceiptBottomSheetState = aReadReceiptBottomSheetState(),
|
||||||
actionListState: ActionListState = anActionListState(),
|
actionListState: ActionListState = anActionListState(),
|
||||||
customReactionState: CustomReactionState = aCustomReactionState(),
|
customReactionState: CustomReactionState = aCustomReactionState(),
|
||||||
|
|
@ -132,7 +129,6 @@ fun aMessagesState(
|
||||||
voiceMessageComposerState = voiceMessageComposerState,
|
voiceMessageComposerState = voiceMessageComposerState,
|
||||||
typingNotificationState = aTypingNotificationState(),
|
typingNotificationState = aTypingNotificationState(),
|
||||||
timelineState = timelineState,
|
timelineState = timelineState,
|
||||||
retrySendMenuState = retrySendMenuState,
|
|
||||||
readReceiptBottomSheetState = readReceiptBottomSheetState,
|
readReceiptBottomSheetState = readReceiptBottomSheetState,
|
||||||
actionListState = actionListState,
|
actionListState = actionListState,
|
||||||
customReactionState = customReactionState,
|
customReactionState = customReactionState,
|
||||||
|
|
|
||||||
|
|
@ -76,8 +76,6 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum
|
||||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryView
|
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryView
|
||||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheet
|
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheet
|
||||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents
|
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents
|
||||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuEvents
|
|
||||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMessageMenu
|
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerEvents
|
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerEvents
|
||||||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessagePermissionRationaleDialog
|
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessagePermissionRationaleDialog
|
||||||
|
|
@ -215,11 +213,7 @@ fun MessagesView(
|
||||||
onMessageLongClick = ::onMessageLongClick,
|
onMessageLongClick = ::onMessageLongClick,
|
||||||
onUserDataClick = onUserDataClick,
|
onUserDataClick = onUserDataClick,
|
||||||
onLinkClick = onLinkClick,
|
onLinkClick = onLinkClick,
|
||||||
onTimestampClick = { event ->
|
onTimestampClick = { /* no-op */ },
|
||||||
if (event.localSendState is LocalEventSendState.SendingFailed) {
|
|
||||||
state.retrySendMenuState.eventSink(RetrySendMenuEvents.EventSelected(event))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onReactionClick = ::onEmojiReactionClick,
|
onReactionClick = ::onEmojiReactionClick,
|
||||||
onReactionLongClick = ::onEmojiReactionLongClick,
|
onReactionLongClick = ::onEmojiReactionLongClick,
|
||||||
onMoreReactionsClick = ::onMoreReactionsClick,
|
onMoreReactionsClick = ::onMoreReactionsClick,
|
||||||
|
|
@ -260,7 +254,6 @@ fun MessagesView(
|
||||||
)
|
)
|
||||||
|
|
||||||
ReactionSummaryView(state = state.reactionSummaryState)
|
ReactionSummaryView(state = state.reactionSummaryState)
|
||||||
RetrySendMessageMenu(state = state.retrySendMenuState)
|
|
||||||
ReadReceiptBottomSheet(
|
ReadReceiptBottomSheet(
|
||||||
state = state.readReceiptBottomSheetState,
|
state = state.readReceiptBottomSheetState,
|
||||||
onUserDataClick = onUserDataClick,
|
onUserDataClick = onUserDataClick,
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,6 @@ private fun ReadReceiptBottomSheetContent(
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ReadReceiptBottomSheetPreview(@PreviewParameter(ReadReceiptBottomSheetStateProvider::class) state: ReadReceiptBottomSheetState) = ElementPreview {
|
internal fun ReadReceiptBottomSheetPreview(@PreviewParameter(ReadReceiptBottomSheetStateProvider::class) state: ReadReceiptBottomSheetState) = ElementPreview {
|
||||||
// TODO restore RetrySendMessageMenuBottomSheet once the issue with bottom sheet not being previewable is fixed
|
|
||||||
Column {
|
Column {
|
||||||
ReadReceiptBottomSheetContent(
|
ReadReceiptBottomSheetContent(
|
||||||
state = state,
|
state = state,
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.messages.impl.timeline.components.retrysendmenu
|
|
||||||
|
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
|
||||||
|
|
||||||
sealed interface RetrySendMenuEvents {
|
|
||||||
data class EventSelected(val event: TimelineItem.Event) : RetrySendMenuEvents
|
|
||||||
data object Retry : RetrySendMenuEvents
|
|
||||||
data object Remove : RetrySendMenuEvents
|
|
||||||
data object Dismiss : RetrySendMenuEvents
|
|
||||||
}
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.messages.impl.timeline.components.retrysendmenu
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
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 io.element.android.features.messages.impl.timeline.model.TimelineItem
|
|
||||||
import io.element.android.libraries.architecture.Presenter
|
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class RetrySendMenuPresenter @Inject constructor(
|
|
||||||
private val room: MatrixRoom,
|
|
||||||
) : Presenter<RetrySendMenuState> {
|
|
||||||
@Composable
|
|
||||||
override fun present(): RetrySendMenuState {
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
var selectedEvent: TimelineItem.Event? by remember { mutableStateOf(null) }
|
|
||||||
|
|
||||||
fun handleEvent(event: RetrySendMenuEvents) {
|
|
||||||
when (event) {
|
|
||||||
is RetrySendMenuEvents.EventSelected -> {
|
|
||||||
selectedEvent = event.event
|
|
||||||
}
|
|
||||||
RetrySendMenuEvents.Retry -> {
|
|
||||||
coroutineScope.launch {
|
|
||||||
selectedEvent?.transactionId?.let { transactionId ->
|
|
||||||
room.retrySendMessage(transactionId)
|
|
||||||
}
|
|
||||||
selectedEvent = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RetrySendMenuEvents.Remove -> {
|
|
||||||
coroutineScope.launch {
|
|
||||||
selectedEvent?.transactionId?.let { transactionId ->
|
|
||||||
room.cancelSend(transactionId)
|
|
||||||
}
|
|
||||||
selectedEvent = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RetrySendMenuEvents.Dismiss -> {
|
|
||||||
selectedEvent = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return RetrySendMenuState(
|
|
||||||
selectedEvent = selectedEvent,
|
|
||||||
eventSink = { handleEvent(it) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.messages.impl.timeline.components.retrysendmenu
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
data class RetrySendMenuState(
|
|
||||||
val selectedEvent: TimelineItem.Event?,
|
|
||||||
val eventSink: (RetrySendMenuEvents) -> Unit,
|
|
||||||
)
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.messages.impl.timeline.components.retrysendmenu
|
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|
||||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
|
||||||
|
|
||||||
class RetrySendMenuStateProvider : PreviewParameterProvider<RetrySendMenuState> {
|
|
||||||
override val values: Sequence<RetrySendMenuState> = sequenceOf(
|
|
||||||
aRetrySendMenuState(),
|
|
||||||
aRetrySendMenuState(event = aTimelineItemEvent()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun aRetrySendMenuState(
|
|
||||||
event: TimelineItem.Event? = null,
|
|
||||||
eventSink: (RetrySendMenuEvents) -> Unit = {},
|
|
||||||
) = RetrySendMenuState(
|
|
||||||
selectedEvent = event,
|
|
||||||
eventSink = eventSink,
|
|
||||||
)
|
|
||||||
|
|
@ -1,159 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.messages.impl.timeline.components.retrysendmenu
|
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.ListItem
|
|
||||||
import androidx.compose.material3.ListItemDefaults
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.SheetState
|
|
||||||
import androidx.compose.material3.rememberModalBottomSheetState
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import io.element.android.compound.theme.ElementTheme
|
|
||||||
import io.element.android.features.messages.impl.R
|
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
internal fun RetrySendMessageMenu(
|
|
||||||
state: RetrySendMenuState,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
val isVisible = state.selectedEvent != null
|
|
||||||
|
|
||||||
fun onDismiss() {
|
|
||||||
state.eventSink(RetrySendMenuEvents.Dismiss)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onRetry() {
|
|
||||||
state.eventSink(RetrySendMenuEvents.Retry)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onRemove() {
|
|
||||||
state.eventSink(RetrySendMenuEvents.Remove)
|
|
||||||
}
|
|
||||||
|
|
||||||
RetrySendMessageMenuBottomSheet(
|
|
||||||
modifier = modifier,
|
|
||||||
isVisible = isVisible,
|
|
||||||
onRetry = ::onRetry,
|
|
||||||
onRemove = ::onRemove,
|
|
||||||
onDismiss = ::onDismiss
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
private fun RetrySendMessageMenuBottomSheet(
|
|
||||||
isVisible: Boolean,
|
|
||||||
onRetry: () -> Unit,
|
|
||||||
onRemove: () -> Unit,
|
|
||||||
onDismiss: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
val sheetState = rememberModalBottomSheetState()
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
if (isVisible) {
|
|
||||||
ModalBottomSheet(
|
|
||||||
modifier = modifier,
|
|
||||||
// modifier = modifier.navigationBarsPadding() - FIXME after https://issuetracker.google.com/issues/275849044
|
|
||||||
// .imePadding()
|
|
||||||
sheetState = sheetState,
|
|
||||||
onDismissRequest = {
|
|
||||||
coroutineScope.launch {
|
|
||||||
sheetState.hide()
|
|
||||||
onDismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
RetrySendMenuContents(
|
|
||||||
onRetry = onRetry,
|
|
||||||
onRemove = onRemove,
|
|
||||||
)
|
|
||||||
// FIXME remove after https://issuetracker.google.com/issues/275849044
|
|
||||||
Spacer(modifier = Modifier.height(32.dp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
private fun ColumnScope.RetrySendMenuContents(
|
|
||||||
onRetry: () -> Unit,
|
|
||||||
onRemove: () -> Unit,
|
|
||||||
sheetState: SheetState = rememberModalBottomSheetState(),
|
|
||||||
) {
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
ListItem(headlineContent = {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.screen_room_retry_send_menu_title),
|
|
||||||
style = ElementTheme.typography.fontBodyLgMedium,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
ListItem(
|
|
||||||
headlineContent = {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.screen_room_retry_send_menu_send_again_action),
|
|
||||||
style = ElementTheme.typography.fontBodyLgRegular,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
modifier = Modifier.clickable {
|
|
||||||
coroutineScope.launch {
|
|
||||||
sheetState.hide()
|
|
||||||
onRetry()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
ListItem(
|
|
||||||
headlineContent = {
|
|
||||||
Text(
|
|
||||||
text = stringResource(CommonStrings.action_remove),
|
|
||||||
style = ElementTheme.typography.fontBodyLgRegular,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
colors = ListItemDefaults.colors(headlineColor = MaterialTheme.colorScheme.error),
|
|
||||||
modifier = Modifier.clickable {
|
|
||||||
coroutineScope.launch {
|
|
||||||
sheetState.hide()
|
|
||||||
onRemove()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreviewsDayNight
|
|
||||||
@Composable
|
|
||||||
internal fun RetrySendMessageMenuPreview(@PreviewParameter(RetrySendMenuStateProvider::class) state: RetrySendMenuState) = ElementPreview {
|
|
||||||
RetrySendMessageMenu(
|
|
||||||
state = state,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -38,7 +38,6 @@ import io.element.android.features.messages.impl.timeline.components.customreact
|
||||||
import io.element.android.features.messages.impl.timeline.components.customreaction.FakeEmojibaseProvider
|
import io.element.android.features.messages.impl.timeline.components.customreaction.FakeEmojibaseProvider
|
||||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter
|
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter
|
||||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetPresenter
|
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetPresenter
|
||||||
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuPresenter
|
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
||||||
|
|
@ -839,7 +838,6 @@ class MessagesPresenterTest {
|
||||||
val readReceiptBottomSheetPresenter = ReadReceiptBottomSheetPresenter()
|
val readReceiptBottomSheetPresenter = ReadReceiptBottomSheetPresenter()
|
||||||
val customReactionPresenter = CustomReactionPresenter(emojibaseProvider = FakeEmojibaseProvider())
|
val customReactionPresenter = CustomReactionPresenter(emojibaseProvider = FakeEmojibaseProvider())
|
||||||
val reactionSummaryPresenter = ReactionSummaryPresenter(room = matrixRoom)
|
val reactionSummaryPresenter = ReactionSummaryPresenter(room = matrixRoom)
|
||||||
val retrySendMenuPresenter = RetrySendMenuPresenter(room = matrixRoom)
|
|
||||||
return MessagesPresenter(
|
return MessagesPresenter(
|
||||||
room = matrixRoom,
|
room = matrixRoom,
|
||||||
composerPresenter = messageComposerPresenter,
|
composerPresenter = messageComposerPresenter,
|
||||||
|
|
@ -849,7 +847,6 @@ class MessagesPresenterTest {
|
||||||
actionListPresenter = actionListPresenter,
|
actionListPresenter = actionListPresenter,
|
||||||
customReactionPresenter = customReactionPresenter,
|
customReactionPresenter = customReactionPresenter,
|
||||||
reactionSummaryPresenter = reactionSummaryPresenter,
|
reactionSummaryPresenter = reactionSummaryPresenter,
|
||||||
retrySendMenuPresenter = retrySendMenuPresenter,
|
|
||||||
readReceiptBottomSheetPresenter = readReceiptBottomSheetPresenter,
|
readReceiptBottomSheetPresenter = readReceiptBottomSheetPresenter,
|
||||||
networkMonitor = FakeNetworkMonitor(),
|
networkMonitor = FakeNetworkMonitor(),
|
||||||
snackbarDispatcher = SnackbarDispatcher(),
|
snackbarDispatcher = SnackbarDispatcher(),
|
||||||
|
|
|
||||||
|
|
@ -1,163 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.messages.impl.timeline.components.retrysendmenu
|
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionMode
|
|
||||||
import app.cash.molecule.moleculeFlow
|
|
||||||
import app.cash.turbine.test
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
|
||||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
|
||||||
import io.element.android.libraries.matrix.test.A_TRANSACTION_ID
|
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
class RetrySendMenuPresenterTest {
|
|
||||||
@get:Rule
|
|
||||||
val warmUpRule = WarmUpRule()
|
|
||||||
|
|
||||||
private val room = FakeMatrixRoom()
|
|
||||||
private val presenter = RetrySendMenuPresenter(room)
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - handle event selected`() = runTest {
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
val selectedEvent = aTimelineItemEvent()
|
|
||||||
initialState.eventSink(RetrySendMenuEvents.EventSelected(selectedEvent))
|
|
||||||
assertThat(awaitItem().selectedEvent).isSameInstanceAs(selectedEvent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - handle dismiss`() = runTest {
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
val selectedEvent = aTimelineItemEvent()
|
|
||||||
initialState.eventSink(RetrySendMenuEvents.EventSelected(selectedEvent))
|
|
||||||
skipItems(1)
|
|
||||||
initialState.eventSink(RetrySendMenuEvents.Dismiss)
|
|
||||||
assertThat(room.cancelSendCount).isEqualTo(0)
|
|
||||||
assertThat(room.retrySendMessageCount).isEqualTo(0)
|
|
||||||
assertThat(awaitItem().selectedEvent).isNull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - handle resend with transactionId`() = runTest {
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
val selectedEvent = aTimelineItemEvent(transactionId = A_TRANSACTION_ID)
|
|
||||||
initialState.eventSink(RetrySendMenuEvents.EventSelected(selectedEvent))
|
|
||||||
skipItems(1)
|
|
||||||
initialState.eventSink(RetrySendMenuEvents.Retry)
|
|
||||||
assertThat(room.cancelSendCount).isEqualTo(0)
|
|
||||||
assertThat(room.retrySendMessageCount).isEqualTo(1)
|
|
||||||
assertThat(awaitItem().selectedEvent).isNull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - handle resend without transactionId`() = runTest {
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
val selectedEvent = aTimelineItemEvent(transactionId = null)
|
|
||||||
initialState.eventSink(RetrySendMenuEvents.EventSelected(selectedEvent))
|
|
||||||
skipItems(1)
|
|
||||||
initialState.eventSink(RetrySendMenuEvents.Retry)
|
|
||||||
assertThat(room.cancelSendCount).isEqualTo(0)
|
|
||||||
assertThat(room.retrySendMessageCount).isEqualTo(0)
|
|
||||||
assertThat(awaitItem().selectedEvent).isNull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - handle resend with error`() = runTest {
|
|
||||||
room.givenRetrySendMessageResult(Result.failure(IllegalStateException("An error")))
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
val selectedEvent = aTimelineItemEvent(transactionId = A_TRANSACTION_ID)
|
|
||||||
initialState.eventSink(RetrySendMenuEvents.EventSelected(selectedEvent))
|
|
||||||
skipItems(1)
|
|
||||||
initialState.eventSink(RetrySendMenuEvents.Retry)
|
|
||||||
assertThat(room.cancelSendCount).isEqualTo(0)
|
|
||||||
assertThat(room.retrySendMessageCount).isEqualTo(1)
|
|
||||||
assertThat(awaitItem().selectedEvent).isNull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - handle remove failed message with transactionId`() = runTest {
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
val selectedEvent = aTimelineItemEvent(transactionId = A_TRANSACTION_ID)
|
|
||||||
initialState.eventSink(RetrySendMenuEvents.EventSelected(selectedEvent))
|
|
||||||
skipItems(1)
|
|
||||||
initialState.eventSink(RetrySendMenuEvents.Remove)
|
|
||||||
assertThat(room.cancelSendCount).isEqualTo(1)
|
|
||||||
assertThat(room.retrySendMessageCount).isEqualTo(0)
|
|
||||||
assertThat(awaitItem().selectedEvent).isNull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - handle remove failed message without transactionId`() = runTest {
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
val selectedEvent = aTimelineItemEvent(transactionId = null)
|
|
||||||
initialState.eventSink(RetrySendMenuEvents.EventSelected(selectedEvent))
|
|
||||||
skipItems(1)
|
|
||||||
initialState.eventSink(RetrySendMenuEvents.Remove)
|
|
||||||
assertThat(room.cancelSendCount).isEqualTo(0)
|
|
||||||
assertThat(room.retrySendMessageCount).isEqualTo(0)
|
|
||||||
assertThat(awaitItem().selectedEvent).isNull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `present - handle remove failed message with error`() = runTest {
|
|
||||||
room.givenRetrySendMessageResult(Result.failure(IllegalStateException("An error")))
|
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
|
||||||
presenter.present()
|
|
||||||
}.test {
|
|
||||||
val initialState = awaitItem()
|
|
||||||
val selectedEvent = aTimelineItemEvent(transactionId = A_TRANSACTION_ID)
|
|
||||||
initialState.eventSink(RetrySendMenuEvents.EventSelected(selectedEvent))
|
|
||||||
skipItems(1)
|
|
||||||
initialState.eventSink(RetrySendMenuEvents.Remove)
|
|
||||||
assertThat(room.cancelSendCount).isEqualTo(1)
|
|
||||||
assertThat(room.retrySendMessageCount).isEqualTo(0)
|
|
||||||
assertThat(awaitItem().selectedEvent).isNull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.messages.impl.timeline.components.retrysendmenu
|
|
||||||
|
|
||||||
import androidx.activity.ComponentActivity
|
|
||||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
|
||||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import io.element.android.features.messages.impl.R
|
|
||||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
|
||||||
import io.element.android.tests.testutils.EventsRecorder
|
|
||||||
import io.element.android.tests.testutils.clickOn
|
|
||||||
import io.element.android.tests.testutils.pressBackKey
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.rules.TestRule
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.robolectric.annotation.Config
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
class RetrySendMessageMenuTest {
|
|
||||||
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `dismiss the bottom sheet emits the expected event`() {
|
|
||||||
val eventsRecorder = EventsRecorder<RetrySendMenuEvents>()
|
|
||||||
rule.setRetrySendMessageMenu(
|
|
||||||
aRetrySendMenuState(
|
|
||||||
event = aTimelineItemEvent(),
|
|
||||||
eventSink = eventsRecorder
|
|
||||||
),
|
|
||||||
)
|
|
||||||
rule.pressBackKey()
|
|
||||||
// Cannot test this for now.
|
|
||||||
// eventsRecorder.assertSingle(RetrySendMenuEvents.Dismiss)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Config(qualifiers = "h1024dp")
|
|
||||||
@Test
|
|
||||||
fun `retry to send the event emits the expected event`() {
|
|
||||||
val eventsRecorder = EventsRecorder<RetrySendMenuEvents>()
|
|
||||||
rule.setRetrySendMessageMenu(
|
|
||||||
aRetrySendMenuState(
|
|
||||||
event = aTimelineItemEvent(),
|
|
||||||
eventSink = eventsRecorder
|
|
||||||
),
|
|
||||||
)
|
|
||||||
rule.clickOn(R.string.screen_room_retry_send_menu_send_again_action)
|
|
||||||
eventsRecorder.assertSingle(RetrySendMenuEvents.Retry)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Config(qualifiers = "h1024dp")
|
|
||||||
@Test
|
|
||||||
fun `remove the event emits the expected event`() {
|
|
||||||
val eventsRecorder = EventsRecorder<RetrySendMenuEvents>()
|
|
||||||
rule.setRetrySendMessageMenu(
|
|
||||||
aRetrySendMenuState(
|
|
||||||
event = aTimelineItemEvent(),
|
|
||||||
eventSink = eventsRecorder
|
|
||||||
),
|
|
||||||
)
|
|
||||||
rule.clickOn(CommonStrings.action_remove)
|
|
||||||
eventsRecorder.assertSingle(RetrySendMenuEvents.Remove)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRetrySendMessageMenu(
|
|
||||||
state: RetrySendMenuState,
|
|
||||||
) {
|
|
||||||
setContent {
|
|
||||||
RetrySendMessageMenu(
|
|
||||||
state = state,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue