Merge branch 'develop' into julioromano/poll_history_entry_point
This commit is contained in:
commit
863d156e4d
738 changed files with 9387 additions and 1581 deletions
|
|
@ -33,6 +33,7 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import kotlin.coroutines.coroutineContext
|
||||
|
||||
class AttachmentsPreviewPresenter @AssistedInject constructor(
|
||||
|
|
@ -114,6 +115,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
|
|||
sendActionState.value = SendActionState.Done
|
||||
},
|
||||
onFailure = { error ->
|
||||
Timber.e(error, "Failed to send attachment")
|
||||
if (error is CancellationException) {
|
||||
throw error
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ import kotlinx.coroutines.flow.filter
|
|||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.coroutineContext
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
|
@ -432,6 +433,7 @@ class MessageComposerPresenter @Inject constructor(
|
|||
attachmentState.value = AttachmentsState.None
|
||||
}
|
||||
.onFailure { cause ->
|
||||
Timber.e(cause, "Failed to send attachment")
|
||||
attachmentState.value = AttachmentsState.None
|
||||
if (cause is CancellationException) {
|
||||
throw cause
|
||||
|
|
|
|||
|
|
@ -175,6 +175,9 @@ class TimelinePresenter @AssistedInject constructor(
|
|||
}
|
||||
|
||||
return TimelineState(
|
||||
timelineRoomInfo = TimelineRoomInfo(
|
||||
isDirect = room.isDirect
|
||||
),
|
||||
highlightedEventId = highlightedEventId.value,
|
||||
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
|
||||
paginationState = paginationState,
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
@Immutable
|
||||
data class TimelineState(
|
||||
val timelineItems: ImmutableList<TimelineItem>,
|
||||
val timelineRoomInfo: TimelineRoomInfo,
|
||||
val showReadReceipts: Boolean,
|
||||
val highlightedEventId: EventId?,
|
||||
val userHasPermissionToSendMessage: Boolean,
|
||||
|
|
@ -35,3 +36,8 @@ data class TimelineState(
|
|||
val sessionState: SessionState,
|
||||
val eventSink: (TimelineEvents) -> Unit
|
||||
)
|
||||
|
||||
@Immutable
|
||||
data class TimelineRoomInfo(
|
||||
val isDirect: Boolean,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ import kotlin.random.Random
|
|||
|
||||
fun aTimelineState(timelineItems: ImmutableList<TimelineItem> = persistentListOf()) = TimelineState(
|
||||
timelineItems = timelineItems,
|
||||
timelineRoomInfo = aTimelineRoomInfo(),
|
||||
showReadReceipts = false,
|
||||
paginationState = MatrixTimeline.PaginationState(
|
||||
isBackPaginating = false,
|
||||
|
|
@ -212,3 +213,9 @@ internal fun aGroupedEvents(id: Long = 0): TimelineItem.GroupedEvents {
|
|||
aggregatedReadReceipts = events.flatMap { it.readReceiptState.receipts }.toImmutableList(),
|
||||
)
|
||||
}
|
||||
|
||||
internal fun aTimelineRoomInfo(
|
||||
isDirect: Boolean = false,
|
||||
) = TimelineRoomInfo(
|
||||
isDirect = isDirect,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ fun TimelineView(
|
|||
) { timelineItem ->
|
||||
TimelineItemRow(
|
||||
timelineItem = timelineItem,
|
||||
timelineRoomInfo = state.timelineRoomInfo,
|
||||
showReadReceipts = state.showReadReceipts,
|
||||
isLastOutgoingMessage = (timelineItem as? TimelineItem.Event)?.isMine == true
|
||||
&& state.timelineItems.first().identifier() == timelineItem.identifier(),
|
||||
|
|
|
|||
|
|
@ -17,17 +17,21 @@
|
|||
package io.element.android.features.messages.impl.timeline.components
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
|
||||
// For previews
|
||||
@Composable
|
||||
internal fun ATimelineItemEventRow(
|
||||
event: TimelineItem.Event,
|
||||
timelineRoomInfo: TimelineRoomInfo = aTimelineRoomInfo(),
|
||||
showReadReceipts: Boolean = false,
|
||||
isLastOutgoingMessage: Boolean = false,
|
||||
isHighlighted: Boolean = false,
|
||||
) = TimelineItemEventRow(
|
||||
event = event,
|
||||
timelineRoomInfo = timelineRoomInfo,
|
||||
showReadReceipts = showReadReceipts,
|
||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||
isHighlighted = isHighlighted,
|
||||
|
|
|
|||
|
|
@ -35,17 +35,17 @@ import androidx.compose.ui.draw.clip
|
|||
import androidx.compose.ui.graphics.Shape
|
||||
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.timeline.model.TimelineItemGroupPosition
|
||||
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState
|
||||
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleStateProvider
|
||||
import io.element.android.libraries.core.extensions.to01
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Surface
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.messageFromMeBackground
|
||||
import io.element.android.libraries.designsystem.theme.messageFromOtherBackground
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
|
||||
private val BUBBLE_RADIUS = 12.dp
|
||||
private val BUBBLE_INCOMING_OFFSET = 16.dp
|
||||
|
|
@ -91,10 +91,10 @@ fun MessageEventBubble(
|
|||
}
|
||||
|
||||
fun Modifier.offsetForItem(): Modifier {
|
||||
return if (state.isMine) {
|
||||
this
|
||||
} else {
|
||||
offset(x = BUBBLE_INCOMING_OFFSET)
|
||||
return when {
|
||||
state.isMine -> this
|
||||
state.timelineRoomInfo.isDirect -> this
|
||||
else -> offset(x = BUBBLE_INCOMING_OFFSET)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ import androidx.compose.ui.zIndex
|
|||
import androidx.constraintlayout.compose.ConstrainScope
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
|
||||
|
|
@ -101,6 +102,7 @@ import kotlin.math.roundToInt
|
|||
@Composable
|
||||
fun TimelineItemEventRow(
|
||||
event: TimelineItem.Event,
|
||||
timelineRoomInfo: TimelineRoomInfo,
|
||||
showReadReceipts: Boolean,
|
||||
isLastOutgoingMessage: Boolean,
|
||||
isHighlighted: Boolean,
|
||||
|
|
@ -163,9 +165,8 @@ fun TimelineItemEventRow(
|
|||
state = state.draggableState,
|
||||
),
|
||||
event = event,
|
||||
showReadReceipts = showReadReceipts,
|
||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||
isHighlighted = isHighlighted,
|
||||
timelineRoomInfo = timelineRoomInfo,
|
||||
interactionSource = interactionSource,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
|
|
@ -175,7 +176,6 @@ fun TimelineItemEventRow(
|
|||
onReactionClicked = { emoji -> onReactionClick(emoji, event) },
|
||||
onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) },
|
||||
onMoreReactionsClicked = { onMoreReactionsClick(event) },
|
||||
onReadReceiptsClicked = { onReadReceiptClick(event) },
|
||||
eventSink = eventSink,
|
||||
)
|
||||
}
|
||||
|
|
@ -183,9 +183,8 @@ fun TimelineItemEventRow(
|
|||
} else {
|
||||
TimelineItemEventRowContent(
|
||||
event = event,
|
||||
showReadReceipts = showReadReceipts,
|
||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||
isHighlighted = isHighlighted,
|
||||
timelineRoomInfo = timelineRoomInfo,
|
||||
interactionSource = interactionSource,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
|
|
@ -195,10 +194,20 @@ fun TimelineItemEventRow(
|
|||
onReactionClicked = { emoji -> onReactionClick(emoji, event) },
|
||||
onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) },
|
||||
onMoreReactionsClicked = { onMoreReactionsClick(event) },
|
||||
onReadReceiptsClicked = { onReadReceiptClick(event) },
|
||||
eventSink = eventSink,
|
||||
)
|
||||
}
|
||||
// Read receipts / Send state
|
||||
TimelineItemReadReceiptView(
|
||||
state = ReadReceiptViewState(
|
||||
sendState = event.localSendState,
|
||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||
receipts = event.readReceiptState.receipts,
|
||||
),
|
||||
showReadReceipts = showReadReceipts,
|
||||
onReadReceiptsClicked = { onReadReceiptClick(event) },
|
||||
modifier = Modifier.padding(top = 4.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -228,9 +237,8 @@ private fun SwipeSensitivity(
|
|||
@Composable
|
||||
private fun TimelineItemEventRowContent(
|
||||
event: TimelineItem.Event,
|
||||
showReadReceipts: Boolean,
|
||||
isLastOutgoingMessage: Boolean,
|
||||
isHighlighted: Boolean,
|
||||
timelineRoomInfo: TimelineRoomInfo,
|
||||
interactionSource: MutableInteractionSource,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
|
|
@ -238,7 +246,6 @@ private fun TimelineItemEventRowContent(
|
|||
inReplyToClicked: () -> Unit,
|
||||
onUserDataClicked: () -> Unit,
|
||||
onReactionClicked: (emoji: String) -> Unit,
|
||||
onReadReceiptsClicked: () -> Unit,
|
||||
onReactionLongClicked: (emoji: String) -> Unit,
|
||||
onMoreReactionsClicked: (event: TimelineItem.Event) -> Unit,
|
||||
eventSink: (TimelineEvents) -> Unit,
|
||||
|
|
@ -259,12 +266,11 @@ private fun TimelineItemEventRowContent(
|
|||
sender,
|
||||
message,
|
||||
reactions,
|
||||
readReceipts,
|
||||
) = createRefs()
|
||||
|
||||
// Sender
|
||||
val avatarStrokeSize = 3.dp
|
||||
if (event.showSenderInformation) {
|
||||
if (event.showSenderInformation && !timelineRoomInfo.isDirect) {
|
||||
MessageSenderInformation(
|
||||
event.safeSenderName,
|
||||
event.senderAvatar,
|
||||
|
|
@ -284,6 +290,7 @@ private fun TimelineItemEventRowContent(
|
|||
groupPosition = event.groupPosition,
|
||||
isMine = event.isMine,
|
||||
isHighlighted = isHighlighted,
|
||||
timelineRoomInfo = timelineRoomInfo,
|
||||
)
|
||||
MessageEventBubble(
|
||||
modifier = Modifier
|
||||
|
|
@ -326,25 +333,6 @@ private fun TimelineItemEventRowContent(
|
|||
.padding(start = if (event.isMine) 16.dp else 36.dp, end = 16.dp)
|
||||
)
|
||||
}
|
||||
|
||||
// Read receipts / Send state
|
||||
TimelineItemReadReceiptView(
|
||||
state = ReadReceiptViewState(
|
||||
sendState = event.localSendState,
|
||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||
receipts = event.readReceiptState.receipts,
|
||||
),
|
||||
showReadReceipts = showReadReceipts,
|
||||
onReadReceiptsClicked = onReadReceiptsClicked,
|
||||
modifier = Modifier
|
||||
.constrainAs(readReceipts) {
|
||||
if (event.reactionsState.reactions.isNotEmpty()) {
|
||||
top.linkTo(reactions.bottom, margin = 4.dp)
|
||||
} else {
|
||||
top.linkTo(message.bottom, margin = 4.dp)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -378,6 +366,7 @@ private fun MessageSenderInformation(
|
|||
Avatar(senderAvatar)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Text(
|
||||
modifier = Modifier.clipToBounds(),
|
||||
text = sender,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun TimelineItemEventRowForDirectRoomPreview() = ElementPreview {
|
||||
Column {
|
||||
sequenceOf(false, true).forEach {
|
||||
ATimelineItemEventRow(
|
||||
event = aTimelineItemEvent(
|
||||
isMine = it,
|
||||
content = aTimelineItemTextContent().copy(
|
||||
body = "A long text which will be displayed on several lines and" +
|
||||
" hopefully can be manually adjusted to test different behaviors."
|
||||
),
|
||||
groupPosition = TimelineItemGroupPosition.First,
|
||||
),
|
||||
timelineRoomInfo = aTimelineRoomInfo(
|
||||
isDirect = true,
|
||||
),
|
||||
)
|
||||
ATimelineItemEventRow(
|
||||
event = aTimelineItemEvent(
|
||||
isMine = it,
|
||||
content = aTimelineItemImageContent().copy(
|
||||
aspectRatio = 5f
|
||||
),
|
||||
groupPosition = TimelineItemGroupPosition.Last,
|
||||
),
|
||||
timelineRoomInfo = aTimelineRoomInfo(
|
||||
isDirect = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -24,8 +24,10 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import io.element.android.features.messages.impl.R
|
||||
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.aGroupedEvents
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.components.group.GroupHeaderView
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewState
|
||||
import io.element.android.features.messages.impl.timeline.components.receipt.TimelineItemReadReceiptView
|
||||
|
|
@ -40,6 +42,7 @@ import io.element.android.libraries.matrix.api.core.UserId
|
|||
@Composable
|
||||
fun TimelineItemGroupedEventsRow(
|
||||
timelineItem: TimelineItem.GroupedEvents,
|
||||
timelineRoomInfo: TimelineRoomInfo,
|
||||
showReadReceipts: Boolean,
|
||||
isLastOutgoingMessage: Boolean,
|
||||
highlightedItem: String?,
|
||||
|
|
@ -66,6 +69,7 @@ fun TimelineItemGroupedEventsRow(
|
|||
isExpanded = isExpanded.value,
|
||||
onExpandGroupClick = ::onExpandGroupClick,
|
||||
timelineItem = timelineItem,
|
||||
timelineRoomInfo = timelineRoomInfo,
|
||||
highlightedItem = highlightedItem,
|
||||
showReadReceipts = showReadReceipts,
|
||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||
|
|
@ -89,6 +93,7 @@ private fun TimelineItemGroupedEventsRowContent(
|
|||
isExpanded: Boolean,
|
||||
onExpandGroupClick: () -> Unit,
|
||||
timelineItem: TimelineItem.GroupedEvents,
|
||||
timelineRoomInfo: TimelineRoomInfo,
|
||||
highlightedItem: String?,
|
||||
showReadReceipts: Boolean,
|
||||
isLastOutgoingMessage: Boolean,
|
||||
|
|
@ -121,6 +126,7 @@ private fun TimelineItemGroupedEventsRowContent(
|
|||
timelineItem.events.forEach { subGroupEvent ->
|
||||
TimelineItemRow(
|
||||
timelineItem = subGroupEvent,
|
||||
timelineRoomInfo = timelineRoomInfo,
|
||||
showReadReceipts = showReadReceipts,
|
||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||
highlightedItem = highlightedItem,
|
||||
|
|
@ -148,7 +154,8 @@ private fun TimelineItemGroupedEventsRowContent(
|
|||
receipts = timelineItem.aggregatedReadReceipts,
|
||||
),
|
||||
showReadReceipts = true,
|
||||
onReadReceiptsClicked = { /* No op for group event */ })
|
||||
onReadReceiptsClicked = onExpandGroupClick
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -160,6 +167,7 @@ internal fun TimelineItemGroupedEventsRowContentExpandedPreview() = ElementPrevi
|
|||
isExpanded = true,
|
||||
onExpandGroupClick = {},
|
||||
timelineItem = aGroupedEvents(),
|
||||
timelineRoomInfo = aTimelineRoomInfo(),
|
||||
highlightedItem = null,
|
||||
showReadReceipts = true,
|
||||
isLastOutgoingMessage = false,
|
||||
|
|
@ -184,6 +192,7 @@ internal fun TimelineItemGroupedEventsRowContentCollapsePreview() = ElementPrevi
|
|||
isExpanded = false,
|
||||
onExpandGroupClick = {},
|
||||
timelineItem = aGroupedEvents(),
|
||||
timelineRoomInfo = aTimelineRoomInfo(),
|
||||
highlightedItem = null,
|
||||
showReadReceipts = true,
|
||||
isLastOutgoingMessage = false,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline.components
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
|
||||
|
|
@ -29,6 +30,7 @@ import io.element.android.libraries.matrix.api.core.UserId
|
|||
@Composable
|
||||
internal fun TimelineItemRow(
|
||||
timelineItem: TimelineItem,
|
||||
timelineRoomInfo: TimelineRoomInfo,
|
||||
showReadReceipts: Boolean,
|
||||
isLastOutgoingMessage: Boolean,
|
||||
highlightedItem: String?,
|
||||
|
|
@ -71,6 +73,7 @@ internal fun TimelineItemRow(
|
|||
} else {
|
||||
TimelineItemEventRow(
|
||||
event = timelineItem,
|
||||
timelineRoomInfo = timelineRoomInfo,
|
||||
showReadReceipts = showReadReceipts,
|
||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||
isHighlighted = highlightedItem == timelineItem.identifier(),
|
||||
|
|
@ -93,6 +96,7 @@ internal fun TimelineItemRow(
|
|||
is TimelineItem.GroupedEvents -> {
|
||||
TimelineItemGroupedEventsRow(
|
||||
timelineItem = timelineItem,
|
||||
timelineRoomInfo = timelineRoomInfo,
|
||||
showReadReceipts = showReadReceipts,
|
||||
isLastOutgoingMessage = isLastOutgoingMessage,
|
||||
highlightedItem = highlightedItem,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package io.element.android.features.messages.impl.timeline.components.group
|
||||
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
|
|
@ -26,6 +28,7 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
|
|
@ -76,9 +79,17 @@ fun GroupHeaderView(
|
|||
color = MaterialTheme.colorScheme.secondary,
|
||||
style = ElementTheme.typography.fontBodyMdRegular,
|
||||
)
|
||||
val rotation: Float by animateFloatAsState(
|
||||
targetValue = if (isExpanded) 90f else 0f,
|
||||
animationSpec = tween(
|
||||
delayMillis = 0,
|
||||
durationMillis = 300,
|
||||
),
|
||||
label = "chevron"
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier.rotate(if (isExpanded) 180f else 0f),
|
||||
imageVector = CompoundIcons.ChevronDown,
|
||||
modifier = Modifier.rotate(rotation),
|
||||
imageVector = CompoundIcons.ChevronRight,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,10 +16,12 @@
|
|||
|
||||
package io.element.android.features.messages.impl.timeline.model.bubble
|
||||
|
||||
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition
|
||||
|
||||
data class BubbleState(
|
||||
val groupPosition: TimelineItemGroupPosition,
|
||||
val isMine: Boolean,
|
||||
val isHighlighted: Boolean,
|
||||
val timelineRoomInfo: TimelineRoomInfo,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
package io.element.android.features.messages.impl.timeline.model.bubble
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.messages.impl.timeline.TimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition
|
||||
|
||||
open class BubbleStateProvider : PreviewParameterProvider<BubbleState> {
|
||||
|
|
@ -29,7 +31,11 @@ open class BubbleStateProvider : PreviewParameterProvider<BubbleState> {
|
|||
).map { groupPosition ->
|
||||
sequenceOf(false, true).map { isMine ->
|
||||
sequenceOf(false, true).map { isHighlighted ->
|
||||
BubbleState(groupPosition, isMine = isMine, isHighlighted = isHighlighted)
|
||||
aBubbleState(
|
||||
groupPosition = groupPosition,
|
||||
isMine = isMine,
|
||||
isHighlighted = isHighlighted,
|
||||
)
|
||||
}
|
||||
}
|
||||
.flatten()
|
||||
|
|
@ -37,8 +43,14 @@ open class BubbleStateProvider : PreviewParameterProvider<BubbleState> {
|
|||
.flatten()
|
||||
}
|
||||
|
||||
fun aBubbleState() = BubbleState(
|
||||
groupPosition = TimelineItemGroupPosition.First,
|
||||
isMine = false,
|
||||
isHighlighted = false,
|
||||
internal fun aBubbleState(
|
||||
groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.First,
|
||||
isMine: Boolean = false,
|
||||
isHighlighted: Boolean = false,
|
||||
timelineRoomInfo: TimelineRoomInfo = aTimelineRoomInfo(),
|
||||
) = BubbleState(
|
||||
groupPosition = groupPosition,
|
||||
isMine = isMine,
|
||||
isHighlighted = isHighlighted,
|
||||
timelineRoomInfo = timelineRoomInfo,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import io.element.android.libraries.di.CacheDirectory
|
|||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
import io.element.android.libraries.matrix.api.mxc.MxcTools
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
|
|
@ -66,6 +67,7 @@ interface VoiceMessageMediaRepo {
|
|||
|
||||
class DefaultVoiceMessageMediaRepo @AssistedInject constructor(
|
||||
@CacheDirectory private val cacheDir: File,
|
||||
mxcTools: MxcTools,
|
||||
private val matrixMediaLoader: MatrixMediaLoader,
|
||||
@Assisted private val mediaSource: MediaSource,
|
||||
@Assisted("mimeType") private val mimeType: String?,
|
||||
|
|
@ -101,7 +103,7 @@ class DefaultVoiceMessageMediaRepo @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private val cachedFile: File? = mxcUri2FilePath(mediaSource.url)?.let {
|
||||
private val cachedFile: File? = mxcTools.mxcUri2FilePath(mediaSource.url)?.let {
|
||||
File("${cacheDir.path}/$CACHE_VOICE_SUBDIR/$it")
|
||||
}
|
||||
}
|
||||
|
|
@ -110,24 +112,3 @@ class DefaultVoiceMessageMediaRepo @AssistedInject constructor(
|
|||
* Subdirectory of the application's cache directory where voice messages are stored.
|
||||
*/
|
||||
private const val CACHE_VOICE_SUBDIR = "temp/voice"
|
||||
|
||||
/**
|
||||
* Regex to match a Matrix Content (mxc://) URI.
|
||||
*
|
||||
* See: https://spec.matrix.org/v1.8/client-server-api/#matrix-content-mxc-uris
|
||||
*/
|
||||
private val mxcRegex = Regex("""^mxc:\/\/([^\/]+)\/([^\/]+)$""")
|
||||
|
||||
/**
|
||||
* Sanitizes an mxcUri to be used as a relative file path.
|
||||
*
|
||||
* @param mxcUri the Matrix Content (mxc://) URI of the voice message.
|
||||
* @return the relative file path as "<server-name>/<media-id>" or null if the mxcUri is invalid.
|
||||
*/
|
||||
private fun mxcUri2FilePath(mxcUri: String): String? = mxcRegex.matchEntire(mxcUri)?.let { match ->
|
||||
buildString {
|
||||
append(match.groupValues[1])
|
||||
append("/")
|
||||
append(match.groupValues[2])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
<string name="room_timeline_beginning_of_room">"Dies ist der Anfang von %1$s."</string>
|
||||
<string name="room_timeline_beginning_of_room_no_name">"Dies ist der Anfang dieses Gesprächs."</string>
|
||||
<string name="room_timeline_read_marker_title">"Neu"</string>
|
||||
<string name="screen_room_mentions_at_room_subtitle">"Den ganzen Raum benachrichtigen"</string>
|
||||
<string name="screen_report_content_block_user_hint">"Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest"</string>
|
||||
<string name="screen_room_attachment_source_camera">"Kamera"</string>
|
||||
<string name="screen_room_attachment_source_camera_photo">"Foto machen"</string>
|
||||
|
|
@ -27,6 +28,7 @@
|
|||
<string name="screen_room_attachment_source_poll">"Umfrage"</string>
|
||||
<string name="screen_room_attachment_text_formatting">"Textformatierung"</string>
|
||||
<string name="screen_room_encrypted_history_banner">"Der Nachrichtenverlauf ist derzeit in diesem Raum nicht verfügbar"</string>
|
||||
<string name="screen_room_encrypted_history_banner_unverified">"Der Nachrichtenverlauf ist in diesem Raum nicht verfügbar. Verifiziere dieses Gerät, um deinen Nachrichtenverlauf zu sehen."</string>
|
||||
<string name="screen_room_error_failed_retrieving_user_details">"Benutzerdetails konnten nicht abgerufen werden"</string>
|
||||
<string name="screen_room_invite_again_alert_message">"Möchtest du sie wieder einladen?"</string>
|
||||
<string name="screen_room_invite_again_alert_title">"Du bist allein in diesem Chat"</string>
|
||||
|
|
@ -42,6 +44,7 @@
|
|||
<string name="screen_room_notification_settings_error_loading_settings">"Beim Laden der Benachrichtigungseinstellungen ist ein Fehler aufgetreten."</string>
|
||||
<string name="screen_room_notification_settings_error_restoring_default">"Fehler beim Wiederherstellen des Standardmodus. Bitte versuche es erneut."</string>
|
||||
<string name="screen_room_notification_settings_error_setting_mode">"Fehler beim Einstellen des Modus. Bitte versuche es erneut."</string>
|
||||
<string name="screen_room_notification_settings_mentions_only_disclaimer">"Dein Homeserver unterstützt diese Option in verschlüsselten Räumen nicht. Du wirst in diesem Raum nicht benachrichtigt."</string>
|
||||
<string name="screen_room_notification_settings_mode_all_messages">"Alle Nachrichten"</string>
|
||||
<string name="screen_room_notification_settings_room_custom_settings_title">"Benachrichtige mich in diesem Raum bei"</string>
|
||||
<string name="screen_room_reactions_show_less">"Weniger anzeigen"</string>
|
||||
|
|
@ -50,6 +53,8 @@
|
|||
<string name="screen_room_retry_send_menu_title">"Deine Nachricht konnte nicht gesendet werden"</string>
|
||||
<string name="screen_room_timeline_add_reaction">"Emoji hinzufügen"</string>
|
||||
<string name="screen_room_timeline_less_reactions">"Weniger anzeigen"</string>
|
||||
<string name="screen_room_voice_message_tooltip">"Zum Aufnehmen gedrückt halten"</string>
|
||||
<string name="screen_room_mentions_at_room_title">"Alle"</string>
|
||||
<string name="screen_report_content_block_user">"Benutzer sperren"</string>
|
||||
<string name="screen_room_error_failed_processing_media">"Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut."</string>
|
||||
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Nur Erwähnungen und Schlüsselwörter"</string>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="emoji_picker_category_activity">"Tevékenységek"</string>
|
||||
<string name="emoji_picker_category_flags">"Zászlók"</string>
|
||||
<string name="emoji_picker_category_foods">"Étel és ital"</string>
|
||||
<string name="emoji_picker_category_nature">"Állatok és természet"</string>
|
||||
<string name="emoji_picker_category_objects">"Tárgyak"</string>
|
||||
<string name="emoji_picker_category_people">"Mosolyok és emberek"</string>
|
||||
<string name="emoji_picker_category_places">"Utazás és helyek"</string>
|
||||
<string name="emoji_picker_category_symbols">"Szimbólumok"</string>
|
||||
<string name="report_content_explanation">"Ez az üzenet jelentve lesz a Matrix-kiszolgáló rendszergazdájának. Nem fogja tudni elolvasni a titkosított üzeneteket."</string>
|
||||
<string name="report_content_hint">"A tartalom jelentésének oka"</string>
|
||||
<string name="room_timeline_beginning_of_room">"Ez a(z) %1$s kezdete."</string>
|
||||
<string name="room_timeline_beginning_of_room_no_name">"Ez a beszélgetés kezdete."</string>
|
||||
<string name="room_timeline_read_marker_title">"Új"</string>
|
||||
<string name="screen_room_mentions_at_room_subtitle">"Teljes szoba értesítése"</string>
|
||||
<string name="screen_report_content_block_user_hint">"Jelölje be, ha el akarja rejteni az összes jelenlegi és jövőbeli üzenetet ettől a felhasználótól"</string>
|
||||
<string name="screen_room_attachment_source_camera">"Kamera"</string>
|
||||
<string name="screen_room_attachment_source_camera_photo">"Fénykép készítése"</string>
|
||||
<string name="screen_room_attachment_source_camera_video">"Videó rögzítése"</string>
|
||||
<string name="screen_room_attachment_source_files">"Melléklet"</string>
|
||||
<string name="screen_room_attachment_source_gallery">"Fénykép- és videótár"</string>
|
||||
<string name="screen_room_attachment_source_location">"Hely"</string>
|
||||
<string name="screen_room_attachment_source_poll">"Szavazás"</string>
|
||||
<string name="screen_room_attachment_text_formatting">"Szövegformázás"</string>
|
||||
<string name="screen_room_encrypted_history_banner">"Az üzenetelőzmények jelenleg nem érhetők el."</string>
|
||||
<string name="screen_room_encrypted_history_banner_unverified">"Az üzenetelőzmények nem érhetők el ebben a szobában. Ellenőrizze ezt az eszközt, hogy lássa az előzményeket."</string>
|
||||
<string name="screen_room_error_failed_retrieving_user_details">"Nem sikerült letölteni a felhasználói adatokat"</string>
|
||||
<string name="screen_room_invite_again_alert_message">"Visszahívja?"</string>
|
||||
<string name="screen_room_invite_again_alert_title">"Egyedül van ebben a csevegésben"</string>
|
||||
<string name="screen_room_message_copied">"Üzenet másolva"</string>
|
||||
<string name="screen_room_no_permission_to_post">"Nincs jogosultsága arra, hogy bejegyzést tegyen közzé ebben a szobában"</string>
|
||||
<string name="screen_room_notification_settings_allow_custom">"Egyéni beállítás engedélyezése"</string>
|
||||
<string name="screen_room_notification_settings_allow_custom_footnote">"Ennek bekapcsolása felülírja az alapértelmezett beállítást"</string>
|
||||
<string name="screen_room_notification_settings_custom_settings_title">"Értesítések kérése ebben a csevegésben ezekről:"</string>
|
||||
<string name="screen_room_notification_settings_default_setting_footnote">"Megváltoztathatja a %1$s."</string>
|
||||
<string name="screen_room_notification_settings_default_setting_footnote_content_link">"globális beállításokban"</string>
|
||||
<string name="screen_room_notification_settings_default_setting_title">"Alapértelmezett beállítás"</string>
|
||||
<string name="screen_room_notification_settings_edit_remove_setting">"Egyéni beállítás eltávolítása"</string>
|
||||
<string name="screen_room_notification_settings_error_loading_settings">"Hiba történt az értesítési beállítások betöltésekor."</string>
|
||||
<string name="screen_room_notification_settings_error_restoring_default">"Nem sikerült visszaállítani az alapértelmezett módot, próbálja újra."</string>
|
||||
<string name="screen_room_notification_settings_error_setting_mode">"Nem sikerült a mód beállítása, próbálja újra."</string>
|
||||
<string name="screen_room_notification_settings_mentions_only_disclaimer">"A Matrix-kiszolgálója nem támogatja ezt a beállítást a titkosított szobákban, egyes szobákban nem fog értesítéseket kapni."</string>
|
||||
<string name="screen_room_notification_settings_mode_all_messages">"Összes üzenet"</string>
|
||||
<string name="screen_room_notification_settings_room_custom_settings_title">"Ebben a szobában, értesítés ezekről:"</string>
|
||||
<string name="screen_room_reactions_show_less">"Kevesebb megjelenítése"</string>
|
||||
<string name="screen_room_reactions_show_more">"Több megjelenítése"</string>
|
||||
<string name="screen_room_retry_send_menu_send_again_action">"Újraküldés"</string>
|
||||
<string name="screen_room_retry_send_menu_title">"Az üzenet elküldése sikertelen"</string>
|
||||
<string name="screen_room_timeline_add_reaction">"Emodzsi hozzáadása"</string>
|
||||
<string name="screen_room_timeline_less_reactions">"Kevesebb megjelenítése"</string>
|
||||
<string name="screen_room_voice_message_tooltip">"Tartsa a rögzítéshez"</string>
|
||||
<string name="screen_room_mentions_at_room_title">"Mindenki"</string>
|
||||
<string name="screen_report_content_block_user">"Felhasználó letiltása"</string>
|
||||
<string name="screen_room_error_failed_processing_media">"Nem sikerült feldolgozni a feltöltendő médiát, próbálja újra."</string>
|
||||
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Csak említések és kulcsszavak"</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="emoji_picker_category_activity">"Aktivitas"</string>
|
||||
<string name="emoji_picker_category_flags">"Bendera"</string>
|
||||
<string name="emoji_picker_category_foods">"Makanan & Minuman"</string>
|
||||
<string name="emoji_picker_category_nature">"Hewan & Alam"</string>
|
||||
<string name="emoji_picker_category_objects">"Objek"</string>
|
||||
<string name="emoji_picker_category_people">"Senyuman & Orang"</string>
|
||||
<string name="emoji_picker_category_places">"Wisata & Tempat"</string>
|
||||
<string name="emoji_picker_category_symbols">"Simbol"</string>
|
||||
<plurals name="room_timeline_state_changes">
|
||||
<item quantity="other">"%1$d perubahan ruangan"</item>
|
||||
</plurals>
|
||||
<string name="report_content_explanation">"Pesan ini akan dilaporkan ke administrator homeserver Anda. Mereka tidak akan dapat membaca pesan terenkripsi apa pun."</string>
|
||||
<string name="report_content_hint">"Alasan melaporkan konten ini"</string>
|
||||
<string name="room_timeline_beginning_of_room">"Ini adalah awal dari %1$s."</string>
|
||||
<string name="room_timeline_beginning_of_room_no_name">"Ini adalah awal dari percakapan ini."</string>
|
||||
<string name="room_timeline_read_marker_title">"Baru"</string>
|
||||
<string name="screen_room_mentions_at_room_subtitle">"Beri tahu seluruh ruangan"</string>
|
||||
<string name="screen_report_content_block_user_hint">"Centang jika Anda ingin menyembunyikan semua pesan saat ini dan yang akan datang dari pengguna ini"</string>
|
||||
<string name="screen_room_attachment_source_camera">"Kamera"</string>
|
||||
<string name="screen_room_attachment_source_camera_photo">"Ambil foto"</string>
|
||||
<string name="screen_room_attachment_source_camera_video">"Rekam video"</string>
|
||||
<string name="screen_room_attachment_source_files">"Lampiran"</string>
|
||||
<string name="screen_room_attachment_source_gallery">"Pustaka Foto & Video"</string>
|
||||
<string name="screen_room_attachment_source_location">"Lokasi"</string>
|
||||
<string name="screen_room_attachment_source_poll">"Pemungutan suara"</string>
|
||||
<string name="screen_room_attachment_text_formatting">"Pemformatan Teks"</string>
|
||||
<string name="screen_room_encrypted_history_banner">"Riwayat pesan saat ini tidak tersedia di ruangan ini"</string>
|
||||
<string name="screen_room_encrypted_history_banner_unverified">"Riwayat pesan tidak tersedia di ruangan ini. Verifikasi perangkat ini untuk melihat riwayat pesan."</string>
|
||||
<string name="screen_room_error_failed_retrieving_user_details">"Tidak dapat mengambil detail pengguna"</string>
|
||||
<string name="screen_room_invite_again_alert_message">"Apakah Anda ingin mengundang mereka kembali?"</string>
|
||||
<string name="screen_room_invite_again_alert_title">"Anda sendirian di obrolan ini"</string>
|
||||
<string name="screen_room_message_copied">"Pesan disalin"</string>
|
||||
<string name="screen_room_no_permission_to_post">"Anda tidak memiliki izin untuk mengirim di ruangan ini"</string>
|
||||
<string name="screen_room_notification_settings_allow_custom">"Izinkan pengaturan khusus"</string>
|
||||
<string name="screen_room_notification_settings_allow_custom_footnote">"Mengaktifkan ini akan mengganti pengaturan bawaan Anda"</string>
|
||||
<string name="screen_room_notification_settings_custom_settings_title">"Beri tahu saya di obrolan ini tentang"</string>
|
||||
<string name="screen_room_notification_settings_default_setting_footnote">"Anda dapat mengubahnya di %1$s Anda."</string>
|
||||
<string name="screen_room_notification_settings_default_setting_footnote_content_link">"pengaturan global"</string>
|
||||
<string name="screen_room_notification_settings_default_setting_title">"Pengaturan bawaan"</string>
|
||||
<string name="screen_room_notification_settings_edit_remove_setting">"Hapus pengaturan khusus"</string>
|
||||
<string name="screen_room_notification_settings_error_loading_settings">"Terjadi kesalahan saat memuat pengaturan pemberitahuan."</string>
|
||||
<string name="screen_room_notification_settings_error_restoring_default">"Gagal memulihkan mode bawaan, silakan coba lagi."</string>
|
||||
<string name="screen_room_notification_settings_error_setting_mode">"Gagal mengatur mode, silakan coba lagi."</string>
|
||||
<string name="screen_room_notification_settings_mentions_only_disclaimer">"Homeserver Anda tidak mendukung opsi ini dalam ruangan terenkripsi, Anda tidak akan diberi tahu dalam ruangan ini."</string>
|
||||
<string name="screen_room_notification_settings_mode_all_messages">"Semua pesan"</string>
|
||||
<string name="screen_room_notification_settings_room_custom_settings_title">"Di ruangan ini, beri tahu saya tentang"</string>
|
||||
<string name="screen_room_reactions_show_less">"Tampilkan lebih sedikit"</string>
|
||||
<string name="screen_room_reactions_show_more">"Tampilkan lebih banyak"</string>
|
||||
<string name="screen_room_retry_send_menu_send_again_action">"Kirim ulang"</string>
|
||||
<string name="screen_room_retry_send_menu_title">"Pesan Anda gagal dikirim"</string>
|
||||
<string name="screen_room_timeline_add_reaction">"Tambahkan emoji"</string>
|
||||
<string name="screen_room_timeline_less_reactions">"Tampilkan lebih sedikit"</string>
|
||||
<string name="screen_room_voice_message_tooltip">"Tahan untuk merekam"</string>
|
||||
<string name="screen_room_mentions_at_room_title">"Semua orang"</string>
|
||||
<string name="screen_report_content_block_user">"Blokir pengguna"</string>
|
||||
<string name="screen_room_error_failed_processing_media">"Gagal memproses media untuk diunggah, silakan coba lagi."</string>
|
||||
<string name="screen_room_notification_settings_mode_mentions_and_keywords">"Sebutan dan Kata Kunci saja"</string>
|
||||
</resources>
|
||||
|
|
@ -379,9 +379,9 @@ class MessageComposerPresenterTest {
|
|||
}.test {
|
||||
skipItems(1)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.showAttachmentSourcePicker).isEqualTo(false)
|
||||
assertThat(initialState.showAttachmentSourcePicker).isFalse()
|
||||
initialState.eventSink(MessageComposerEvents.AddAttachment)
|
||||
assertThat(awaitItem().showAttachmentSourcePicker).isEqualTo(true)
|
||||
assertThat(awaitItem().showAttachmentSourcePicker).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class ReactionSummaryPresenterTests {
|
|||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.target).isEqualTo(null)
|
||||
assertThat(initialState.target).isNull()
|
||||
|
||||
initialState.eventSink(summaryEvent)
|
||||
assertThat(awaitItem().target).isNotNull()
|
||||
|
|
@ -69,7 +69,7 @@ class ReactionSummaryPresenterTests {
|
|||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.target).isEqualTo(null)
|
||||
assertThat(initialState.target).isNull()
|
||||
|
||||
initialState.eventSink(summaryEvent)
|
||||
val reactions = awaitItem().target?.reactions
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
|||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.media.AudioInfo
|
||||
|
|
@ -59,7 +59,7 @@ class InReplyToMetadataKtTest {
|
|||
anInReplyToDetails(eventContent = aMessageContent()).metadata()
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it).isEqualTo(InReplyToMetadata.Text("textContent"))
|
||||
assertThat(it).isEqualTo(InReplyToMetadata.Text("textContent"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -78,7 +78,7 @@ class InReplyToMetadataKtTest {
|
|||
).metadata()
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it).isEqualTo(
|
||||
assertThat(it).isEqualTo(
|
||||
InReplyToMetadata.Thumbnail(
|
||||
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||
thumbnailSource = aMediaSource(),
|
||||
|
|
@ -115,7 +115,7 @@ class InReplyToMetadataKtTest {
|
|||
).metadata()
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it).isEqualTo(
|
||||
assertThat(it).isEqualTo(
|
||||
InReplyToMetadata.Thumbnail(
|
||||
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||
thumbnailSource = aMediaSource(),
|
||||
|
|
@ -148,7 +148,7 @@ class InReplyToMetadataKtTest {
|
|||
).metadata()
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it).isEqualTo(
|
||||
assertThat(it).isEqualTo(
|
||||
InReplyToMetadata.Thumbnail(
|
||||
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||
thumbnailSource = aMediaSource(),
|
||||
|
|
@ -180,7 +180,7 @@ class InReplyToMetadataKtTest {
|
|||
).metadata()
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it).isEqualTo(
|
||||
assertThat(it).isEqualTo(
|
||||
InReplyToMetadata.Thumbnail(
|
||||
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||
textContent = "body",
|
||||
|
|
@ -209,7 +209,7 @@ class InReplyToMetadataKtTest {
|
|||
}
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it).isEqualTo(
|
||||
assertThat(it).isEqualTo(
|
||||
InReplyToMetadata.Thumbnail(
|
||||
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||
thumbnailSource = null,
|
||||
|
|
@ -240,7 +240,7 @@ class InReplyToMetadataKtTest {
|
|||
}
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it).isEqualTo(
|
||||
assertThat(it).isEqualTo(
|
||||
InReplyToMetadata.Thumbnail(
|
||||
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||
thumbnailSource = null,
|
||||
|
|
@ -262,7 +262,7 @@ class InReplyToMetadataKtTest {
|
|||
).metadata()
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it).isEqualTo(
|
||||
assertThat(it).isEqualTo(
|
||||
InReplyToMetadata.Thumbnail(
|
||||
attachmentThumbnailInfo = AttachmentThumbnailInfo(
|
||||
thumbnailSource = null,
|
||||
|
|
@ -284,7 +284,7 @@ class InReplyToMetadataKtTest {
|
|||
).metadata()
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it).isEqualTo(null)
|
||||
assertThat(it).isNull()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,10 +16,11 @@
|
|||
|
||||
package io.element.android.features.messages.impl.voicemessages.timeline
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
import io.element.android.libraries.matrix.api.mxc.MxcTools
|
||||
import io.element.android.libraries.matrix.test.media.FakeMediaLoader
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
|
|
@ -43,10 +44,10 @@ class DefaultVoiceMessageMediaRepoTest {
|
|||
)
|
||||
|
||||
repo.getMediaFile().let { result ->
|
||||
Truth.assertThat(result.isSuccess).isTrue()
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
result.getOrThrow().let { file ->
|
||||
Truth.assertThat(file.path).isEqualTo(temporaryFolder.cachedFilePath)
|
||||
Truth.assertThat(file.exists()).isTrue()
|
||||
assertThat(file.path).isEqualTo(temporaryFolder.cachedFilePath)
|
||||
assertThat(file.exists()).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -62,9 +63,9 @@ class DefaultVoiceMessageMediaRepoTest {
|
|||
)
|
||||
|
||||
repo.getMediaFile().let { result ->
|
||||
Truth.assertThat(result.isFailure).isTrue()
|
||||
assertThat(result.isFailure).isTrue()
|
||||
result.exceptionOrNull()!!.let { exception ->
|
||||
Truth.assertThat(exception).isInstanceOf(RuntimeException::class.java)
|
||||
assertThat(exception).isInstanceOf(RuntimeException::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -87,9 +88,9 @@ class DefaultVoiceMessageMediaRepoTest {
|
|||
)
|
||||
|
||||
repo.getMediaFile().let { result ->
|
||||
Truth.assertThat(result.isFailure).isTrue()
|
||||
assertThat(result.isFailure).isTrue()
|
||||
result.exceptionOrNull()?.let { exception ->
|
||||
Truth.assertThat(exception).apply {
|
||||
assertThat(exception).apply {
|
||||
isInstanceOf(IllegalStateException::class.java)
|
||||
hasMessageThat().isEqualTo("Failed to move file to cache.")
|
||||
}
|
||||
|
|
@ -109,10 +110,10 @@ class DefaultVoiceMessageMediaRepoTest {
|
|||
)
|
||||
|
||||
repo.getMediaFile().let { result ->
|
||||
Truth.assertThat(result.isSuccess).isTrue()
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
result.getOrThrow().let { file ->
|
||||
Truth.assertThat(file.path).isEqualTo(temporaryFolder.cachedFilePath)
|
||||
Truth.assertThat(file.exists()).isTrue()
|
||||
assertThat(file.path).isEqualTo(temporaryFolder.cachedFilePath)
|
||||
assertThat(file.exists()).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -124,10 +125,10 @@ class DefaultVoiceMessageMediaRepoTest {
|
|||
mxcUri = INVALID_MXC_URI,
|
||||
)
|
||||
repo.getMediaFile().let { result ->
|
||||
Truth.assertThat(result.isFailure).isTrue()
|
||||
assertThat(result.isFailure).isTrue()
|
||||
result.exceptionOrNull()!!.let { exception ->
|
||||
Truth.assertThat(exception).isInstanceOf(RuntimeException::class.java)
|
||||
Truth.assertThat(exception).hasMessageThat().isEqualTo("Invalid mxcUri.")
|
||||
assertThat(exception).isInstanceOf(RuntimeException::class.java)
|
||||
assertThat(exception).hasMessageThat().isEqualTo("Invalid mxcUri.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -139,6 +140,7 @@ private fun createDefaultVoiceMessageMediaRepo(
|
|||
mxcUri: String = MXC_URI,
|
||||
) = DefaultVoiceMessageMediaRepo(
|
||||
cacheDir = temporaryFolder.root,
|
||||
mxcTools = MxcTools(),
|
||||
matrixMediaLoader = matrixMediaLoader,
|
||||
mediaSource = MediaSource(
|
||||
url = mxcUri,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package io.element.android.features.messages.impl.voicemessages.timeline
|
|||
|
||||
import app.cash.turbine.TurbineTestContext
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||
|
|
@ -42,7 +42,7 @@ class DefaultVoiceMessagePlayerTest {
|
|||
val player = createDefaultVoiceMessagePlayer()
|
||||
player.state.test {
|
||||
matchInitialState()
|
||||
Truth.assertThat(player.prepare().isSuccess).isTrue()
|
||||
assertThat(player.prepare().isSuccess).isTrue()
|
||||
matchReadyState()
|
||||
}
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ class DefaultVoiceMessagePlayerTest {
|
|||
)
|
||||
player.state.test {
|
||||
matchInitialState()
|
||||
Truth.assertThat(player.prepare().isFailure).isTrue()
|
||||
assertThat(player.prepare().isFailure).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ class DefaultVoiceMessagePlayerTest {
|
|||
)
|
||||
player.state.test {
|
||||
matchInitialState()
|
||||
Truth.assertThat(player.prepare().isFailure).isTrue()
|
||||
assertThat(player.prepare().isFailure).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -76,12 +76,12 @@ class DefaultVoiceMessagePlayerTest {
|
|||
val player = createDefaultVoiceMessagePlayer()
|
||||
player.state.test {
|
||||
matchInitialState()
|
||||
Truth.assertThat(player.prepare().isSuccess).isTrue()
|
||||
assertThat(player.prepare().isSuccess).isTrue()
|
||||
matchReadyState()
|
||||
player.play()
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(true)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(1000)
|
||||
assertThat(it.isPlaying).isTrue()
|
||||
assertThat(it.currentPosition).isEqualTo(1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -96,15 +96,15 @@ class DefaultVoiceMessagePlayerTest {
|
|||
)
|
||||
player.state.test {
|
||||
matchInitialState()
|
||||
Truth.assertThat(player.prepare().isSuccess).isTrue()
|
||||
assertThat(player.prepare().isSuccess).isTrue()
|
||||
matchReadyState(fakeTotalDurationMs = 1000)
|
||||
player.play()
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it.isReady).isEqualTo(false)
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(false)
|
||||
Truth.assertThat(it.isEnded).isEqualTo(true)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(1000)
|
||||
Truth.assertThat(it.duration).isEqualTo(1000)
|
||||
assertThat(it.isReady).isFalse()
|
||||
assertThat(it.isPlaying).isFalse()
|
||||
assertThat(it.isEnded).isTrue()
|
||||
assertThat(it.currentPosition).isEqualTo(1000)
|
||||
assertThat(it.duration).isEqualTo(1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -121,72 +121,72 @@ class DefaultVoiceMessagePlayerTest {
|
|||
// Play player1 until the end.
|
||||
player1.state.test {
|
||||
matchInitialState()
|
||||
Truth.assertThat(player1.prepare().isSuccess).isTrue()
|
||||
assertThat(player1.prepare().isSuccess).isTrue()
|
||||
matchReadyState(1_000L)
|
||||
player1.play()
|
||||
awaitItem().let { // it plays until the end.
|
||||
Truth.assertThat(it.isReady).isEqualTo(false)
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(false)
|
||||
Truth.assertThat(it.isEnded).isEqualTo(true)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(1000)
|
||||
Truth.assertThat(it.duration).isEqualTo(1000)
|
||||
assertThat(it.isReady).isFalse()
|
||||
assertThat(it.isPlaying).isFalse()
|
||||
assertThat(it.isEnded).isTrue()
|
||||
assertThat(it.currentPosition).isEqualTo(1000)
|
||||
assertThat(it.duration).isEqualTo(1000)
|
||||
}
|
||||
}
|
||||
|
||||
// Play player2 until the end.
|
||||
player2.state.test {
|
||||
matchInitialState()
|
||||
Truth.assertThat(player2.prepare().isSuccess).isTrue()
|
||||
assertThat(player2.prepare().isSuccess).isTrue()
|
||||
awaitItem().let { // Additional spurious state due to MediaPlayer owner change.
|
||||
Truth.assertThat(it.isReady).isEqualTo(false)
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(false)
|
||||
Truth.assertThat(it.isEnded).isEqualTo(true)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(1000)
|
||||
Truth.assertThat(it.duration).isEqualTo(1000)
|
||||
assertThat(it.isReady).isFalse()
|
||||
assertThat(it.isPlaying).isFalse()
|
||||
assertThat(it.isEnded).isTrue()
|
||||
assertThat(it.currentPosition).isEqualTo(1000)
|
||||
assertThat(it.duration).isEqualTo(1000)
|
||||
}
|
||||
awaitItem().let {// Additional spurious state due to MediaPlayer owner change.
|
||||
Truth.assertThat(it.isReady).isEqualTo(false)
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(false)
|
||||
Truth.assertThat(it.isEnded).isEqualTo(false)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(0)
|
||||
Truth.assertThat(it.duration).isEqualTo(null)
|
||||
assertThat(it.isReady).isFalse()
|
||||
assertThat(it.isPlaying).isFalse()
|
||||
assertThat(it.isEnded).isFalse()
|
||||
assertThat(it.currentPosition).isEqualTo(0)
|
||||
assertThat(it.duration).isNull()
|
||||
}
|
||||
matchReadyState(1_000L)
|
||||
player2.play()
|
||||
awaitItem().let { // it plays until the end.
|
||||
Truth.assertThat(it.isReady).isEqualTo(false)
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(false)
|
||||
Truth.assertThat(it.isEnded).isEqualTo(true)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(1000)
|
||||
Truth.assertThat(it.duration).isEqualTo(1000)
|
||||
assertThat(it.isReady).isFalse()
|
||||
assertThat(it.isPlaying).isFalse()
|
||||
assertThat(it.isEnded).isTrue()
|
||||
assertThat(it.currentPosition).isEqualTo(1000)
|
||||
assertThat(it.duration).isEqualTo(1000)
|
||||
}
|
||||
}
|
||||
|
||||
// Play player1 again.
|
||||
player1.state.test {
|
||||
awaitItem().let {// Last previous state/
|
||||
Truth.assertThat(it.isReady).isEqualTo(false)
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(false)
|
||||
Truth.assertThat(it.isEnded).isEqualTo(true)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(1000)
|
||||
Truth.assertThat(it.duration).isEqualTo(1000)
|
||||
assertThat(it.isReady).isFalse()
|
||||
assertThat(it.isPlaying).isFalse()
|
||||
assertThat(it.isEnded).isTrue()
|
||||
assertThat(it.currentPosition).isEqualTo(1000)
|
||||
assertThat(it.duration).isEqualTo(1000)
|
||||
}
|
||||
Truth.assertThat(player1.prepare().isSuccess).isTrue()
|
||||
assertThat(player1.prepare().isSuccess).isTrue()
|
||||
awaitItem().let {// Additional spurious state due to MediaPlayer owner change.
|
||||
Truth.assertThat(it.isReady).isEqualTo(false)
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(false)
|
||||
Truth.assertThat(it.isEnded).isEqualTo(false)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(0)
|
||||
Truth.assertThat(it.duration).isEqualTo(null)
|
||||
assertThat(it.isReady).isFalse()
|
||||
assertThat(it.isPlaying).isFalse()
|
||||
assertThat(it.isEnded).isFalse()
|
||||
assertThat(it.currentPosition).isEqualTo(0)
|
||||
assertThat(it.duration).isNull()
|
||||
}
|
||||
matchReadyState(1_000L)
|
||||
player1.play()
|
||||
awaitItem().let { // it played again until the end.
|
||||
Truth.assertThat(it.isReady).isEqualTo(false)
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(false)
|
||||
Truth.assertThat(it.isEnded).isEqualTo(true)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(1000)
|
||||
Truth.assertThat(it.duration).isEqualTo(1000)
|
||||
assertThat(it.isReady).isFalse()
|
||||
assertThat(it.isPlaying).isFalse()
|
||||
assertThat(it.isEnded).isTrue()
|
||||
assertThat(it.currentPosition).isEqualTo(1000)
|
||||
assertThat(it.duration).isEqualTo(1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -196,14 +196,14 @@ class DefaultVoiceMessagePlayerTest {
|
|||
val player = createDefaultVoiceMessagePlayer()
|
||||
player.state.test {
|
||||
matchInitialState()
|
||||
Truth.assertThat(player.prepare().isSuccess).isTrue()
|
||||
assertThat(player.prepare().isSuccess).isTrue()
|
||||
matchReadyState()
|
||||
player.play()
|
||||
skipItems(1) // skip play state
|
||||
player.pause()
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(false)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(1000)
|
||||
assertThat(it.isPlaying).isFalse()
|
||||
assertThat(it.currentPosition).isEqualTo(1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -213,7 +213,7 @@ class DefaultVoiceMessagePlayerTest {
|
|||
val player = createDefaultVoiceMessagePlayer()
|
||||
player.state.test {
|
||||
matchInitialState()
|
||||
Truth.assertThat(player.prepare().isSuccess).isTrue()
|
||||
assertThat(player.prepare().isSuccess).isTrue()
|
||||
matchReadyState()
|
||||
player.play()
|
||||
skipItems(1) // skip play state
|
||||
|
|
@ -221,8 +221,8 @@ class DefaultVoiceMessagePlayerTest {
|
|||
skipItems(1) // skip pause state
|
||||
player.play()
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(true)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(2000)
|
||||
assertThat(it.isPlaying).isTrue()
|
||||
assertThat(it.currentPosition).isEqualTo(2000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -234,19 +234,19 @@ class DefaultVoiceMessagePlayerTest {
|
|||
matchInitialState()
|
||||
player.seekTo(2000)
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it.isReady).isEqualTo(false)
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(false)
|
||||
Truth.assertThat(it.isEnded).isEqualTo(false)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(2000)
|
||||
Truth.assertThat(it.duration).isEqualTo(null)
|
||||
assertThat(it.isReady).isFalse()
|
||||
assertThat(it.isPlaying).isFalse()
|
||||
assertThat(it.isEnded).isFalse()
|
||||
assertThat(it.currentPosition).isEqualTo(2000)
|
||||
assertThat(it.duration).isNull()
|
||||
}
|
||||
Truth.assertThat(player.prepare().isSuccess).isTrue()
|
||||
assertThat(player.prepare().isSuccess).isTrue()
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it.isReady).isEqualTo(true)
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(false)
|
||||
Truth.assertThat(it.isEnded).isEqualTo(false)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(2000)
|
||||
Truth.assertThat(it.duration).isEqualTo(FAKE_TOTAL_DURATION_MS)
|
||||
assertThat(it.isReady).isTrue()
|
||||
assertThat(it.isPlaying).isFalse()
|
||||
assertThat(it.isEnded).isFalse()
|
||||
assertThat(it.currentPosition).isEqualTo(2000)
|
||||
assertThat(it.duration).isEqualTo(FAKE_TOTAL_DURATION_MS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -256,15 +256,15 @@ class DefaultVoiceMessagePlayerTest {
|
|||
val player = createDefaultVoiceMessagePlayer()
|
||||
player.state.test {
|
||||
matchInitialState()
|
||||
Truth.assertThat(player.prepare().isSuccess).isTrue()
|
||||
assertThat(player.prepare().isSuccess).isTrue()
|
||||
matchReadyState()
|
||||
player.seekTo(2000)
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it.isReady).isEqualTo(true)
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(false)
|
||||
Truth.assertThat(it.isEnded).isEqualTo(false)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(2000)
|
||||
Truth.assertThat(it.duration).isEqualTo(FAKE_TOTAL_DURATION_MS)
|
||||
assertThat(it.isReady).isTrue()
|
||||
assertThat(it.isPlaying).isFalse()
|
||||
assertThat(it.isEnded).isFalse()
|
||||
assertThat(it.currentPosition).isEqualTo(2000)
|
||||
assertThat(it.duration).isEqualTo(FAKE_TOTAL_DURATION_MS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -296,11 +296,11 @@ private const val MXC_URI = "mxc://matrix.org/1234567890abcdefg"
|
|||
|
||||
private suspend fun TurbineTestContext<VoiceMessagePlayer.State>.matchInitialState() {
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it.isReady).isEqualTo(false)
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(false)
|
||||
Truth.assertThat(it.isEnded).isEqualTo(false)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(0)
|
||||
Truth.assertThat(it.duration).isEqualTo(null)
|
||||
assertThat(it.isReady).isFalse()
|
||||
assertThat(it.isPlaying).isFalse()
|
||||
assertThat(it.isEnded).isFalse()
|
||||
assertThat(it.currentPosition).isEqualTo(0)
|
||||
assertThat(it.duration).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -308,10 +308,10 @@ private suspend fun TurbineTestContext<VoiceMessagePlayer.State>.matchReadyState
|
|||
fakeTotalDurationMs: Long = FAKE_TOTAL_DURATION_MS,
|
||||
) {
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it.isReady).isEqualTo(true)
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(false)
|
||||
Truth.assertThat(it.isEnded).isEqualTo(false)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(0)
|
||||
Truth.assertThat(it.duration).isEqualTo(fakeTotalDurationMs)
|
||||
assertThat(it.isReady).isTrue()
|
||||
assertThat(it.isPlaying).isFalse()
|
||||
assertThat(it.isEnded).isFalse()
|
||||
assertThat(it.currentPosition).isEqualTo(0)
|
||||
assertThat(it.duration).isEqualTo(fakeTotalDurationMs)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package io.element.android.features.messages.impl.voicemessages.timeline
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
|
|
@ -44,13 +44,13 @@ class RedactedVoiceMessageManagerTest {
|
|||
}
|
||||
val manager = aDefaultRedactedVoiceMessageManager(mediaPlayer = mediaPlayer)
|
||||
|
||||
Truth.assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value)
|
||||
Truth.assertThat(mediaPlayer.state.value.isPlaying).isTrue()
|
||||
assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value)
|
||||
assertThat(mediaPlayer.state.value.isPlaying).isTrue()
|
||||
|
||||
manager.onEachMatrixTimelineItem(aRedactedMatrixTimeline(AN_EVENT_ID_2))
|
||||
|
||||
Truth.assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value)
|
||||
Truth.assertThat(mediaPlayer.state.value.isPlaying).isTrue()
|
||||
assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value)
|
||||
assertThat(mediaPlayer.state.value.isPlaying).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -61,13 +61,13 @@ class RedactedVoiceMessageManagerTest {
|
|||
}
|
||||
val manager = aDefaultRedactedVoiceMessageManager(mediaPlayer = mediaPlayer)
|
||||
|
||||
Truth.assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value)
|
||||
Truth.assertThat(mediaPlayer.state.value.isPlaying).isTrue()
|
||||
assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value)
|
||||
assertThat(mediaPlayer.state.value.isPlaying).isTrue()
|
||||
|
||||
manager.onEachMatrixTimelineItem(aRedactedMatrixTimeline(AN_EVENT_ID))
|
||||
|
||||
Truth.assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value)
|
||||
Truth.assertThat(mediaPlayer.state.value.isPlaying).isFalse()
|
||||
assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value)
|
||||
assertThat(mediaPlayer.state.value.isPlaying).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ package io.element.android.features.messages.impl.voicemessages.timeline
|
|||
import app.cash.molecule.RecompositionMode
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemVoiceContent
|
||||
import io.element.android.features.messages.impl.voicemessages.VoiceMessageException
|
||||
|
|
@ -39,9 +39,9 @@ class VoiceMessagePresenterTest {
|
|||
presenter.present()
|
||||
}.test {
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play)
|
||||
Truth.assertThat(it.progress).isEqualTo(0f)
|
||||
Truth.assertThat(it.time).isEqualTo("1:01")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play)
|
||||
assertThat(it.progress).isEqualTo(0f)
|
||||
assertThat(it.time).isEqualTo("1:01")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -56,27 +56,27 @@ class VoiceMessagePresenterTest {
|
|||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play)
|
||||
Truth.assertThat(it.progress).isEqualTo(0f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:02")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play)
|
||||
assertThat(it.progress).isEqualTo(0f)
|
||||
assertThat(it.time).isEqualTo("0:02")
|
||||
}
|
||||
|
||||
initialState.eventSink(VoiceMessageEvents.PlayPause)
|
||||
|
||||
awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading)
|
||||
Truth.assertThat(it.progress).isEqualTo(0f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:02")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading)
|
||||
assertThat(it.progress).isEqualTo(0f)
|
||||
assertThat(it.time).isEqualTo("0:02")
|
||||
}
|
||||
awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading)
|
||||
Truth.assertThat(it.progress).isEqualTo(0f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:00")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading)
|
||||
assertThat(it.progress).isEqualTo(0f)
|
||||
assertThat(it.time).isEqualTo("0:00")
|
||||
}
|
||||
awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause)
|
||||
Truth.assertThat(it.progress).isEqualTo(0.5f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:01")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause)
|
||||
assertThat(it.progress).isEqualTo(0.5f)
|
||||
assertThat(it.time).isEqualTo("0:01")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -94,25 +94,25 @@ class VoiceMessagePresenterTest {
|
|||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play)
|
||||
Truth.assertThat(it.progress).isEqualTo(0f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:02")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play)
|
||||
assertThat(it.progress).isEqualTo(0f)
|
||||
assertThat(it.time).isEqualTo("0:02")
|
||||
}
|
||||
|
||||
initialState.eventSink(VoiceMessageEvents.PlayPause)
|
||||
|
||||
awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading)
|
||||
Truth.assertThat(it.progress).isEqualTo(0f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:02")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading)
|
||||
assertThat(it.progress).isEqualTo(0f)
|
||||
assertThat(it.time).isEqualTo("0:02")
|
||||
}
|
||||
awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Retry)
|
||||
Truth.assertThat(it.progress).isEqualTo(0f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:02")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Retry)
|
||||
assertThat(it.progress).isEqualTo(0f)
|
||||
assertThat(it.time).isEqualTo("0:02")
|
||||
}
|
||||
analyticsService.trackedErrors.first().also {
|
||||
Truth.assertThat(it).apply {
|
||||
assertThat(it).apply {
|
||||
isInstanceOf(VoiceMessageException.PlayMessageError::class.java)
|
||||
hasMessageThat().isEqualTo("Error while trying to play voice message")
|
||||
}
|
||||
|
|
@ -130,25 +130,25 @@ class VoiceMessagePresenterTest {
|
|||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play)
|
||||
Truth.assertThat(it.progress).isEqualTo(0f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:02")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play)
|
||||
assertThat(it.progress).isEqualTo(0f)
|
||||
assertThat(it.time).isEqualTo("0:02")
|
||||
}
|
||||
|
||||
initialState.eventSink(VoiceMessageEvents.PlayPause)
|
||||
skipItems(2) // skip downloading states
|
||||
|
||||
val playingState = awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause)
|
||||
Truth.assertThat(it.progress).isEqualTo(0.5f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:01")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause)
|
||||
assertThat(it.progress).isEqualTo(0.5f)
|
||||
assertThat(it.time).isEqualTo("0:01")
|
||||
}
|
||||
|
||||
playingState.eventSink(VoiceMessageEvents.PlayPause)
|
||||
awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play)
|
||||
Truth.assertThat(it.progress).isEqualTo(0.5f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:01")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play)
|
||||
assertThat(it.progress).isEqualTo(0.5f)
|
||||
assertThat(it.time).isEqualTo("0:01")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -162,9 +162,9 @@ class VoiceMessagePresenterTest {
|
|||
presenter.present()
|
||||
}.test {
|
||||
awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Disabled)
|
||||
Truth.assertThat(it.progress).isEqualTo(0f)
|
||||
Truth.assertThat(it.time).isEqualTo("1:01")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Disabled)
|
||||
assertThat(it.progress).isEqualTo(0f)
|
||||
assertThat(it.time).isEqualTo("1:01")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -179,17 +179,17 @@ class VoiceMessagePresenterTest {
|
|||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play)
|
||||
Truth.assertThat(it.progress).isEqualTo(0f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:10")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play)
|
||||
assertThat(it.progress).isEqualTo(0f)
|
||||
assertThat(it.time).isEqualTo("0:10")
|
||||
}
|
||||
|
||||
initialState.eventSink(VoiceMessageEvents.Seek(0.5f))
|
||||
|
||||
awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play)
|
||||
Truth.assertThat(it.progress).isEqualTo(0.5f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:05")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play)
|
||||
assertThat(it.progress).isEqualTo(0.5f)
|
||||
assertThat(it.time).isEqualTo("0:05")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -203,9 +203,9 @@ class VoiceMessagePresenterTest {
|
|||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play)
|
||||
Truth.assertThat(it.progress).isEqualTo(0f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:10")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play)
|
||||
assertThat(it.progress).isEqualTo(0f)
|
||||
assertThat(it.time).isEqualTo("0:10")
|
||||
}
|
||||
|
||||
initialState.eventSink(VoiceMessageEvents.PlayPause)
|
||||
|
|
@ -213,17 +213,17 @@ class VoiceMessagePresenterTest {
|
|||
skipItems(2) // skip downloading states
|
||||
|
||||
awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause)
|
||||
Truth.assertThat(it.progress).isEqualTo(0.1f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:01")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause)
|
||||
assertThat(it.progress).isEqualTo(0.1f)
|
||||
assertThat(it.time).isEqualTo("0:01")
|
||||
}
|
||||
|
||||
initialState.eventSink(VoiceMessageEvents.Seek(0.5f))
|
||||
|
||||
awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause)
|
||||
Truth.assertThat(it.progress).isEqualTo(0.5f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:05")
|
||||
assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause)
|
||||
assertThat(it.progress).isEqualTo(0.5f)
|
||||
assertThat(it.time).isEqualTo("0:05")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue