Merge branch 'develop' into feature/fga/room-version-upgrade

This commit is contained in:
ganfra 2025-06-16 20:46:28 +02:00
commit e2b1ab2632
150 changed files with 2027 additions and 1121 deletions

View file

@ -76,8 +76,6 @@ import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.libraries.ui.utils.time.isTalkbackActive
import io.element.android.wysiwyg.link.Link
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
@ -225,7 +223,6 @@ private fun MessageShieldDialog(state: TimelineState) {
)
}
@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)
@Composable
private fun TimelinePrefetchingHelper(
lazyListState: LazyListState,

View file

@ -21,7 +21,6 @@ internal fun ATimelineItemEventRow(
timelineRoomInfo: TimelineRoomInfo = aTimelineRoomInfo(),
renderReadReceipts: Boolean = false,
isLastOutgoingMessage: Boolean = false,
isHighlighted: Boolean = false,
timelineProtectionState: TimelineProtectionState = aTimelineProtectionState(),
) = TimelineItemEventRow(
event = event,
@ -29,7 +28,6 @@ internal fun ATimelineItemEventRow(
renderReadReceipts = renderReadReceipts,
timelineProtectionState = timelineProtectionState,
isLastOutgoingMessage = isLastOutgoingMessage,
isHighlighted = isHighlighted,
onEventClick = {},
onLongClick = {},
onLinkClick = {},

View file

@ -7,7 +7,6 @@
package io.element.android.features.messages.impl.timeline.components
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
@ -59,7 +58,6 @@ private val avatarRadius = AvatarSize.TimelineSender.dp / 2
private const val BUBBLE_WIDTH_RATIO = 0.78f
private val MIN_BUBBLE_WIDTH = 80.dp
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MessageEventBubble(
state: BubbleState,
@ -184,7 +182,7 @@ internal fun MessageEventBubblePreview(@PreviewParameter(BubbleStateProvider::cl
contentAlignment = Alignment.Center,
) {
Text(
text = "${state.groupPosition.javaClass.simpleName} m:${state.isMine.to01()} h:${state.isHighlighted.to01()}",
text = "${state.groupPosition.javaClass.simpleName} isMine:${state.isMine.to01()}",
style = ElementTheme.typography.fontBodyXsRegular,
)
}

View file

@ -7,12 +7,8 @@
package io.element.android.features.messages.impl.timeline.components
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ripple
@ -28,17 +24,14 @@ import io.element.android.libraries.designsystem.theme.components.Surface
private val CORNER_RADIUS = 8.dp
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MessageStateEventContainer(
@Suppress("UNUSED_PARAMETER") isHighlighted: Boolean,
interactionSource: MutableInteractionSource,
onClick: () -> Unit,
onLongClick: () -> Unit,
modifier: Modifier = Modifier,
content: @Composable () -> Unit = {},
) {
// Ignore isHighlighted for now, we need a design decision on it.
val backgroundColor = Color.Transparent
val shape = RoundedCornerShape(CORNER_RADIUS)
Surface(
@ -60,22 +53,9 @@ fun MessageStateEventContainer(
@PreviewsDayNight
@Composable
internal fun MessageStateEventContainerPreview() = ElementPreview {
Column {
MessageStateEventContainer(
isHighlighted = false,
interactionSource = remember { MutableInteractionSource() },
onClick = {},
onLongClick = {},
) {
Spacer(modifier = Modifier.size(width = 120.dp, height = 32.dp))
}
MessageStateEventContainer(
isHighlighted = true,
interactionSource = remember { MutableInteractionSource() },
onClick = {},
onLongClick = {},
) {
Spacer(modifier = Modifier.size(width = 120.dp, height = 32.dp))
}
}
MessageStateEventContainer(
interactionSource = remember { MutableInteractionSource() },
onClick = {},
onLongClick = {},
)
}

View file

@ -9,7 +9,6 @@ package io.element.android.features.messages.impl.timeline.components
import androidx.annotation.DrawableRes
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.combinedClickable
@ -50,7 +49,6 @@ import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.ui.media.MediaRequestData
@Composable
@OptIn(ExperimentalFoundationApi::class)
@Suppress("ModifierClickableOrder") // This is needed to display the right ripple shape
fun MessagesReactionButton(
onClick: () -> Unit,

View file

@ -7,7 +7,6 @@
package io.element.android.features.messages.impl.timeline.components
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.border
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
@ -39,7 +38,6 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.text.toDp
import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalFoundationApi::class)
@Composable
internal fun TimelineItemCallNotifyView(
event: TimelineItem.Event,

View file

@ -32,7 +32,6 @@ import androidx.compose.runtime.movableContentOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalViewConfiguration
@ -120,7 +119,6 @@ fun TimelineItemEventRow(
timelineProtectionState: TimelineProtectionState,
renderReadReceipts: Boolean,
isLastOutgoingMessage: Boolean,
isHighlighted: Boolean,
onEventClick: () -> Unit,
onLongClick: () -> Unit,
onLinkClick: (Link) -> Unit,
@ -196,7 +194,6 @@ fun TimelineItemEventRow(
TimelineItemEventRowContent(
event = event,
timelineProtectionState = timelineProtectionState,
isHighlighted = isHighlighted,
timelineRoomInfo = timelineRoomInfo,
interactionSource = interactionSource,
onContentClick = onContentClick,
@ -230,7 +227,6 @@ fun TimelineItemEventRow(
TimelineItemEventRowContent(
event = event,
timelineProtectionState = timelineProtectionState,
isHighlighted = isHighlighted,
timelineRoomInfo = timelineRoomInfo,
interactionSource = interactionSource,
onContentClick = onContentClick,
@ -281,12 +277,10 @@ private fun SwipeSensitivity(
}
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun TimelineItemEventRowContent(
event: TimelineItem.Event,
timelineProtectionState: TimelineProtectionState,
isHighlighted: Boolean,
timelineRoomInfo: TimelineRoomInfo,
interactionSource: MutableInteractionSource,
onContentClick: () -> Unit,
@ -340,7 +334,6 @@ private fun TimelineItemEventRowContent(
val bubbleState = BubbleState(
groupPosition = event.groupPosition,
isMine = event.isMine,
isHighlighted = isHighlighted,
timelineRoomInfo = timelineRoomInfo,
)
MessageEventBubble(

View file

@ -7,7 +7,6 @@
package io.element.android.features.messages.impl.timeline.components
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
@ -21,6 +20,7 @@ import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.isTraversalGroup
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@ -32,6 +32,7 @@ import io.element.android.features.messages.impl.timeline.components.layout.Cont
import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemCallNotifyContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLegacyCallInviteContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionEvent
@ -48,7 +49,6 @@ import io.element.android.libraries.ui.utils.time.isTalkbackActive
import io.element.android.wysiwyg.link.Link
import kotlin.time.DurationUnit
@OptIn(ExperimentalFoundationApi::class)
@Composable
internal fun TimelineItemRow(
timelineItem: TimelineItem,
@ -113,7 +113,6 @@ internal fun TimelineItemRow(
event = timelineItem,
renderReadReceipts = renderReadReceipts,
isLastOutgoingMessage = isLastOutgoingMessage,
isHighlighted = timelineItem.isEvent(focusedEventId),
onClick = { onContentClick(timelineItem) },
onReadReceiptsClick = onReadReceiptClick,
onLongClick = { onLongClick(timelineItem) },
@ -140,6 +139,8 @@ internal fun TimelineItemRow(
} else {
timelineItem.safeSenderName
}
// For Polls, allow the answers to be traversed by Talkback
isTraversalGroup = timelineItem.content is TimelineItemPollContent
}
// Custom clickable that applies over the whole item for accessibility
.then(
@ -157,7 +158,6 @@ internal fun TimelineItemRow(
renderReadReceipts = renderReadReceipts,
timelineProtectionState = timelineProtectionState,
isLastOutgoingMessage = isLastOutgoingMessage,
isHighlighted = timelineItem.isEvent(focusedEventId),
onEventClick = { onContentClick(timelineItem) },
onLongClick = { onLongClick(timelineItem) },
onLinkClick = onLinkClick,

View file

@ -40,7 +40,6 @@ fun TimelineItemStateEventRow(
event: TimelineItem.Event,
renderReadReceipts: Boolean,
isLastOutgoingMessage: Boolean,
isHighlighted: Boolean,
onClick: () -> Unit,
onLongClick: () -> Unit,
onReadReceiptsClick: (event: TimelineItem.Event) -> Unit,
@ -60,7 +59,6 @@ fun TimelineItemStateEventRow(
contentAlignment = Alignment.Center
) {
MessageStateEventContainer(
isHighlighted = isHighlighted,
interactionSource = interactionSource,
onClick = onClick,
onLongClick = onLongClick,
@ -107,7 +105,6 @@ internal fun TimelineItemStateEventRowPreview() = ElementPreview {
),
renderReadReceipts = true,
isLastOutgoingMessage = false,
isHighlighted = false,
onClick = {},
onLongClick = {},
onReadReceiptsClick = {},

View file

@ -8,7 +8,6 @@
package io.element.android.features.messages.impl.timeline.components.event
import android.text.SpannedString
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Column
@ -57,7 +56,6 @@ import io.element.android.libraries.ui.utils.time.isTalkbackActive
import io.element.android.wysiwyg.compose.EditorStyledText
import io.element.android.wysiwyg.link.Link
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun TimelineItemImageView(
content: TimelineItemImageContent,

View file

@ -7,7 +7,6 @@
package io.element.android.features.messages.impl.timeline.components.event
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Column
@ -39,7 +38,6 @@ import io.element.android.libraries.ui.strings.CommonStrings
private const val STICKER_SIZE_IN_DP = 128
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun TimelineItemStickerView(
content: TimelineItemStickerContent,

View file

@ -8,7 +8,6 @@
package io.element.android.features.messages.impl.timeline.components.event
import android.text.SpannedString
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
@ -67,7 +66,6 @@ import io.element.android.libraries.ui.utils.time.isTalkbackActive
import io.element.android.wysiwyg.compose.EditorStyledText
import io.element.android.wysiwyg.link.Link
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun TimelineItemVideoView(
content: TimelineItemVideoContent,

View file

@ -13,7 +13,6 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItemGrou
data class BubbleState(
val groupPosition: TimelineItemGroupPosition,
val isMine: Boolean,
val isHighlighted: Boolean,
val timelineRoomInfo: TimelineRoomInfo,
) {
/** True to cut out the top start corner of the bubble, to give margin for the sender avatar. */

View file

@ -21,15 +21,11 @@ open class BubbleStateProvider : PreviewParameterProvider<BubbleState> {
TimelineItemGroupPosition.None,
).map { groupPosition ->
sequenceOf(false, true).map { isMine ->
sequenceOf(false, true).map { isHighlighted ->
aBubbleState(
groupPosition = groupPosition,
isMine = isMine,
isHighlighted = isHighlighted,
)
}
aBubbleState(
groupPosition = groupPosition,
isMine = isMine,
)
}
.flatten()
}
.flatten()
}
@ -37,11 +33,9 @@ open class BubbleStateProvider : PreviewParameterProvider<BubbleState> {
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,
)

View file

@ -35,6 +35,15 @@
<string name="screen_room_timeline_less_reactions">"Afficher moins"</string>
<string name="screen_room_timeline_message_copied">"Message copié"</string>
<string name="screen_room_timeline_no_permission_to_post">"Vous nêtes pas autorisé à publier dans ce salon"</string>
<plurals name="screen_room_timeline_reaction_a11y">
<item quantity="one">"%1$d membre a réagi avec %2$s"</item>
<item quantity="other">"%1$d membres ont réagi avec %2$s"</item>
</plurals>
<plurals name="screen_room_timeline_reaction_including_you_a11y">
<item quantity="one">"Vous et %1$d membre avez réagi avec %2$s"</item>
<item quantity="other">"Vous et %1$d membres avez réagi avec %2$s"</item>
</plurals>
<string name="screen_room_timeline_reaction_you_a11y">"Vous avez réagi avec %1$s"</string>
<string name="screen_room_timeline_reactions_show_less">"Afficher moins"</string>
<string name="screen_room_timeline_reactions_show_more">"Afficher plus"</string>
<string name="screen_room_timeline_read_marker_title">"Nouveau"</string>

View file

@ -35,6 +35,15 @@
<string name="screen_room_timeline_less_reactions">"Vis mindre"</string>
<string name="screen_room_timeline_message_copied">"Melding kopiert"</string>
<string name="screen_room_timeline_no_permission_to_post">"Du har ikke tillatelse til å legge ut innlegg i dette rommet"</string>
<plurals name="screen_room_timeline_reaction_a11y">
<item quantity="one">"%1$d medlem reagerte med %2$s"</item>
<item quantity="other">"%1$d medlemmer reagerte med %2$s"</item>
</plurals>
<plurals name="screen_room_timeline_reaction_including_you_a11y">
<item quantity="one">"Du og %1$d medlem reagerte med%2$s"</item>
<item quantity="other">"Du og %1$d medlemmer reagerte med%2$s"</item>
</plurals>
<string name="screen_room_timeline_reaction_you_a11y">"Du reagerte med %1$s"</string>
<string name="screen_room_timeline_reactions_show_less">"Vis mindre"</string>
<string name="screen_room_timeline_reactions_show_more">"Vis mer"</string>
<string name="screen_room_timeline_read_marker_title">"Ny"</string>

View file

@ -28,13 +28,22 @@
<string name="screen_room_mentions_at_room_title">"Everyone"</string>
<string name="screen_room_retry_send_menu_send_again_action">"Send again"</string>
<string name="screen_room_retry_send_menu_title">"Your message failed to send"</string>
<string name="screen_room_timeline_add_reaction">"Add emoji"</string>
<string name="screen_room_timeline_add_reaction">"Add a reaction"</string>
<string name="screen_room_timeline_beginning_of_room">"This is the beginning of %1$s."</string>
<string name="screen_room_timeline_beginning_of_room_no_name">"This is the beginning of this conversation."</string>
<string name="screen_room_timeline_legacy_call">"Unsupported call. Ask if the caller can use the new Element X app."</string>
<string name="screen_room_timeline_less_reactions">"Show less"</string>
<string name="screen_room_timeline_message_copied">"Message copied"</string>
<string name="screen_room_timeline_no_permission_to_post">"You do not have permission to post to this room"</string>
<plurals name="screen_room_timeline_reaction_a11y">
<item quantity="one">"%1$d member reacted with %2$s"</item>
<item quantity="other">"%1$d members reacted with %2$s"</item>
</plurals>
<plurals name="screen_room_timeline_reaction_including_you_a11y">
<item quantity="one">"You and %1$d member reacted with %2$s"</item>
<item quantity="other">"You and %1$d members reacted with %2$s"</item>
</plurals>
<string name="screen_room_timeline_reaction_you_a11y">"You reacted with %1$s"</string>
<string name="screen_room_timeline_reactions_show_less">"Show less"</string>
<string name="screen_room_timeline_reactions_show_more">"Show more"</string>
<string name="screen_room_timeline_read_marker_title">"New"</string>

View file

@ -47,7 +47,10 @@ class TimelineItemPollViewTest {
)
}
val answer = content.answerItems[answerIndex].answer
rule.onNode(hasText(answer.text)).performClick()
rule.onNode(
matcher = hasText(answer.text),
useUnmergedTree = true,
).performClick()
eventsRecorder.assertSingle(TimelineEvents.SelectPollAnswer(content.eventId!!, answer.id))
}