From a1c482d673b4576e2d2f1c4059d2e99b22c0719c Mon Sep 17 00:00:00 2001 From: David Langley Date: Tue, 18 Jul 2023 21:18:27 +0100 Subject: [PATCH 001/251] Add custom reaction layout - Add the custom reaction layout that only shows the expand UI after 2 lines. - It also enforces that the add more and expand buttons are always on the same line. - In LTR languages we want an incoming message's reactions to be LRT and outgoing to be RTL. - For RTL languages it should be the opposite. --- .../components/TimelineItemEventRow.kt | 2 +- .../components/TimelineItemReactionsLayout.kt | 207 ++++++++++++++++++ .../components/TimelineItemReactionsView.kt | 177 ++++++--------- 3 files changed, 274 insertions(+), 112 deletions(-) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt index 8d7ff263e8..f783e19563 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt @@ -291,7 +291,7 @@ private fun TimelineItemEventRowContent( if (event.reactionsState.reactions.isNotEmpty()) { TimelineItemReactions( reactionsState = event.reactionsState, - mainAxisAlignment = if (event.isMine) FlowMainAxisAlignment.End else FlowMainAxisAlignment.Start, + isOutgoing = event.isMine, onReactionClicked = onReactionClicked, onMoreReactionsClicked = { onMoreReactionsClicked(event) }, modifier = Modifier diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt new file mode 100644 index 0000000000..8f3f5865ea --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt @@ -0,0 +1,207 @@ +/* + * 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.material.icons.Icons +import androidx.compose.material.icons.outlined.AddReaction +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.layout.Placeable +import androidx.compose.ui.layout.SubcomposeLayout +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.dp +import io.element.android.features.messages.impl.R +import io.element.android.libraries.designsystem.preview.DayNightPreviews +import io.element.android.libraries.designsystem.preview.ElementPreview + +/** + * A flow layout for reactions that will show a collapse/expand button when the layout wraps over a defined number of rows. + * It displays an add more button when there are greater than 0 reactions and always displays the reaction and add more button + * on the same row (moving them both to a new row if necessary). + * @param itemSpacing The horizontal spacing between items + * @param rowSpacing The vertical spacing between rows + * @param expanded Whether the layout should display in expanded or collapsed state + * @param rowsBeforeCollapsible The number of rows before the collapse/expand button is shown + * @param expandButton The expand button + * @param addMoreButton The add more button + * @param reactions The reaction buttons + */ +@Composable +fun TimelineItemReactionsLayout( + modifier: Modifier = Modifier, + itemSpacing: Dp = 0.dp, + rowSpacing: Dp = 0.dp, + expanded: Boolean = false, + rowsBeforeCollapsible: Int? = 2, + expandButton: @Composable () -> Unit, + addMoreButton: @Composable () -> Unit, + reactions: @Composable () -> Unit +) { + SubcomposeLayout(modifier) { constraints -> + // Given the placeables and returns a structure representing + // how they should wrap on to multiple rows given the constraints max width. + fun calculateRows(measurables: List): List> { + val rows = mutableListOf>() + var currentRow = mutableListOf() + var rowX = 0 + + measurables.forEach { placeable -> + val horizontalSpacing = if (currentRow.isEmpty()) 0 else itemSpacing.toPx().toInt() + // If the current view does not fine on this row bump to the next + if (rowX + placeable.width > constraints.maxWidth) { + rows.add(currentRow) + currentRow = mutableListOf() + rowX = 0 + } + rowX += horizontalSpacing + placeable.width + currentRow.add(placeable) + } + // If there are items in the current row remember to append it to the returned value + if (currentRow.size > 0) { + rows.add(currentRow) + } + return rows + } + + // Used to render the collapsed state, this takes the rows inputted and adds the extra button to the last row, + // removing only as many trailing reactions as needed to make space for it. + fun replaceTrailingItemsWithButtons(rowsIn: List>, expandButton: Placeable, addMoreButton: Placeable): List> { + val rows = rowsIn.toMutableList() + val lastRow = rows[rows.size - 1] + val buttonsWidth = expandButton.width + itemSpacing.toPx().toInt() + addMoreButton.width + var rowX = 0 + lastRow.forEachIndexed { i, placeable -> + val horizontalSpacing = if (i == 0) 0 else itemSpacing.toPx().toInt() + rowX += placeable.width + horizontalSpacing + if (rowX > (constraints.maxWidth - (buttonsWidth + horizontalSpacing))) { + val lastRowWithButton = lastRow.take(i) + listOf(expandButton, addMoreButton) + rows[rows.size - 1] = lastRowWithButton + return rows + } + } + val lastRowWithButton = lastRow + listOf(expandButton, addMoreButton) + rows[rows.size - 1] = lastRowWithButton + return rows + } + + // To prevent the add more and expand buttons from wrapping on to separate lines. + // If there is one item on the last line, it moves the expand button down. + fun ensureCollapseAndAddMoreButtonsAreOnTheSameRow(rowsIn: List>): List> { + val lastRow = rowsIn.last().toMutableList() + if (lastRow.size != 1) { + return rowsIn + } + val rows = rowsIn.toMutableList() + val secondLastRow = rows[rows.size - 2].toMutableList() + val expandButtonPlaceable = secondLastRow.removeLast() + lastRow.add(0, expandButtonPlaceable) + rows[rows.size - 2] = secondLastRow + rows[rows.size - 1] = lastRow + return rows + } + + /// Given a list of rows place them in the layout. + fun layoutRows(rows: List>): MeasureResult { + var width = 0 + var height = 0 + val placeables = rows.mapIndexed { i, row -> + var rowX = 0 + var rowHeight = 0 + val verticalSpacing = if (i == 0) 0 else rowSpacing.toPx().toInt() + val rowWithPoints = row.mapIndexed { j, placeable -> + val horizontalSpacing = if (j == 0) 0 else itemSpacing.toPx().toInt() + val point = IntOffset(rowX + horizontalSpacing, height + verticalSpacing) + rowX += placeable.width + horizontalSpacing + rowHeight = maxOf(rowHeight, placeable.height) + Pair(placeable, point) + } + height += rowHeight + verticalSpacing + width = maxOf(width, rowX) + rowWithPoints + }.flatten() + + return layout(width = width, height = height) { + placeables.forEach { + val (placeable, origin) = it + placeable.placeRelative(origin.x, origin.y) + } + } + } + + val reactionsPlaceables = subcompose(0, reactions).map { it.measure(constraints) } + if (reactionsPlaceables.isEmpty()) { + return@SubcomposeLayout layoutRows(listOf()) + } + val addMorePlaceable = subcompose(1, addMoreButton).first().measure(constraints) + val expandPlaceable = subcompose(2, expandButton).first().measure(constraints) + + // Calculate the layout of the rows with the reactions button and add more button + val reactionsAndAddMore = calculateRows(reactionsPlaceables + listOf(addMorePlaceable)) + // If we have extended beyond the defined number of rows we are showing the expand/collapse ui + if (rowsBeforeCollapsible?.let { reactionsAndAddMore.size > it } == true) { + if (expanded) { + // Show all subviews with the add more button at the end + var reactionsAndButtons = calculateRows(reactionsPlaceables + listOf(expandPlaceable, addMorePlaceable)) + reactionsAndButtons = ensureCollapseAndAddMoreButtonsAreOnTheSameRow(reactionsAndButtons) + layoutRows(reactionsAndButtons) + } else { + // Truncate to `rowsBeforeCollapsible` number of rows and replace the reactions at the end of the last row with the buttons + val collapsedRows = reactionsAndAddMore.take(rowsBeforeCollapsible) + val collapsedRowsWithButtons = replaceTrailingItemsWithButtons(collapsedRows, expandPlaceable, addMorePlaceable) + layoutRows(collapsedRowsWithButtons) + } + } else { + // Otherwise we are just showing all items without the expand button + layoutRows(reactionsAndAddMore) + } + } +} + +@DayNightPreviews +@Composable +internal fun TimelineItemReactionsLayoutPreview() = ElementPreview { + TimelineItemReactionsLayout( + expanded = false, + expandButton = { + MessagesReactionButton( + content = MessagesReactionsButtonContent.Text( + text = stringResource(id = R.string.screen_room_timeline_less_reactions) + ), + onClick = { }, + ) + }, + addMoreButton = { + MessagesReactionButton( + content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction), + onClick = {} + ) + }, + reactions = { + io.element.android.features.messages.impl.timeline.aTimelineItemReactions(count = 18).reactions.forEach { + MessagesReactionButton( + content = MessagesReactionsButtonContent.Reaction( + it + ), + onClick = {} + ) + } + } + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt index ca0a58da70..962d3a2b2b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt @@ -19,18 +19,16 @@ package io.element.android.features.messages.impl.timeline.components import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.AddReaction import androidx.compose.runtime.Composable -import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp -import com.google.accompanist.flowlayout.FlowMainAxisAlignment -import com.google.accompanist.flowlayout.FlowRow import io.element.android.features.messages.impl.R import io.element.android.features.messages.impl.timeline.aTimelineItemReactions import io.element.android.features.messages.impl.timeline.model.AggregatedReaction @@ -38,162 +36,119 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItemReac import io.element.android.libraries.designsystem.preview.DayNightPreviews import io.element.android.libraries.designsystem.preview.ElementPreview import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toPersistentList - -/** - * The maximum number of items that can be displayed before some items will be hidden - * - * TODO The threshold should be based on the number of rows, rather than items. - * Once items would spill onto a third row, they should be hidden. - * Note this could be particularly worthwhile to handle reactions that are - * longer than a single character (as annotation keys are free text). - */ -private const val COLLAPSE_ITEMS_THRESHOLD = 8 @Composable fun TimelineItemReactions( reactionsState: TimelineItemReactions, - mainAxisAlignment: FlowMainAxisAlignment, + isOutgoing: Boolean, onReactionClicked: (emoji: String) -> Unit, onMoreReactionsClicked: () -> Unit, modifier: Modifier = Modifier, ) { var expanded: Boolean by rememberSaveable { mutableStateOf(false) } - val reactions by remember(reactionsState, expanded) { - derivedStateOf { - val numToDisplay = if (expanded) { - reactionsState.reactions.count() - } else { - COLLAPSE_ITEMS_THRESHOLD - } - reactionsState.reactions.take(numToDisplay).toPersistentList() - } + // In LTR languages we want an incoming message's reactions to be LRT and outgoing to be RTL. + // For RTL languages it should be the opposite. + val reactionsLayoutDirection = if (!isOutgoing) LocalLayoutDirection.current + else if (LocalLayoutDirection.current == LayoutDirection.Ltr) + LayoutDirection.Rtl + else + LayoutDirection.Ltr + + CompositionLocalProvider(LocalLayoutDirection provides reactionsLayoutDirection) { + TimelineItemReactionsView( + modifier = modifier, + reactions = reactionsState.reactions, + expanded = expanded, + onReactionClick = onReactionClicked, + onMoreReactionsClick = onMoreReactionsClicked, + onToggleExpandClick = { expanded = !expanded }, + ) } - - val expandableState by remember { - derivedStateOf { - if (expanded) { - ExpandableState.Expanded - } else { - val hiddenItems = reactionsState.reactions.count() - reactions.count() - if (hiddenItems > 0) { - ExpandableState.Collapsed(hidden = hiddenItems) - } else { - ExpandableState.None - } - } - } - } - - TimelineItemReactionsView( - modifier = modifier, - reactions = reactions, - expandableState = expandableState, - mainAxisAlignment = mainAxisAlignment, - onReactionClick = onReactionClicked, - onMoreReactionsClick = onMoreReactionsClicked, - onExpandClick = { expanded = true }, - onCollapseClick = { expanded = false } - ) -} - -private sealed class ExpandableState { - object None: ExpandableState() - data class Collapsed(val hidden: Int): ExpandableState() - object Expanded : ExpandableState() } @Composable private fun TimelineItemReactionsView( reactions: ImmutableList, - expandableState: ExpandableState, - mainAxisAlignment: FlowMainAxisAlignment, + expanded: Boolean, onReactionClick: (emoji: String) -> Unit, onMoreReactionsClick: () -> Unit, - onExpandClick: () -> Unit, - onCollapseClick: () -> Unit, + onToggleExpandClick: () -> Unit, modifier: Modifier = Modifier -) = - FlowRow( - modifier = modifier, - mainAxisSpacing = 4.dp, - crossAxisSpacing = 4.dp, - mainAxisAlignment = mainAxisAlignment, - ) { +) = TimelineItemReactionsLayout( + modifier = modifier, + itemSpacing = 4.dp, + rowSpacing = 4.dp, + expanded = expanded, + expandButton = { + MessagesReactionButton( + content = MessagesReactionsButtonContent.Text( + text = stringResource(id = if (expanded) R.string.screen_room_reactions_show_less else R.string.screen_room_reactions_show_more) + ), + onClick = onToggleExpandClick, + ) + }, + addMoreButton = { + MessagesReactionButton( + content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction), + onClick = onMoreReactionsClick + ) + }, + reactions = { reactions.forEach { reaction -> MessagesReactionButton( content = MessagesReactionsButtonContent.Reaction(reaction = reaction), onClick = { onReactionClick(reaction.key) } ) } - when (expandableState) { - ExpandableState.Expanded -> - MessagesReactionButton( - content = MessagesReactionsButtonContent.Text( - text = stringResource(id = R.string.screen_room_timeline_less_reactions) - ), - onClick = onCollapseClick, - ) - is ExpandableState.Collapsed -> { - val hidden = expandableState.hidden - MessagesReactionButton( - content = MessagesReactionsButtonContent.Text( - text = pluralStringResource(id = R.plurals.screen_room_timeline_more_reactions, hidden, hidden) - ), - onClick = onExpandClick, - ) - } - ExpandableState.None -> { - // No expand or collapse action available - } - } - MessagesReactionButton( - content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction), - onClick = onMoreReactionsClick - ) } +) @DayNightPreviews @Composable fun TimelineItemReactionsViewPreview() = ElementPreview { ContentToPreview( - reactions = aTimelineItemReactions(count = 1).reactions, - expandableState = ExpandableState.None, + reactions = aTimelineItemReactions(count = 1).reactions ) } @DayNightPreviews @Composable -fun TimelineItemReactionsViewCollapsedPreview() = ElementPreview { +fun TimelineItemReactionsViewFewPreview() = ElementPreview { ContentToPreview( - reactions = aTimelineItemReactions(count = 3).reactions, - expandableState = ExpandableState.Collapsed(hidden = 7), + reactions = aTimelineItemReactions(count = 3).reactions ) } @DayNightPreviews @Composable -fun TimelineItemReactionsViewExpandedPreview() = ElementPreview { +fun TimelineItemReactionsViewIncomingPreview() = ElementPreview { ContentToPreview( - reactions = aTimelineItemReactions(count = 10).reactions, - expandableState = ExpandableState.Expanded, + reactions = aTimelineItemReactions(count = 18).reactions + ) +} + +@DayNightPreviews +@Composable +fun TimelineItemReactionsViewOutgoingPreview() = ElementPreview { + ContentToPreview( + reactions = aTimelineItemReactions(count = 18).reactions, + isOutgoing = true ) } @Composable private fun ContentToPreview( reactions: ImmutableList, - expandableState: ExpandableState + isOutgoing: Boolean = false ) { - TimelineItemReactionsView( - reactions = reactions, - expandableState = expandableState, - mainAxisAlignment = FlowMainAxisAlignment.Center, - onReactionClick = {}, - onMoreReactionsClick = {}, - onExpandClick = {}, - onCollapseClick = {} + TimelineItemReactions( + reactionsState = TimelineItemReactions( + reactions + ), + isOutgoing = isOutgoing, + onReactionClicked = {}, + onMoreReactionsClicked = {}, ) } From b0c680e1c6ec60aa66d343521f682db436b1e48e Mon Sep 17 00:00:00 2001 From: David Langley Date: Tue, 18 Jul 2023 21:39:37 +0100 Subject: [PATCH 002/251] Add more button should have primary text colour --- .../messages/impl/timeline/components/MessagesReactionButton.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt index 8d2eb06f99..083bfcb7b5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt @@ -117,6 +117,7 @@ private fun TextContent( .height(reactionEmojiLineHeight.toDp()), text = text, style = ElementTheme.typography.fontBodyMdRegular, + color = MaterialTheme.colorScheme.primary ) @Composable From a0af09b79f632be3ac72e20c5f29c853105d1c18 Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 19 Jul 2023 09:28:02 +0100 Subject: [PATCH 003/251] Update screenshots --- ...ineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png | 3 +++ ...ineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png | 3 +++ ...ineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png | 3 +++ ...ineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png | 3 +++ ...ineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png | 3 +++ ...ineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png | 3 +++ ...ItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png | 3 +++ ...ItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png | 3 +++ ...ItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png | 3 +++ ...ItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png | 3 +++ ...dHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png | 3 +++ ...dHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png | 3 +++ ...ReactionExtraButtonsPreview-D-4_5_null,NEXUS_5,1.0,en].png | 4 ++-- ...ReactionExtraButtonsPreview-N-4_6_null,NEXUS_5,1.0,en].png | 4 ++-- ...TimelineItemEventRowDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...imelineItemEventRowLightPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...RowWithManyReactionsDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...owWithManyReactionsLightPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...temEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...emEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...eItemReactionsLayoutPreview-D-5_6_null,NEXUS_5,1.0,en].png | 3 +++ ...eItemReactionsLayoutPreview-N-5_7_null,NEXUS_5,1.0,en].png | 3 +++ ...ItemReactionsViewFewPreview-D-7_8_null,NEXUS_5,1.0,en].png | 3 +++ ...ItemReactionsViewFewPreview-N-7_9_null,NEXUS_5,1.0,en].png | 3 +++ ...eactionsViewIncomingPreview-D-8_9_null,NEXUS_5,1.0,en].png | 3 +++ ...actionsViewIncomingPreview-N-8_10_null,NEXUS_5,1.0,en].png | 3 +++ ...actionsViewOutgoingPreview-D-9_10_null,NEXUS_5,1.0,en].png | 3 +++ ...actionsViewOutgoingPreview-N-9_11_null,NEXUS_5,1.0,en].png | 3 +++ ...ineItemReactionsViewPreview-D-6_7_null,NEXUS_5,1.0,en].png | 3 +++ ...ineItemReactionsViewPreview-N-6_8_null,NEXUS_5,1.0,en].png | 3 +++ ...Group_TimelineViewPreview-D-2_3_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewPreview-D-2_3_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewPreview-D-2_3_null_10,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewPreview-D-2_3_null_11,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewPreview-D-2_3_null_12,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewPreview-D-2_3_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewPreview-D-2_3_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewPreview-D-2_3_null_8,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewPreview-D-2_3_null_9,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewPreview-N-2_4_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewPreview-N-2_4_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewPreview-N-2_4_null_10,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewPreview-N-2_4_null_11,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewPreview-N-2_4_null_12,NEXUS_5,1.0,en].png | 2 +- ...Group_TimelineViewPreview-N-2_4_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewPreview-N-2_4_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewPreview-N-2_4_null_8,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewPreview-N-2_4_null_9,NEXUS_5,1.0,en].png | 4 ++-- ...Group_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...Group_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...Group_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...Group_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...Group_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...roup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...roup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...roup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...roup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...roup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- 58 files changed, 137 insertions(+), 71 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-D-5_6_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-N-5_7_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-D-7_8_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-N-7_9_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-D-8_9_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-N-8_10_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-D-9_10_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-N-9_11_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-6_7_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-6_8_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..53bfbe6d54 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e00510a5f35eb33aaef15c30e6caac62045d8e4c37c032a24922bbb749ad0375 +size 9817 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9ce9173802 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:229d83ed02804137ab1dad4114eecc6d52d6a0e3ccbad436cf226f6cfc628cf7 +size 12200 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..77a65f3b40 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27fff9f0ea88cf06934298ea6155cbf4dd49c370cc5d0a14b4d387ac8a7e7c39 +size 23203 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c02a4d8bf8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba5f5fefedaaf994fb2eae3df38724b245e27a1b03d48cb20d33f7ca49caa356 +size 9458 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d636a6b668 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:19ce1d8ddd69760417e267cda7d4663b44d7a50f8ef1a5617497e1135f8aa586 +size 11500 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6db46605ae --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e589a013be7a4b54e3f91816565dd38a05086c19d9832480713d895b1462e72 +size 20864 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6bdd02b8f4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7cf8ad6c8f8da98fdfc7a9d5c47e959aaa612dfc5192dd93b589c783d4e94fd6 +size 155690 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fbfd3b1b30 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ae5e86ad1728027f7189041745592d4ed24df07e008b5798b29eadba5449569 +size 159493 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8b8209ffcc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b4c033b42d6e7fcd4fece8b8023ee07b1d0477e5bdff338c353903c83da62211 +size 76073 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4794defdd9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56875cfd798c1f5993c6630bc7183dab367913f7c58753434a7d4a08763eac48 +size 80041 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..45593d6af2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c806ee0293d94ab1c882b0f0ceaac2c7ac69d587f47cc22b4d16bc331351a2e +size 14711 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c74bbe95f8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:77c97c6c99943858530219234356459a7f0a88774332a4f11cd738b92ee15c91 +size 14200 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-4_5_null,NEXUS_5,1.0,en].png index d732d1c37b..37cce233e5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-4_5_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-4_5_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:724bc9b0db8f581119201a8db3c405c9a7e7261cccea45a354fd37a5e33fcb41 -size 11141 +oid sha256:6c73a80923ec5b269bbb5f5108dde9617dac39e9c51f9199ab9229c4dd7ca2b0 +size 11532 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-4_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-4_6_null,NEXUS_5,1.0,en].png index 9b08510703..c122f1e1f8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-4_6_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-4_6_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:109a1a9c6359a9fd7fe4258f022e845c9b8142f9e590a03e5c2efa9273cb572f -size 10742 +oid sha256:9eee3a5873231554cc0335fe7b552a62459f35e4ffb8c43b91a9746b34314bdc +size 11171 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowDarkPreview_0_null,NEXUS_5,1.0,en].png index 3fc8c841f2..fa1f71720b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:38c6d3f4a47ed89c3deb4150024b49c0a533a859f21a1592a82309b8c7316ea4 -size 152242 +oid sha256:85f9960cebee2e04d09f3ef17bd81e9adf9f463b24edae73a56d6d0c0a09ce48 +size 152228 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowLightPreview_0_null,NEXUS_5,1.0,en].png index b204098bdc..abd9d48625 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f82840398e396e038ad68a5fe855394a0088c413e7aade339998b8ed63fb1c09 -size 157273 +oid sha256:39226828ae899c8b765827cad36f9966ec80c0a39ed406c1257224a56255af9b +size 157243 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsDarkPreview_0_null,NEXUS_5,1.0,en].png index d389368d7e..5349a17c04 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c5ef7519aef3badeda3983b2ec5474f31404bc06b4b46b9829848dc7884fe1d -size 81749 +oid sha256:88bdceab1a4e44f971ce507b5b6aeb5513657dfe6c4d61ec5483e215d83254d5 +size 81534 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsLightPreview_0_null,NEXUS_5,1.0,en].png index 722a25f340..f5b5822a12 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9e9b769422e4714e87ace97058e5a8f064e3b927e3a7c6042494de8381d6b09 -size 85856 +oid sha256:461683659d023323184049e122d02e4c63f217157e0bb3165f50be833d19ba7e +size 85517 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png index f2add007e5..dd1ef38d17 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4970830b042e7b6588c03bf632ae3ade5b39de7339e27618ee341cbd9861c0e9 -size 129351 +oid sha256:c00bf46d1ef6337bad18f6454cf4601141ed2190c6a697ddc46d5a156d4997ca +size 127950 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png index a048edf7d8..dce8da362e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4aba48e03e8424e5ff131c22fd5a606280745dd318f5822e2167f3b39794ff41 -size 134569 +oid sha256:88cd76c95c31061bf4e9e7528ed5d66a8b49813d868f21b2d2615e77318a9706 +size 133068 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-D-5_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-D-5_6_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a47fa7ba44 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-D-5_6_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c81a4a854304b62bd356ae9a6d588918d11bf1df0aaa209910fa2fa970f47cb8 +size 26684 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-N-5_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-N-5_7_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..20d82e1483 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-N-5_7_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f32e1f9f7c10672b3a35d6c0c3e3a9347b92ef3fe62be40bc378a5225da5d3d6 +size 26285 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-D-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-D-7_8_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d85a93d2d4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-D-7_8_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e892d14d11fb576228418acd216468438f41a631a073e80638675ec48a91ed6 +size 12334 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-N-7_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-N-7_9_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a645425261 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-N-7_9_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64c2e18e97bba9ff6ae6bb24a17fe567352cd49f1b5905065b7de8854369e22f +size 12209 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-D-8_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-D-8_9_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7fd9264402 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-D-8_9_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d0d29cc0acc0d9009c6fd6aebccf72d6244cec8ec3e3bf3bd445e7cced52561 +size 26004 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-N-8_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-N-8_10_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..674e4ccb32 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-N-8_10_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbfe5166cb1df7be35f2d3f114f73fb4425a10f4eb5bd24a87cafe3ddd76e873 +size 25719 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-D-9_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-D-9_10_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8fb3ea38a2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-D-9_10_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d5f4241f356099cc2f2f55e557218c8872cd0f25f582ba75db254c11613090db +size 26026 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-N-9_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-N-9_11_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fe175d156e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-N-9_11_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf149f6a8c5c8d5d4591b9f6cfe8612a1af2b1cb101b0d1f699945bd711890b4 +size 25685 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-6_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-6_7_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..119678a386 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-6_7_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82550de45d39c52f34fca45f84b6e9eff53dfef6d2e9622dcaf49c707927d665 +size 7873 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-6_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-6_8_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..896fd1051e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-6_8_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fc39041ac035aa372640ca7b2e9af254564aa10ce7a7f4c55fbc4bca9f6dc6d9 +size 7844 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_0,NEXUS_5,1.0,en].png index 7c99f5e856..f1add17e7a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54c12970e3563de958f88e4c538dd368f9810266060627393256a91741f7c6cf -size 53340 +oid sha256:cfc4f0cc8e252cdc9200286cf330c4bbdf3cd66ced2c23364cca680aca79954d +size 53293 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_1,NEXUS_5,1.0,en].png index def9cbe0d1..3a9a78de1d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b7ae3084cb9d1ecee2e4db49c228516bdd7352683e797edf737c8d216922dec -size 65601 +oid sha256:01c7f94b759eea738fb42baa4b8b835c27d48e3fbfdd40d0a925080db8196a80 +size 65563 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_10,NEXUS_5,1.0,en].png index 258ce3f3de..2cf5e925c9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd034d439c08793e0dfd59f6bd5dcd88c06ded6cbe98172bf3cf296888e6d575 -size 51244 +oid sha256:e4360b6f5be55e5b468e735f83b6263ae3b0f1a3f7c0084e6dc3c07396723abd +size 51206 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_11,NEXUS_5,1.0,en].png index b2f16a9d23..d025eed585 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d9d835cb1a420117b4d967181e2ca0fad71ba243d6a17bea08b82cae41f6b8e2 -size 68760 +oid sha256:0a58bb336bbf56b05584d73fa7fd3bd2e2fe97c82829a01988343d4c3b4c77ab +size 68727 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_12,NEXUS_5,1.0,en].png index 3bbd94a1c8..78e3c4cef7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_12,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_12,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ad09278ae2ebb8171adba96d9f6e91d0cc4f120b0b2368087796dadb37eeb87 -size 58539 +oid sha256:ff2c7a701945b72afc6731c9da722e0bf178879f0bd80bc40da7ebb1d6e90d28 +size 58505 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_4,NEXUS_5,1.0,en].png index 4c991e3c30..76a8b6f2f9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:082206122d4e6d9e6171b3b2444c576ff7bd47fb3946d8d98e2812654f39cd40 -size 73641 +oid sha256:4591b08ec297c0b024870e60ac18b03aadeaa8ff20a726dd991b18a80ec353ef +size 73607 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_5,NEXUS_5,1.0,en].png index 80eeb03c19..b76ddce111 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dc695bfc589e8e5a852eeb9b2828ce968d17519bb5be957cf2734a7d6d9cc356 -size 89482 +oid sha256:6586d62bdbd3d4331105f561669c5383d578ad8af559270a26c7d9ff741b473b +size 89412 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_8,NEXUS_5,1.0,en].png index ef081b76b8..7aca1bcd17 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a243d53d10ca2eaa249b22af8a9fddf1a8ccf60db4f3ad9374e31cf494fe878 -size 54909 +oid sha256:8981ec8fd5e68e6e332f98ee2c95fc0f902b9b912a05201cad1c6a9feb5dbc89 +size 54868 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_9,NEXUS_5,1.0,en].png index 0b13a93b0c..2c64ab5210 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3fdd478be89b47fcaf9725ca14c1e775087f1ccf2a266f3476a537bbc2b29922 -size 67476 +oid sha256:a5e787ce25a3542e398bec3aadd2b9b923c81dee4f3fbf8936c1c15e5aa1816b +size 67450 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_0,NEXUS_5,1.0,en].png index 9f5b7d48c6..cb3cb766a7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a3b5bbcdd1593e81384b335045bdcb0b3e01782868993e9f6437a15ca39dbca -size 51380 +oid sha256:1a8af5ff868170d946d03db115271e7ea9c0a15db35bf6815bf95cc4e4f7fa81 +size 51357 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_1,NEXUS_5,1.0,en].png index ba9f8a8d63..5b168bd85f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e26db0a0a8d767d6e732c1d2742cba3c3475d761b0c3017ff01cee7e3d362a5 -size 62773 +oid sha256:438bc82c007c9cd72228f47681981e833645c591c0a48e8a5cb70bd51721d35a +size 62756 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_10,NEXUS_5,1.0,en].png index 7d12d81b18..d2d115d1e0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d5cae7d73178aecc12fbf5f1f27c9e66608b90d462deec862a881b403469e93 -size 49475 +oid sha256:a0926170ca4277e47f9ae49f5092c195cee2a84d67259ba8dc3aa3539d4faecf +size 49454 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_11,NEXUS_5,1.0,en].png index 44fd92b664..7af4192912 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe9456f4446104142221e28167a578a2b5cac772dc35aeb14352c0d422dd6fb0 -size 65726 +oid sha256:68ce3ada02e8ed6cb1e257e49d8c703bd0ff3401bf0e520fa617ed1ea91c6756 +size 65691 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_12,NEXUS_5,1.0,en].png index ee3a9ae3fe..2eeac61c7d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_12,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_12,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cad3b5ce023d890fd4a5ff0f5bbabb678bc6d5c76d3476e8053673c06f360c8b +oid sha256:c902787fb2d84a0e92832020cdd447fc039264fc5add63abea32241251c92bf7 size 56102 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_4,NEXUS_5,1.0,en].png index 4a58253b47..cb7e8bc36f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:41a6c2dd81698696708802276b615a78ff22cc7cb8ae2d4b8d8cc862bfe44a24 -size 70856 +oid sha256:bc956e4fdab7bf15d71e10585947f9f5c24bc1d9e4eaa071de9b747fae879f53 +size 70883 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_5,NEXUS_5,1.0,en].png index fdb8345d8c..38441b5580 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:167f8368e0b94aa05e5d1cc30055e3b0c2c910752d719092ef2d34aae09dc832 -size 84649 +oid sha256:fdf1b66ffe0b7042356d22aa835ceea2c5b697c4ce62287b5ec040f528210216 +size 84635 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_8,NEXUS_5,1.0,en].png index 08c13f8f2a..01dfac9579 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:789ee5ccad8356198cdc0634b4e9a65ed44be2d26e7ce83a8662598c1bd8d4c2 -size 52765 +oid sha256:e438a2e542780851a7b570407e02f125e191d23b9c5299d9457baad78523de84 +size 52740 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_9,NEXUS_5,1.0,en].png index 300bcf7167..c72d23a518 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b40a2e5d60a906d7c35c3ece1854671f5b319b00ad077322d094cbf906c07f7 -size 64803 +oid sha256:17f11b2cb93b8736a2d1224b7ad70c7e3a1a45bcb7e5ceb60475dba734e6efec +size 64787 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 101f913aa8..ba94c22dd0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c724bc77185a9ceec2cf092cb1d7865b13718d5320bd7ac4850bb85590f05b2 -size 52294 +oid sha256:de7f21b5fcc1235a80ea2e7f9e26a93503ea2d0c0793a89db8657979afb33a7d +size 52267 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index cb28314caa..482ab3b775 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a303894134ed06348b609e41cf109dcafcd994c3dffabc6d9ab436fe92605245 -size 53710 +oid sha256:3d368f7cc84b5a8850577fac658aaeb89a1c83a2890b7fd393577c9cde919069 +size 53689 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 2ba437739f..33a7951011 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7523ec0d6defd7074af0c804fdde64fb8d421f6cfa729bc1c4f9858bd87c42d0 -size 52554 +oid sha256:56440b44cf610b0baaf7401bebb8ba2f4504b5dd36cb54fe1669d5deec6d1674 +size 52530 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 73715a33a2..01722cbae6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f48089147f7e089abfa64254143a80857ccc9f840aa60403e1aabc67e2b6d51 -size 55458 +oid sha256:9387ab3e62a9e10721efdea3a6abcdf29db82a04ebdb3c4cefcad2a20ab9b9b8 +size 55386 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 9ce6f92309..f96cc5216e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ff682ee8363d450bb76db72ea06deea87fa47692ce319b7dff315d2a10dfb6a -size 51033 +oid sha256:c0e3bd4e37bb665df997f189e5c2dc763c7f703b78384c63737675bd764fe7a8 +size 51174 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 57eeca6786..9eeb92ff02 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e84edf8adf1a89153dd45272d36e04561d66f2ea765ad9edecfd8d750ba99f97 -size 54237 +oid sha256:ade57e690313eaaeddc7b21b9a2661026fca70e6af1b09aa331bba3d1b1bbaf3 +size 54240 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 8baf81c8b1..ad548c2fbe 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0bc9521bd1576d47ca6f643adf43ce3638d40328207d908ec863c72503d34f24 -size 55682 +oid sha256:89520fd2999582229ace8fd9304644fc74ce01230a4024b90b47ce1cd61eb564 +size 55678 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 40900d7e92..78402d239a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2209c3cc4e7de32ed92b3b0da4616b54d19091d43d21c0e4013728450d0d3f7 -size 54595 +oid sha256:19420aad469fad8a218a0a8cd0f7cfbb9593d7ceb799478a7ad9b84d9c24602f +size 54598 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index 5777c7f77e..6207b630da 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7bdd0ca39534b31c9d421e28337712b1cf2aaf841a766fcc6bdaf996c756bfa -size 57524 +oid sha256:e224a21a1ae913066530e8be81760795b10be388cb7fa92fe399b1e11367d7af +size 57423 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index 2e2bfa89cb..9a56904200 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba349f81d5c417c612cae1263504ea5b4e83dc606ec20c3942368e8992b87ad4 -size 52886 +oid sha256:cac4b5bdb9f225c5313f36caef190e3f698b734db79707928572c942fed85603 +size 52960 From cb04014bd6886e7efbc151ac7fef7b35744fab42 Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 19 Jul 2023 09:36:09 +0100 Subject: [PATCH 004/251] Don't update non reaction screenshots --- ...lineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png | 3 --- ...lineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png | 3 --- ...lineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png | 3 --- ...lineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png | 3 --- ...lineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png | 3 --- ...lineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png | 3 --- ...eItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png | 3 --- ...eItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png | 3 --- ...eItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png | 3 --- ...eItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png | 3 --- ...edHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png | 3 --- ...edHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png | 3 --- 12 files changed, 36 deletions(-) delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 53bfbe6d54..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e00510a5f35eb33aaef15c30e6caac62045d8e4c37c032a24922bbb749ad0375 -size 9817 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 9ce9173802..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:229d83ed02804137ab1dad4114eecc6d52d6a0e3ccbad436cf226f6cfc628cf7 -size 12200 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 77a65f3b40..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:27fff9f0ea88cf06934298ea6155cbf4dd49c370cc5d0a14b4d387ac8a7e7c39 -size 23203 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index c02a4d8bf8..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ba5f5fefedaaf994fb2eae3df38724b245e27a1b03d48cb20d33f7ca49caa356 -size 9458 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index d636a6b668..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:19ce1d8ddd69760417e267cda7d4663b44d7a50f8ef1a5617497e1135f8aa586 -size 11500 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png deleted file mode 100644 index 6db46605ae..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2e589a013be7a4b54e3f91816565dd38a05086c19d9832480713d895b1462e72 -size 20864 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 6bdd02b8f4..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7cf8ad6c8f8da98fdfc7a9d5c47e959aaa612dfc5192dd93b589c783d4e94fd6 -size 155690 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index fbfd3b1b30..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2ae5e86ad1728027f7189041745592d4ed24df07e008b5798b29eadba5449569 -size 159493 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 8b8209ffcc..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b4c033b42d6e7fcd4fece8b8023ee07b1d0477e5bdff338c353903c83da62211 -size 76073 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 4794defdd9..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:56875cfd798c1f5993c6630bc7183dab367913f7c58753434a7d4a08763eac48 -size 80041 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 45593d6af2..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2c806ee0293d94ab1c882b0f0ceaac2c7ac69d587f47cc22b4d16bc331351a2e -size 14711 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png deleted file mode 100644 index c74bbe95f8..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:77c97c6c99943858530219234356459a7f0a88774332a4f11cd738b92ee15c91 -size 14200 From 90f91160e27f350aa1127822e2837a11e27a2bb0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 19 Jul 2023 16:47:25 +0200 Subject: [PATCH 005/251] version++ --- plugins/src/main/kotlin/Versions.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 36a3a33e70..3a6c591e19 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -18,8 +18,8 @@ import org.gradle.api.JavaVersion import org.gradle.jvm.toolchain.JavaLanguageVersion object Versions { - const val versionCode = 100100 - const val versionName = "0.1.0" + const val versionCode = 100200 + const val versionName = "0.2.0" const val compileSdk = 33 const val targetSdk = 33 From 025d8781a74217a80331bae2e64ebffda9d3f210 Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Wed, 19 Jul 2023 16:58:04 +0200 Subject: [PATCH 006/251] Refactor a custom alignment to its own modifier for readability. (#924) --- .../features/location/api/StaticMapView.kt | 10 ++---- .../api/internal/ModifierCenterBottomEdge.kt | 36 +++++++++++++++++++ .../location/impl/send/SendLocationView.kt | 10 ++---- 3 files changed, 40 insertions(+), 16 deletions(-) create mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/ModifierCenterBottomEdge.kt diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt index 14390d0f4f..03aa21b4c8 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt @@ -29,12 +29,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import coil.compose.AsyncImagePainter import coil.compose.rememberAsyncImagePainter import coil.request.ImageRequest import io.element.android.features.location.api.internal.StaticMapPlaceholder +import io.element.android.features.location.api.internal.centerBottomEdge import io.element.android.features.location.api.internal.staticMapUrl import io.element.android.libraries.designsystem.preview.DayNightPreviews import io.element.android.libraries.designsystem.preview.ElementPreview @@ -106,13 +106,7 @@ fun StaticMapView( resourceId = DesignSystemR.drawable.pin, contentDescription = null, tint = Color.Unspecified, - modifier = Modifier.align { size, space, _ -> - // Center bottom edge of pin (i.e. its arrow) to center of screen - IntOffset( - x = (space.width - size.width) / 2, - y = (space.height / 2) - size.height, - ) - } + modifier = Modifier.centerBottomEdge(this), ) } else { StaticMapPlaceholder( diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/ModifierCenterBottomEdge.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/ModifierCenterBottomEdge.kt new file mode 100644 index 0000000000..7ad299adae --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/ModifierCenterBottomEdge.kt @@ -0,0 +1,36 @@ +/* + * 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.location.api.internal + +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.IntOffset + +/** + * Horizontally aligns the content to the center of the space. + * Vertically aligns the bottom edge of the content to the center of the space. + */ +fun Modifier.centerBottomEdge(scope: BoxScope): Modifier = with(scope) { + then( + Modifier.align { size, space, _ -> + IntOffset( + x = (space.width - size.width) / 2, + y = (space.height / 2) - size.height, + ) + } + ) +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt index 1a30c996a8..d0e9dfd5aa 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt @@ -43,10 +43,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import com.mapbox.mapboxsdk.camera.CameraPosition import io.element.android.features.location.api.Location +import io.element.android.features.location.api.internal.centerBottomEdge import io.element.android.features.location.api.internal.rememberTileStyleUrl import io.element.android.features.location.impl.MapDefaults import io.element.android.libraries.designsystem.components.button.BackButton @@ -201,13 +201,7 @@ fun SendLocationView( resourceId = DesignSystemR.drawable.pin, contentDescription = null, tint = Color.Unspecified, - modifier = Modifier.align { size, space, _ -> - // Center bottom edge of pin (i.e. its arrow) to center of screen - IntOffset( - x = (space.width - size.width) / 2, - y = (space.height / 2) - size.height, - ) - } + modifier = Modifier.centerBottomEdge(this), ) FloatingActionButton( onClick = { state.eventSink(SendLocationEvents.SwitchToMyLocationMode) }, From 0c9c1623542d6b32acfd1ca151c59d276bb5e5cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 20 Jul 2023 07:21:28 +0200 Subject: [PATCH 007/251] Try to fix lint issue --- .../impl/timeline/components/TimelineItemReactionsLayout.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt index 8f3f5865ea..3b1cb9b878 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt @@ -42,6 +42,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview * @param expandButton The expand button * @param addMoreButton The add more button * @param reactions The reaction buttons + * @param modifier The modifier to apply to this layout */ @Composable fun TimelineItemReactionsLayout( From 217169620c85ed140511f229e95bb2dd889d3ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 20 Jul 2023 07:27:02 +0200 Subject: [PATCH 008/251] Use `ElementTheme` as much as possible --- .../impl/timeline/components/MessagesReactionButton.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt index 083bfcb7b5..20658c798e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt @@ -117,7 +117,7 @@ private fun TextContent( .height(reactionEmojiLineHeight.toDp()), text = text, style = ElementTheme.typography.fontBodyMdRegular, - color = MaterialTheme.colorScheme.primary + color = ElementTheme.materialColors.primary ) @Composable @@ -127,7 +127,7 @@ private fun IconContent( ) = Icon( imageVector = imageVector, contentDescription = stringResource(id = R.string.screen_room_timeline_add_reaction), - tint = MaterialTheme.colorScheme.secondary, + tint = ElementTheme.materialColors.secondary, modifier = modifier .size(reactionEmojiLineHeight.toDp()) ) From 281cb17c2b72539e64faeb4fc65f05d57c80248d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 20 Jul 2023 07:27:13 +0200 Subject: [PATCH 009/251] Fix typo --- .../impl/timeline/components/TimelineItemReactionsLayout.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt index 3b1cb9b878..802a593447 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt @@ -65,7 +65,7 @@ fun TimelineItemReactionsLayout( measurables.forEach { placeable -> val horizontalSpacing = if (currentRow.isEmpty()) 0 else itemSpacing.toPx().toInt() - // If the current view does not fine on this row bump to the next + // If the current view does not fit on this row bump to the next if (rowX + placeable.width > constraints.maxWidth) { rows.add(currentRow) currentRow = mutableListOf() From 9073ea2107dd5d37fd02acab262c36bb273a984c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 20 Jul 2023 07:35:27 +0200 Subject: [PATCH 010/251] Fix more lint issues --- .../components/TimelineItemReactionsLayout.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt index 802a593447..8fe0ab1bef 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt @@ -35,25 +35,25 @@ import io.element.android.libraries.designsystem.preview.ElementPreview * A flow layout for reactions that will show a collapse/expand button when the layout wraps over a defined number of rows. * It displays an add more button when there are greater than 0 reactions and always displays the reaction and add more button * on the same row (moving them both to a new row if necessary). + * @param expandButton The expand button + * @param addMoreButton The add more button + * @param modifier The modifier to apply to this layout * @param itemSpacing The horizontal spacing between items * @param rowSpacing The vertical spacing between rows * @param expanded Whether the layout should display in expanded or collapsed state * @param rowsBeforeCollapsible The number of rows before the collapse/expand button is shown - * @param expandButton The expand button - * @param addMoreButton The add more button * @param reactions The reaction buttons - * @param modifier The modifier to apply to this layout */ @Composable fun TimelineItemReactionsLayout( + expandButton: @Composable () -> Unit, + addMoreButton: @Composable () -> Unit, modifier: Modifier = Modifier, itemSpacing: Dp = 0.dp, rowSpacing: Dp = 0.dp, expanded: Boolean = false, rowsBeforeCollapsible: Int? = 2, - expandButton: @Composable () -> Unit, - addMoreButton: @Composable () -> Unit, - reactions: @Composable () -> Unit + reactions: @Composable () -> Unit, ) { SubcomposeLayout(modifier) { constraints -> // Given the placeables and returns a structure representing @@ -85,7 +85,7 @@ fun TimelineItemReactionsLayout( // removing only as many trailing reactions as needed to make space for it. fun replaceTrailingItemsWithButtons(rowsIn: List>, expandButton: Placeable, addMoreButton: Placeable): List> { val rows = rowsIn.toMutableList() - val lastRow = rows[rows.size - 1] + val lastRow = rows.last() val buttonsWidth = expandButton.width + itemSpacing.toPx().toInt() + addMoreButton.width var rowX = 0 lastRow.forEachIndexed { i, placeable -> From 6d3b816bf5dfabc3e7760dc4d4917d609a1e5e91 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 20 Jul 2023 07:36:19 +0200 Subject: [PATCH 011/251] Update dependency app.cash.molecule:molecule-runtime to v1 (#925) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependency app.cash.molecule:molecule-runtime to v1 * Replace `RecompositionClock` with `RecompositionMode` --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jorge Martín --- .../android/appnav/RootPresenterTest.kt | 6 +-- .../appnav/loggedin/LoggedInPresenterTest.kt | 4 +- .../impl/AnalyticsOptInPresenterTest.kt | 6 +-- .../AnalyticsPreferencesPresenterTest.kt | 8 +-- .../impl/addpeople/AddPeoplePresenterTests.kt | 4 +- .../ConfigureRoomPresenterTests.kt | 16 +++--- .../impl/root/CreateRoomRootPresenterTests.kt | 12 ++--- .../userlist/DefaultUserListPresenterTests.kt | 14 +++--- .../impl/InviteListPresenterTests.kt | 30 +++++------ .../impl/LeaveRoomPresenterImplTest.kt | 18 +++---- .../impl/send/SendLocationPresenterTest.kt | 26 +++++----- .../impl/show/ShowLocationPresenterTest.kt | 12 ++--- .../changeserver/ChangeServerPresenterTest.kt | 8 +-- .../impl/oidc/webview/OidcPresenterTest.kt | 14 +++--- .../ChangeAccountProviderPresenterTest.kt | 4 +- .../ConfirmAccountProviderPresenterTest.kt | 12 ++--- .../LoginPasswordPresenterTest.kt | 12 ++--- .../SearchAccountProviderPresenterTest.kt | 12 ++--- .../waitlistscreen/WaitListPresenterTest.kt | 8 +-- .../impl/LogoutPreferencePresenterTest.kt | 8 +-- .../messages/MessagesPresenterTest.kt | 50 +++++++++---------- .../actionlist/ActionListPresenterTest.kt | 24 ++++----- .../AttachmentsPreviewPresenterTest.kt | 6 +-- .../forward/ForwardMessagesPresenterTests.kt | 14 +++--- .../media/viewer/MediaViewerPresenterTest.kt | 8 +-- .../report/ReportMessagePresenterTests.kt | 14 +++--- .../MessageComposerPresenterTest.kt | 40 +++++++-------- .../timeline/TimelinePresenterTest.kt | 16 +++--- .../CustomReactionPresenterTests.kt | 4 +- .../RetrySendMenuPresenterTests.kt | 18 +++---- .../impl/OnBoardingPresenterTest.kt | 4 +- .../impl/about/AboutPresenterTest.kt | 4 +- ...AnalyticsAnalyticsSettingsPresenterTest.kt | 4 +- .../DeveloperSettingsPresenterTest.kt | 10 ++-- .../impl/root/PreferencesRootPresenterTest.kt | 4 +- .../impl/bugreport/BugReportPresenterTest.kt | 20 ++++---- .../crash/ui/CrashDetectionPresenterTest.kt | 10 ++-- .../RageshakeDetectionPresenterTest.kt | 12 ++--- .../RageshakePreferencesPresenterTest.kt | 10 ++-- .../roomdetails/RoomDetailsPresenterTests.kt | 30 +++++------ .../edit/RoomDetailsEditPresenterTest.kt | 36 ++++++------- .../invite/RoomInviteMembersPresenterTest.kt | 20 ++++---- .../members/RoomMemberListPresenterTests.kt | 16 +++--- .../RoomMemberDetailsPresenterTests.kt | 16 +++--- .../roomlist/impl/RoomListPresenterTests.kt | 24 ++++----- .../DefaultInviteStateDataSourceTest.kt | 12 ++--- .../impl/VerifySelfSessionPresenterTests.kt | 22 ++++---- gradle/libs.versions.toml | 2 +- .../impl/DefaultPermissionsPresenterTest.kt | 14 +++--- .../noop/NoopPermissionsPresenterTest.kt | 4 +- 50 files changed, 351 insertions(+), 351 deletions(-) diff --git a/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt index 0efa9e7f3b..dad9365921 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/RootPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.appnav -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -38,7 +38,7 @@ class RootPresenterTest { @Test fun `present - initial state`() = runTest { val presenter = createPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -54,7 +54,7 @@ class RootPresenterTest { showError("Bad news", "Something bad happened") } ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) diff --git a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt index 83bda0ad82..a811d0283d 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.appnav.loggedin -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -34,7 +34,7 @@ class LoggedInPresenterTest { @Test fun `present - initial state`() = runTest { val presenter = createPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt b/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt index a8e42ceb01..541e5858bd 100644 --- a/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt +++ b/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.analytics.impl -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -35,7 +35,7 @@ class AnalyticsOptInPresenterTest { aBuildMeta(), analyticsService ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -53,7 +53,7 @@ class AnalyticsOptInPresenterTest { aBuildMeta(), analyticsService ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt b/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt index 8469abe769..d0914932bb 100644 --- a/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt +++ b/features/analytics/impl/src/test/kotlin/io/element/android/features/analytics/impl/preferences/AnalyticsPreferencesPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.analytics.impl.preferences -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -33,7 +33,7 @@ class AnalyticsPreferencesPresenterTest { FakeAnalyticsService(isEnabled = true, didAskUserConsent = true), aBuildMeta() ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -48,7 +48,7 @@ class AnalyticsPreferencesPresenterTest { FakeAnalyticsService(isEnabled = false, didAskUserConsent = false), aBuildMeta() ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -62,7 +62,7 @@ class AnalyticsPreferencesPresenterTest { FakeAnalyticsService(isEnabled = true, didAskUserConsent = true), aBuildMeta() ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt index fe8c7b7462..43cf6cff4d 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeoplePresenterTests.kt @@ -16,7 +16,7 @@ package io.element.android.features.createroom.impl.addpeople -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -43,7 +43,7 @@ class AddPeoplePresenterTests { @Test fun `present - initial state`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { // TODO This doesn't actually test anything... diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt index 9b6ac2e067..1703cf4142 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt @@ -17,7 +17,7 @@ package io.element.android.features.createroom.impl.configureroom import android.net.Uri -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -93,7 +93,7 @@ class ConfigureRoomPresenterTests { @Test fun `present - initial state`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -108,7 +108,7 @@ class ConfigureRoomPresenterTests { @Test fun `present - create room button is enabled only if the required fields are completed`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -133,7 +133,7 @@ class ConfigureRoomPresenterTests { @Test fun `present - state is updated when fields are changed`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -203,7 +203,7 @@ class ConfigureRoomPresenterTests { @Test fun `present - trigger create room action`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -221,7 +221,7 @@ class ConfigureRoomPresenterTests { @Test fun `present - record analytics when creating room`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -240,7 +240,7 @@ class ConfigureRoomPresenterTests { @Test fun `present - trigger create room with upload error and retry`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -265,7 +265,7 @@ class ConfigureRoomPresenterTests { @Test fun `present - trigger retry and cancel actions`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt index 8976c77b8e..2f439b5107 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt @@ -16,7 +16,7 @@ package io.element.android.features.createroom.impl.root -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -68,7 +68,7 @@ class CreateRoomRootPresenterTests { @Test fun `present - initial state`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -82,7 +82,7 @@ class CreateRoomRootPresenterTests { @Test fun `present - trigger create DM action`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -102,7 +102,7 @@ class CreateRoomRootPresenterTests { @Test fun `present - creating a DM records analytics event`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -123,7 +123,7 @@ class CreateRoomRootPresenterTests { @Test fun `present - trigger retrieve DM action`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -142,7 +142,7 @@ class CreateRoomRootPresenterTests { @Test fun `present - trigger retry create DM action`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt index 745bdf74f9..3561387dcf 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt @@ -16,7 +16,7 @@ package io.element.android.features.createroom.impl.userlist -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -41,7 +41,7 @@ class DefaultUserListPresenterTests { userRepository, UserListDataStore(), ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -62,7 +62,7 @@ class DefaultUserListPresenterTests { userRepository, UserListDataStore(), ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -83,7 +83,7 @@ class DefaultUserListPresenterTests { userRepository, UserListDataStore(), ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -119,7 +119,7 @@ class DefaultUserListPresenterTests { userRepository, UserListDataStore(), ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -158,7 +158,7 @@ class DefaultUserListPresenterTests { userRepository, UserListDataStore(), ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -183,7 +183,7 @@ class DefaultUserListPresenterTests { userRepository, UserListDataStore(), ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) diff --git a/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt b/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt index 1dd9068a1f..03e0f46de8 100644 --- a/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt +++ b/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt @@ -16,7 +16,7 @@ package io.element.android.features.invitelist.impl -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth @@ -55,7 +55,7 @@ class InviteListPresenterTests { val presenter = createPresenter( FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -76,7 +76,7 @@ class InviteListPresenterTests { val presenter = createPresenter( FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val withInviteState = awaitItem() @@ -102,7 +102,7 @@ class InviteListPresenterTests { val presenter = createPresenter( FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val withInviteState = awaitItem() @@ -131,7 +131,7 @@ class InviteListPresenterTests { FakeAnalyticsService(), FakeNotificationDrawerManager() ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val originalState = awaitItem() @@ -152,7 +152,7 @@ class InviteListPresenterTests { val presenter = createPresenter( FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val originalState = awaitItem() @@ -173,7 +173,7 @@ class InviteListPresenterTests { val presenter = createPresenter( FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val originalState = awaitItem() @@ -199,7 +199,7 @@ class InviteListPresenterTests { val presenter = createPresenter(client = client, notificationDrawerManager = fakeNotificationDrawerManager) client.givenGetRoomResult(A_ROOM_ID, room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val originalState = awaitItem() @@ -227,7 +227,7 @@ class InviteListPresenterTests { room.givenLeaveRoomError(ex) client.givenGetRoomResult(A_ROOM_ID, room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val originalState = awaitItem() @@ -257,7 +257,7 @@ class InviteListPresenterTests { room.givenLeaveRoomError(ex) client.givenGetRoomResult(A_ROOM_ID, room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val originalState = awaitItem() @@ -288,7 +288,7 @@ class InviteListPresenterTests { val presenter = createPresenter(client = client, notificationDrawerManager = fakeNotificationDrawerManager) client.givenGetRoomResult(A_ROOM_ID, room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val originalState = awaitItem() @@ -313,7 +313,7 @@ class InviteListPresenterTests { room.givenJoinRoomResult(Result.failure(ex)) client.givenGetRoomResult(A_ROOM_ID, room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val originalState = awaitItem() @@ -335,7 +335,7 @@ class InviteListPresenterTests { room.givenJoinRoomResult(Result.failure(ex)) client.givenGetRoomResult(A_ROOM_ID, room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val originalState = awaitItem() @@ -362,7 +362,7 @@ class InviteListPresenterTests { FakeAnalyticsService(), FakeNotificationDrawerManager() ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { awaitItem() @@ -400,7 +400,7 @@ class InviteListPresenterTests { FakeAnalyticsService(), FakeNotificationDrawerManager() ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { awaitItem() diff --git a/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt b/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt index 03eeb3adc6..3075d3c686 100644 --- a/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt +++ b/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterImplTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.leaveroom.impl -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -40,7 +40,7 @@ class LeaveRoomPresenterImplTest { @Test fun `present - initial state hides all dialogs`() = runTest { val presenter = createPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -60,7 +60,7 @@ class LeaveRoomPresenterImplTest { ) } ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -80,7 +80,7 @@ class LeaveRoomPresenterImplTest { ) } ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -100,7 +100,7 @@ class LeaveRoomPresenterImplTest { ) } ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -122,7 +122,7 @@ class LeaveRoomPresenterImplTest { }, roomMembershipObserver = roomMembershipObserver ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -145,7 +145,7 @@ class LeaveRoomPresenterImplTest { ) } ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -167,7 +167,7 @@ class LeaveRoomPresenterImplTest { ) } ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -191,7 +191,7 @@ class LeaveRoomPresenterImplTest { ) } ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt index 45c99b556a..85a6a28cd9 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.location.impl.send -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth @@ -69,7 +69,7 @@ class SendLocationPresenterTest { ) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { sendLocationPresenter.present() }.test { @@ -96,7 +96,7 @@ class SendLocationPresenterTest { ) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { sendLocationPresenter.present() }.test { @@ -123,7 +123,7 @@ class SendLocationPresenterTest { ) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { sendLocationPresenter.present() }.test { val initialState = awaitItem() @@ -149,7 +149,7 @@ class SendLocationPresenterTest { ) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { sendLocationPresenter.present() }.test { val initialState = awaitItem() @@ -175,7 +175,7 @@ class SendLocationPresenterTest { ) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { sendLocationPresenter.present() }.test { // Skip initial state @@ -206,7 +206,7 @@ class SendLocationPresenterTest { ) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { sendLocationPresenter.present() }.test { // Skip initial state @@ -234,7 +234,7 @@ class SendLocationPresenterTest { ) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { sendLocationPresenter.present() }.test { // Skip initial state @@ -265,7 +265,7 @@ class SendLocationPresenterTest { ) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { sendLocationPresenter.present() }.test { // Skip initial state @@ -322,7 +322,7 @@ class SendLocationPresenterTest { ) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { sendLocationPresenter.present() }.test { // Skip initial state @@ -384,7 +384,7 @@ class SendLocationPresenterTest { ) } - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { sendLocationPresenter.present() }.test { // Skip initial state @@ -431,7 +431,7 @@ class SendLocationPresenterTest { ) } - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { sendLocationPresenter.present() }.test { // Skip initial state @@ -451,7 +451,7 @@ class SendLocationPresenterTest { @Test fun `application name is in state`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { sendLocationPresenter.present() }.test { val initialState = awaitItem() diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt index 47f0e2ccea..7fff766a9f 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/ShowLocationPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.location.impl.show -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth @@ -44,7 +44,7 @@ class ShowLocationPresenterTest { @Test fun `emits initial state with no location permission`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -59,7 +59,7 @@ class ShowLocationPresenterTest { fun `emits initial state with location permission`() = runTest { permissionsPresenterFake.givenState(PermissionsState(permissions = PermissionsState.Permissions.AllGranted)) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -74,7 +74,7 @@ class ShowLocationPresenterTest { fun `emits initial state with partial location permission`() = runTest { permissionsPresenterFake.givenState(PermissionsState(permissions = PermissionsState.Permissions.SomeGranted)) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -87,7 +87,7 @@ class ShowLocationPresenterTest { @Test fun `uses action to share location`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -102,7 +102,7 @@ class ShowLocationPresenterTest { fun `centers on user location`() = runTest { permissionsPresenterFake.givenState(PermissionsState(permissions = PermissionsState.Permissions.AllGranted)) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt index 9aefafb382..009ab31dcd 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.login.impl.changeserver -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -36,7 +36,7 @@ class ChangeServerPresenterTest { FakeAuthenticationService(), AccountProviderDataSource() ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -51,7 +51,7 @@ class ChangeServerPresenterTest { authenticationService, AccountProviderDataSource() ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -72,7 +72,7 @@ class ChangeServerPresenterTest { authenticationService, AccountProviderDataSource() ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt index 5756cd13d2..32d1c6918a 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt @@ -18,7 +18,7 @@ package io.element.android.features.login.impl.oidc.webview -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -38,7 +38,7 @@ class OidcPresenterTest { A_OIDC_DATA, FakeAuthenticationService(), ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -53,7 +53,7 @@ class OidcPresenterTest { A_OIDC_DATA, FakeAuthenticationService(), ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -73,7 +73,7 @@ class OidcPresenterTest { authenticationService, ) authenticationService.givenOidcCancelError(A_THROWABLE) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -92,7 +92,7 @@ class OidcPresenterTest { A_OIDC_DATA, FakeAuthenticationService(), ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -110,7 +110,7 @@ class OidcPresenterTest { A_OIDC_DATA, FakeAuthenticationService(), ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -129,7 +129,7 @@ class OidcPresenterTest { authenticationService, ) authenticationService.givenLoginError(A_THROWABLE) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt index 086428257a..f807355cb1 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.login.impl.screens.changeaccountprovider -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -37,7 +37,7 @@ class ChangeAccountProviderPresenterTest { val presenter = ChangeAccountProviderPresenter( changeServerPresenter ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt index 131d0d9298..76a3ad3d22 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.login.impl.screens.confirmaccountprovider -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -38,7 +38,7 @@ class ConfirmAccountProviderPresenterTest { AccountProviderDataSource(), FakeAuthenticationService(), ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -58,7 +58,7 @@ class ConfirmAccountProviderPresenterTest { authServer, ) authServer.givenHomeserver(A_HOMESERVER) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -82,7 +82,7 @@ class ConfirmAccountProviderPresenterTest { authServer, ) authServer.givenHomeserver(A_HOMESERVER_OIDC) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -105,7 +105,7 @@ class ConfirmAccountProviderPresenterTest { AccountProviderDataSource(), authServer, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -126,7 +126,7 @@ class ConfirmAccountProviderPresenterTest { AccountProviderDataSource(), authenticationService, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt index afd4b542e4..421eb869b0 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.login.impl.screens.loginpassword -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -45,7 +45,7 @@ class LoginPasswordPresenterTest { accountProviderDataSource, loginUserStory, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -67,7 +67,7 @@ class LoginPasswordPresenterTest { loginUserStory, ) authenticationService.givenHomeserver(A_HOMESERVER) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -93,7 +93,7 @@ class LoginPasswordPresenterTest { loginUserStory, ) authenticationService.givenHomeserver(A_HOMESERVER) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { assertThat(loginUserStory.loginFlowIsDone.value).isFalse() @@ -122,7 +122,7 @@ class LoginPasswordPresenterTest { loginUserStory, ) authenticationService.givenHomeserver(A_HOMESERVER) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -150,7 +150,7 @@ class LoginPasswordPresenterTest { loginUserStory, ) authenticationService.givenHomeserver(A_HOMESERVER) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt index 9163f247f5..ae6ae4d344 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.login.impl.screens.searchaccountprovider -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -46,7 +46,7 @@ class SearchAccountProviderPresenterTest { HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest), changeServerPresenter ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -66,7 +66,7 @@ class SearchAccountProviderPresenterTest { HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest), changeServerPresenter ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -90,7 +90,7 @@ class SearchAccountProviderPresenterTest { HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest), changeServerPresenter ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -125,7 +125,7 @@ class SearchAccountProviderPresenterTest { HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest), changeServerPresenter ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -160,7 +160,7 @@ class SearchAccountProviderPresenterTest { HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRequest), changeServerPresenter ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenterTest.kt index 389ac52176..507e8cec8b 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.login.impl.screens.waitlistscreen -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -46,7 +46,7 @@ class WaitListPresenterTest { authenticationService, loginUserStory, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -68,7 +68,7 @@ class WaitListPresenterTest { authenticationService, loginUserStory, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -97,7 +97,7 @@ class WaitListPresenterTest { authenticationService, loginUserStory, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { assertThat(loginUserStory.loginFlowIsDone.value).isFalse() diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt index bed33006d6..29df521cb1 100644 --- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPreferencePresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.logout.impl -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -34,7 +34,7 @@ class LogoutPreferencePresenterTest { val presenter = DefaultLogoutPreferencePresenter( FakeMatrixClient(), ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -47,7 +47,7 @@ class LogoutPreferencePresenterTest { val presenter = DefaultLogoutPreferencePresenter( FakeMatrixClient(), ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -65,7 +65,7 @@ class LogoutPreferencePresenterTest { val presenter = DefaultLogoutPreferencePresenter( matrixClient, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index 8e68da1d12..95848d835f 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -17,7 +17,7 @@ package io.element.android.features.messages import android.net.Uri -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.collect.Iterables.skip @@ -81,7 +81,7 @@ class MessagesPresenterTest { @Test fun `present - initial state`() = runTest { val presenter = createMessagePresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -95,7 +95,7 @@ class MessagesPresenterTest { val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true) val room = FakeMatrixRoom() val presenter = createMessagePresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -116,7 +116,7 @@ class MessagesPresenterTest { val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true) val room = FakeMatrixRoom() val presenter = createMessagePresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -133,7 +133,7 @@ class MessagesPresenterTest { fun `present - handle action forward`() = runTest { val navigator = FakeMessagesNavigator() val presenter = createMessagePresenter(navigator = navigator) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -149,7 +149,7 @@ class MessagesPresenterTest { val clipboardHelper = FakeClipboardHelper() val event = aMessageEvent() val presenter = createMessagePresenter(clipboardHelper = clipboardHelper) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -163,7 +163,7 @@ class MessagesPresenterTest { @Test fun `present - handle action reply`() = runTest { val presenter = createMessagePresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -179,7 +179,7 @@ class MessagesPresenterTest { @Test fun `present - handle action reply to an event with no id does nothing`() = runTest { val presenter = createMessagePresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -194,7 +194,7 @@ class MessagesPresenterTest { @Test fun `present - handle action reply to an image media message`() = runTest { val presenter = createMessagePresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -226,7 +226,7 @@ class MessagesPresenterTest { @Test fun `present - handle action reply to a video media message`() = runTest { val presenter = createMessagePresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -259,7 +259,7 @@ class MessagesPresenterTest { @Test fun `present - handle action reply to a file media message`() = runTest { val presenter = createMessagePresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -287,7 +287,7 @@ class MessagesPresenterTest { @Test fun `present - handle action edit`() = runTest { val presenter = createMessagePresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -305,7 +305,7 @@ class MessagesPresenterTest { val coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true) val matrixRoom = FakeMatrixRoom() val presenter = createMessagePresenter(matrixRoom = matrixRoom, coroutineDispatchers = coroutineDispatchers) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -320,7 +320,7 @@ class MessagesPresenterTest { fun `present - handle action report content`() = runTest { val navigator = FakeMessagesNavigator() val presenter = createMessagePresenter(navigator = navigator) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -334,7 +334,7 @@ class MessagesPresenterTest { @Test fun `present - handle dismiss action`() = runTest { val presenter = createMessagePresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -348,7 +348,7 @@ class MessagesPresenterTest { fun `present - handle action show developer info`() = runTest { val navigator = FakeMessagesNavigator() val presenter = createMessagePresenter(navigator = navigator) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -363,7 +363,7 @@ class MessagesPresenterTest { fun `present - shows prompt to reinvite users in DM`() = runTest { val room = FakeMatrixRoom(sessionId = A_SESSION_ID, isDirect = true, activeMemberCount = 1L) val presenter = createMessagePresenter(matrixRoom = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -388,7 +388,7 @@ class MessagesPresenterTest { fun `present - doesn't show reinvite prompt in non-direct room`() = runTest { val room = FakeMatrixRoom(sessionId = A_SESSION_ID, isDirect = false, activeMemberCount = 1L) val presenter = createMessagePresenter(matrixRoom = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -405,7 +405,7 @@ class MessagesPresenterTest { fun `present - doesn't show reinvite prompt if other party is present`() = runTest { val room = FakeMatrixRoom(sessionId = A_SESSION_ID, isDirect = true, activeMemberCount = 2L) val presenter = createMessagePresenter(matrixRoom = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -430,7 +430,7 @@ class MessagesPresenterTest { ) ) val presenter = createMessagePresenter(matrixRoom = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -460,7 +460,7 @@ class MessagesPresenterTest { ) ) val presenter = createMessagePresenter(matrixRoom = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -482,7 +482,7 @@ class MessagesPresenterTest { val room = FakeMatrixRoom(sessionId = A_SESSION_ID) room.givenRoomMembersState(MatrixRoomMembersState.Unknown) val presenter = createMessagePresenter(matrixRoom = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -511,7 +511,7 @@ class MessagesPresenterTest { ) room.givenInviteUserResult(Result.failure(Throwable("Oops!"))) val presenter = createMessagePresenter(matrixRoom = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -531,7 +531,7 @@ class MessagesPresenterTest { val matrixRoom = FakeMatrixRoom() matrixRoom.givenCanSendEventResult(MessageEventType.ROOM_MESSAGE, Result.success(true)) val presenter = createMessagePresenter(matrixRoom = matrixRoom) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -544,7 +544,7 @@ class MessagesPresenterTest { val matrixRoom = FakeMatrixRoom() matrixRoom.givenCanSendEventResult(MessageEventType.ROOM_MESSAGE, Result.success(false)) val presenter = createMessagePresenter(matrixRoom = matrixRoom) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { // Default value diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt index 0aafa68100..b814a51c42 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.messages.actionlist -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -41,7 +41,7 @@ class ActionListPresenterTest { @Test fun `present - initial state`() = runTest { val presenter = anActionListPresenter(isBuildDebuggable = true) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -52,7 +52,7 @@ class ActionListPresenterTest { @Test fun `present - compute for message from me redacted`() = runTest { val presenter = anActionListPresenter(isBuildDebuggable = true) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -77,7 +77,7 @@ class ActionListPresenterTest { @Test fun `present - compute for message from others redacted`() = runTest { val presenter = anActionListPresenter(isBuildDebuggable = true) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -102,7 +102,7 @@ class ActionListPresenterTest { @Test fun `present - compute for others message`() = runTest { val presenter = anActionListPresenter(isBuildDebuggable = true) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -134,7 +134,7 @@ class ActionListPresenterTest { @Test fun `present - compute for my message`() = runTest { val presenter = anActionListPresenter(isBuildDebuggable = true) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -167,7 +167,7 @@ class ActionListPresenterTest { @Test fun `present - compute for a media item`() = runTest { val presenter = anActionListPresenter(isBuildDebuggable = true) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -198,7 +198,7 @@ class ActionListPresenterTest { @Test fun `present - compute for a state item in debug build`() = runTest { val presenter = anActionListPresenter(isBuildDebuggable = true) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -227,7 +227,7 @@ class ActionListPresenterTest { @Test fun `present - compute for a state item in non-debuggable build`() = runTest { val presenter = anActionListPresenter(isBuildDebuggable = false) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -255,7 +255,7 @@ class ActionListPresenterTest { @Test fun `present - compute message in non-debuggable build`() = runTest { val presenter = anActionListPresenter(isBuildDebuggable = false) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -287,7 +287,7 @@ class ActionListPresenterTest { @Test fun `present - compute message with no actions`() = runTest { val presenter = anActionListPresenter(isBuildDebuggable = false) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -314,7 +314,7 @@ class ActionListPresenterTest { @Test fun `present - compute not sent message`() = runTest { val presenter = anActionListPresenter(isBuildDebuggable = false) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt index db202569ee..a96d7104a3 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt @@ -19,7 +19,7 @@ package io.element.android.features.messages.attachments import android.net.Uri -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -55,7 +55,7 @@ class AttachmentsPreviewPresenterTest { ) ) val presenter = anAttachmentsPreviewPresenter(room = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -77,7 +77,7 @@ class AttachmentsPreviewPresenterTest { val failure = MediaPreProcessor.Failure(null) room.givenSendMediaResult(Result.failure(failure)) val presenter = anAttachmentsPreviewPresenter(room = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt index 502305ab1d..9d19932716 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt @@ -16,7 +16,7 @@ package io.element.android.features.messages.forward -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -40,7 +40,7 @@ class ForwardMessagesPresenterTests { @Test fun `present - initial state`() = runTest { val presenter = aPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -60,7 +60,7 @@ class ForwardMessagesPresenterTests { @Test fun `present - toggle search active`() = runTest { val presenter = aPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -81,7 +81,7 @@ class ForwardMessagesPresenterTests { } val client = FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) val presenter = aPresenter(client = client) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -96,7 +96,7 @@ class ForwardMessagesPresenterTests { @Test fun `present - select a room and forward successful`() = runTest { val presenter = aPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -123,7 +123,7 @@ class ForwardMessagesPresenterTests { fun `present - select a room and forward failed, then clear`() = runTest { val room = FakeMatrixRoom() val presenter = aPresenter(fakeMatrixRoom = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -151,7 +151,7 @@ class ForwardMessagesPresenterTests { @Test fun `present - select and remove a room`() = runTest { val presenter = aPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt index 2b66d2cf9b..3c31fa49d3 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt @@ -19,7 +19,7 @@ package io.element.android.features.messages.media.viewer import android.net.Uri -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -50,7 +50,7 @@ class MediaViewerPresenterTest { val mediaLoader = FakeMediaLoader() val mediaActions = FakeLocalMediaActions() val presenter = aMediaViewerPresenter(mediaLoader, mediaActions) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { var state = awaitItem() @@ -71,7 +71,7 @@ class MediaViewerPresenterTest { val mediaActions = FakeLocalMediaActions() val snackbarDispatcher = SnackbarDispatcher() val presenter = aMediaViewerPresenter(mediaLoader, mediaActions, snackbarDispatcher) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { var state = awaitItem() @@ -117,7 +117,7 @@ class MediaViewerPresenterTest { val mediaLoader = FakeMediaLoader() val mediaActions = FakeLocalMediaActions() val presenter = aMediaViewerPresenter(mediaLoader, mediaActions) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { mediaLoader.shouldFail = true diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt index 090dd36dbe..e6b0dee0c9 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/report/ReportMessagePresenterTests.kt @@ -16,7 +16,7 @@ package io.element.android.features.messages.report -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -36,7 +36,7 @@ class ReportMessagePresenterTests { @Test fun `presenter - initial state`() = runTest { val presenter = aPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -49,7 +49,7 @@ class ReportMessagePresenterTests { @Test fun `presenter - update reason`() = runTest { val presenter = aPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -63,7 +63,7 @@ class ReportMessagePresenterTests { @Test fun `presenter - toggle block user`() = runTest { val presenter = aPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -81,7 +81,7 @@ class ReportMessagePresenterTests { fun `presenter - handle successful report and block user`() = runTest { val room = FakeMatrixRoom() val presenter = aPresenter(matrixRoom = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -98,7 +98,7 @@ class ReportMessagePresenterTests { fun `presenter - handle successful report`() = runTest { val room = FakeMatrixRoom() val presenter = aPresenter(matrixRoom = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -115,7 +115,7 @@ class ReportMessagePresenterTests { givenReportContentResult(Result.failure(Exception("Failed to report content"))) } val presenter = aPresenter(matrixRoom = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt index d3fda7b881..812b925280 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt @@ -19,7 +19,7 @@ package io.element.android.features.messages.textcomposer import android.net.Uri -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.ReceiveTurbine import app.cash.turbine.test @@ -78,7 +78,7 @@ class MessageComposerPresenterTest { @Test fun `present - initial state`() = runTest { val presenter = createPresenter(this) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -94,7 +94,7 @@ class MessageComposerPresenterTest { @Test fun `present - toggle fullscreen`() = runTest { val presenter = createPresenter(this) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -110,7 +110,7 @@ class MessageComposerPresenterTest { @Test fun `present - change message`() = runTest { val presenter = createPresenter(this) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -128,7 +128,7 @@ class MessageComposerPresenterTest { @Test fun `present - change mode to edit`() = runTest { val presenter = createPresenter(this) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { var state = awaitItem() @@ -146,7 +146,7 @@ class MessageComposerPresenterTest { @Test fun `present - change mode to reply`() = runTest { val presenter = createPresenter(this) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { var state = awaitItem() @@ -163,7 +163,7 @@ class MessageComposerPresenterTest { @Test fun `present - change mode to quote`() = runTest { val presenter = createPresenter(this) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { var state = awaitItem() @@ -180,7 +180,7 @@ class MessageComposerPresenterTest { @Test fun `present - send message`() = runTest { val presenter = createPresenter(this) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -202,7 +202,7 @@ class MessageComposerPresenterTest { this, fakeMatrixRoom, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -233,7 +233,7 @@ class MessageComposerPresenterTest { this, fakeMatrixRoom, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -264,7 +264,7 @@ class MessageComposerPresenterTest { this, fakeMatrixRoom, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -291,7 +291,7 @@ class MessageComposerPresenterTest { @Test fun `present - Open attachments menu`() = runTest { val presenter = createPresenter(this) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -304,7 +304,7 @@ class MessageComposerPresenterTest { @Test fun `present - Dismiss attachments menu`() = runTest { val presenter = createPresenter(this) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -338,7 +338,7 @@ class MessageComposerPresenterTest { ) ) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -372,7 +372,7 @@ class MessageComposerPresenterTest { ) ) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -390,7 +390,7 @@ class MessageComposerPresenterTest { givenResult(null) // Simulate a user canceling the flow givenMimeType(MimeTypes.Images) } - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -410,7 +410,7 @@ class MessageComposerPresenterTest { ) ) val presenter = createPresenter(this, room = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -431,7 +431,7 @@ class MessageComposerPresenterTest { fun `present - Take photo`() = runTest { val room = FakeMatrixRoom() val presenter = createPresenter(this, room = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -447,7 +447,7 @@ class MessageComposerPresenterTest { fun `present - Record video`() = runTest { val room = FakeMatrixRoom() val presenter = createPresenter(this, room = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -464,7 +464,7 @@ class MessageComposerPresenterTest { givenSendMediaResult(Result.failure(Exception())) } val presenter = createPresenter(this, room = room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt index c1d414c633..0dfe6fd53c 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.messages.timeline -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -42,7 +42,7 @@ class TimelinePresenterTest { @Test fun `present - initial state`() = runTest { val presenter = createTimelinePresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -55,7 +55,7 @@ class TimelinePresenterTest { @Test fun `present - load more`() = runTest { val presenter = createTimelinePresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -74,7 +74,7 @@ class TimelinePresenterTest { @Test fun `present - set highlighted event`() = runTest { val presenter = createTimelinePresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -97,7 +97,7 @@ class TimelinePresenterTest { ) ) val presenter = createTimelinePresenter(timeline) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { assertThat(timeline.sendReadReceiptCount).isEqualTo(0) @@ -121,7 +121,7 @@ class TimelinePresenterTest { ) ) val presenter = createTimelinePresenter(timeline) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { assertThat(timeline.sendReadReceiptCount).isEqualTo(0) @@ -145,7 +145,7 @@ class TimelinePresenterTest { ) ) val presenter = createTimelinePresenter(timeline) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { assertThat(timeline.sendReadReceiptCount).isEqualTo(0) @@ -165,7 +165,7 @@ class TimelinePresenterTest { fun `present - covers hasNewItems scenarios`() = runTest { val timeline = FakeMatrixTimeline() val presenter = createTimelinePresenter(timeline) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt index 237cb81d38..1c40483ffe 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt @@ -16,7 +16,7 @@ package io.element.android.features.messages.timeline.components.customreaction -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -32,7 +32,7 @@ class CustomReactionPresenterTests { @Test fun `present - handle selecting and de-selecting an event`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt index 1e467b82af..4f4f0a0ee4 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/retrysendmenu/RetrySendMenuPresenterTests.kt @@ -16,7 +16,7 @@ package io.element.android.features.messages.timeline.components.retrysendmenu -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -35,7 +35,7 @@ class RetrySendMenuPresenterTests { @Test fun `present - handle event selected`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -48,7 +48,7 @@ class RetrySendMenuPresenterTests { @Test fun `present - handle dismiss`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -63,7 +63,7 @@ class RetrySendMenuPresenterTests { @Test fun `present - handle resend with transactionId`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -79,7 +79,7 @@ class RetrySendMenuPresenterTests { @Test fun `present - handle resend without transactionId`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -96,7 +96,7 @@ class RetrySendMenuPresenterTests { @Test fun `present - handle resend with error`() = runTest { room.givenRetrySendMessageResult(Result.failure(IllegalStateException("An error"))) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -112,7 +112,7 @@ class RetrySendMenuPresenterTests { @Test fun `present - handle remove failed message with transactionId`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -128,7 +128,7 @@ class RetrySendMenuPresenterTests { @Test fun `present - handle remove failed message without transactionId`() = runTest { - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -145,7 +145,7 @@ class RetrySendMenuPresenterTests { @Test fun `present - handle remove failed message with error`() = runTest { room.givenRetrySendMessageResult(Result.failure(IllegalStateException("An error"))) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt b/features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt index f415cd795f..d336e5b466 100644 --- a/features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt +++ b/features/onboarding/impl/src/test/kotlin/io/element/android/features/onboarding/impl/OnBoardingPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.onboarding.impl -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -27,7 +27,7 @@ class OnBoardingPresenterTest { @Test fun `present - initial state`() = runTest { val presenter = OnBoardingPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutPresenterTest.kt index 97fa158d09..4b025c10ad 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/about/AboutPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.preferences.impl.about -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -27,7 +27,7 @@ class AboutPresenterTest { @Test fun `present - initial state`() = runTest { val presenter = AboutPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsAnalyticsSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsAnalyticsSettingsPresenterTest.kt index 5382ad0b37..29cc25e5be 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsAnalyticsSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsAnalyticsSettingsPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.preferences.impl.analytics -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -33,7 +33,7 @@ class AnalyticsAnalyticsSettingsPresenterTest { val presenter = AnalyticsSettingsPresenter( analyticsPresenter, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt index 87a556621c..9b1bda3631 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.preferences.impl.developer -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -41,7 +41,7 @@ class DeveloperSettingsPresenterTest { FakeClearCacheUseCase(), rageshakePresenter ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -65,7 +65,7 @@ class DeveloperSettingsPresenterTest { FakeClearCacheUseCase(), rageshakePresenter, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -84,7 +84,7 @@ class DeveloperSettingsPresenterTest { FakeClearCacheUseCase(), rageshakePresenter, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -109,7 +109,7 @@ class DeveloperSettingsPresenterTest { clearCacheUseCase, rageshakePresenter, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt index f3cf23599f..91bb0c12f3 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.preferences.impl.root -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -45,7 +45,7 @@ class PreferencesRootPresenterTest { FakeVersionFormatter(), SnackbarDispatcher(), ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt index 9b868a637d..c0418783dd 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.rageshake.impl.bugreport -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -41,7 +41,7 @@ class BugReportPresenterTest { FakeScreenshotHolder(), this, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -62,7 +62,7 @@ class BugReportPresenterTest { FakeScreenshotHolder(), this, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -81,7 +81,7 @@ class BugReportPresenterTest { FakeScreenshotHolder(), this, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -100,7 +100,7 @@ class BugReportPresenterTest { FakeScreenshotHolder(), this, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -120,7 +120,7 @@ class BugReportPresenterTest { FakeScreenshotHolder(), this, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -139,7 +139,7 @@ class BugReportPresenterTest { FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI), this, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -161,7 +161,7 @@ class BugReportPresenterTest { FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI), this, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -186,7 +186,7 @@ class BugReportPresenterTest { FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI), this, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -215,7 +215,7 @@ class BugReportPresenterTest { FakeScreenshotHolder(screenshotUri = A_SCREENSHOT_URI), this, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt index 2d9834607f..b8b8c4b6d0 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/crash/ui/CrashDetectionPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.rageshake.impl.crash.ui -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -33,7 +33,7 @@ class CrashDetectionPresenterTest { val presenter = DefaultCrashDetectionPresenter( FakeCrashDataStore() ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -46,7 +46,7 @@ class CrashDetectionPresenterTest { val presenter = DefaultCrashDetectionPresenter( FakeCrashDataStore(appHasCrashed = true) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -61,7 +61,7 @@ class CrashDetectionPresenterTest { val presenter = DefaultCrashDetectionPresenter( FakeCrashDataStore(appHasCrashed = true) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -77,7 +77,7 @@ class CrashDetectionPresenterTest { val presenter = DefaultCrashDetectionPresenter( FakeCrashDataStore(appHasCrashed = true, crashData = A_CRASH_DATA) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt index eb49eb450e..02a0fc0794 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/detection/RageshakeDetectionPresenterTest.kt @@ -17,7 +17,7 @@ package io.element.android.features.rageshake.impl.detection import android.graphics.Bitmap -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -59,7 +59,7 @@ class RageshakeDetectionPresenterTest { rageshakeDataStore = rageshakeDataStore, ) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -83,7 +83,7 @@ class RageshakeDetectionPresenterTest { rageshakeDataStore = rageshakeDataStore, ) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -108,7 +108,7 @@ class RageshakeDetectionPresenterTest { rageshakeDataStore = rageshakeDataStore, ) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -142,7 +142,7 @@ class RageshakeDetectionPresenterTest { rageshakeDataStore = rageshakeDataStore, ) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -176,7 +176,7 @@ class RageshakeDetectionPresenterTest { rageshakeDataStore = rageshakeDataStore, ) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/preferences/RageshakePreferencesPresenterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/preferences/RageshakePreferencesPresenterTest.kt index b01ce22645..56759c360c 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/preferences/RageshakePreferencesPresenterTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/preferences/RageshakePreferencesPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.rageshake.impl.preferences -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -34,7 +34,7 @@ class RageshakePreferencesPresenterTest { FakeRageShake(isAvailableValue = true), FakeRageshakeDataStore(isEnabled = true) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -50,7 +50,7 @@ class RageshakePreferencesPresenterTest { FakeRageShake(isAvailableValue = false), FakeRageshakeDataStore(isEnabled = true) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -66,7 +66,7 @@ class RageshakePreferencesPresenterTest { FakeRageShake(isAvailableValue = true), FakeRageshakeDataStore(isEnabled = true) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -85,7 +85,7 @@ class RageshakePreferencesPresenterTest { FakeRageShake(isAvailableValue = true), FakeRageshakeDataStore(isEnabled = true) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt index ccd1476c3a..08d6a58535 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt @@ -16,7 +16,7 @@ package io.element.android.features.roomdetails -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -60,7 +60,7 @@ class RoomDetailsPresenterTests { fun `present - initial state is created from room info`() = runTest { val room = aMatrixRoom() val presenter = aRoomDetailsPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -79,7 +79,7 @@ class RoomDetailsPresenterTests { fun `present - initial state with no room name`() = runTest { val room = aMatrixRoom(name = null) val presenter = aRoomDetailsPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -101,7 +101,7 @@ class RoomDetailsPresenterTests { givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers)) } val presenter = aRoomDetailsPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -117,7 +117,7 @@ class RoomDetailsPresenterTests { givenCanInviteResult(Result.success(true)) } val presenter = aRoomDetailsPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { // Initially false @@ -135,7 +135,7 @@ class RoomDetailsPresenterTests { givenCanInviteResult(Result.success(false)) } val presenter = aRoomDetailsPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { assertThat(awaitItem().canInvite).isFalse() @@ -148,7 +148,7 @@ class RoomDetailsPresenterTests { givenCanInviteResult(Result.failure(Throwable("Whoops"))) } val presenter = aRoomDetailsPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { assertThat(awaitItem().canInvite).isFalse() @@ -164,7 +164,7 @@ class RoomDetailsPresenterTests { givenCanInviteResult(Result.success(false)) } val presenter = aRoomDetailsPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { // Initially false @@ -193,7 +193,7 @@ class RoomDetailsPresenterTests { givenCanInviteResult(Result.success(false)) } val presenter = aRoomDetailsPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { // Initially false @@ -222,7 +222,7 @@ class RoomDetailsPresenterTests { givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true)) } val presenter = aRoomDetailsPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -243,7 +243,7 @@ class RoomDetailsPresenterTests { givenCanInviteResult(Result.success(false)) } val presenter = aRoomDetailsPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { // Initially false @@ -264,7 +264,7 @@ class RoomDetailsPresenterTests { givenCanInviteResult(Result.success(false)) } val presenter = aRoomDetailsPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { // Initially false, and no further events @@ -280,7 +280,7 @@ class RoomDetailsPresenterTests { } val presenter = aRoomDetailsPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { // The initial state is "hidden" and no further state changes happen @@ -296,7 +296,7 @@ class RoomDetailsPresenterTests { } val presenter = aRoomDetailsPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { // Ignore the initial state @@ -314,7 +314,7 @@ class RoomDetailsPresenterTests { val leaveRoomPresenter = LeaveRoomPresenterFake() val room = aMatrixRoom() val presenter = aRoomDetailsPresenter(room, leaveRoomPresenter) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { awaitItem().eventSink(RoomDetailsEvent.LeaveRoom) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt index 20d253f3fb..e43703e235 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt @@ -17,7 +17,7 @@ package io.element.android.features.roomdetails.edit import android.net.Uri -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -82,7 +82,7 @@ class RoomDetailsEditPresenterTest { val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL) val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -109,7 +109,7 @@ class RoomDetailsEditPresenterTest { } val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { // Initially false @@ -135,7 +135,7 @@ class RoomDetailsEditPresenterTest { } val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { // Initially false @@ -161,7 +161,7 @@ class RoomDetailsEditPresenterTest { } val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { // Initially false @@ -183,7 +183,7 @@ class RoomDetailsEditPresenterTest { val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL) val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -229,7 +229,7 @@ class RoomDetailsEditPresenterTest { val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -250,7 +250,7 @@ class RoomDetailsEditPresenterTest { val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -271,7 +271,7 @@ class RoomDetailsEditPresenterTest { val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -323,7 +323,7 @@ class RoomDetailsEditPresenterTest { val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -373,7 +373,7 @@ class RoomDetailsEditPresenterTest { val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -398,7 +398,7 @@ class RoomDetailsEditPresenterTest { val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -422,7 +422,7 @@ class RoomDetailsEditPresenterTest { val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -445,7 +445,7 @@ class RoomDetailsEditPresenterTest { val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -470,7 +470,7 @@ class RoomDetailsEditPresenterTest { val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -495,7 +495,7 @@ class RoomDetailsEditPresenterTest { val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -561,7 +561,7 @@ class RoomDetailsEditPresenterTest { val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -580,7 +580,7 @@ class RoomDetailsEditPresenterTest { private suspend fun saveAndAssertFailure(room: MatrixRoom, event: RoomDetailsEditEvents) { val presenter = aRoomDetailsEditPresenter(room) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt index 8600cefeac..ede7342882 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.roomdetails.impl.invite -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -52,7 +52,7 @@ internal class RoomInviteMembersPresenterTest { coroutineDispatchers = testCoroutineDispatchers() ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -74,7 +74,7 @@ internal class RoomInviteMembersPresenterTest { coroutineDispatchers = testCoroutineDispatchers() ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -95,7 +95,7 @@ internal class RoomInviteMembersPresenterTest { roomMemberListDataSource = createDataSource(FakeMatrixRoom()), coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -121,7 +121,7 @@ internal class RoomInviteMembersPresenterTest { roomMemberListDataSource = createDataSource(FakeMatrixRoom()), coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -173,7 +173,7 @@ internal class RoomInviteMembersPresenterTest { ), coroutineDispatchers = coroutineDispatchers ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -232,7 +232,7 @@ internal class RoomInviteMembersPresenterTest { coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -270,7 +270,7 @@ internal class RoomInviteMembersPresenterTest { coroutineDispatchers = testCoroutineDispatchers() ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -298,7 +298,7 @@ internal class RoomInviteMembersPresenterTest { roomMemberListDataSource = createDataSource(FakeMatrixRoom()), coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -339,7 +339,7 @@ internal class RoomInviteMembersPresenterTest { roomMemberListDataSource = createDataSource(FakeMatrixRoom()), coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true) ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt index c3a79481e6..9d21e668f0 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt @@ -16,7 +16,7 @@ package io.element.android.features.roomdetails.members -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth @@ -44,7 +44,7 @@ class RoomMemberListPresenterTests { @Test fun `search is done automatically on start, but is async`() = runTest { val presenter = createPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -63,7 +63,7 @@ class RoomMemberListPresenterTests { @Test fun `open search`() = runTest { val presenter = createPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -79,7 +79,7 @@ class RoomMemberListPresenterTests { @Test fun `search for something which is not found`() = runTest { val presenter = createPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -97,7 +97,7 @@ class RoomMemberListPresenterTests { @Test fun `search for something which is found`() = runTest { val presenter = createPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -122,7 +122,7 @@ class RoomMemberListPresenterTests { givenCanInviteResult(Result.success(true)) } ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -138,7 +138,7 @@ class RoomMemberListPresenterTests { givenCanInviteResult(Result.success(false)) } ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -154,7 +154,7 @@ class RoomMemberListPresenterTests { givenCanInviteResult(Result.failure(Throwable("Eek"))) } ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt index 94b940bb17..71df3ad633 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/details/RoomMemberDetailsPresenterTests.kt @@ -16,7 +16,7 @@ package io.element.android.features.roomdetails.members.details -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth @@ -45,7 +45,7 @@ class RoomMemberDetailsPresenterTests { givenRoomMembersState(MatrixRoomMembersState.Ready(listOf(roomMember))) } val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -69,7 +69,7 @@ class RoomMemberDetailsPresenterTests { givenRoomMembersState(MatrixRoomMembersState.Ready(listOf(roomMember))) } val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -89,7 +89,7 @@ class RoomMemberDetailsPresenterTests { givenRoomMembersState(MatrixRoomMembersState.Ready(listOf(roomMember))) } val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -105,7 +105,7 @@ class RoomMemberDetailsPresenterTests { val room = aMatrixRoom() val roomMember = aRoomMember() val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -126,7 +126,7 @@ class RoomMemberDetailsPresenterTests { val room = aMatrixRoom() val roomMember = aRoomMember() val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -147,7 +147,7 @@ class RoomMemberDetailsPresenterTests { val matrixClient = FakeMatrixClient() matrixClient.givenIgnoreUserResult(Result.failure(A_THROWABLE)) val presenter = RoomMemberDetailsPresenter(matrixClient, room, roomMember.userId) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -166,7 +166,7 @@ class RoomMemberDetailsPresenterTests { val room = aMatrixRoom() val roomMember = aRoomMember() val presenter = RoomMemberDetailsPresenter(FakeMatrixClient(), room, roomMember.userId) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index 0ead16da45..ce8e83c70d 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -16,7 +16,7 @@ package io.element.android.features.roomlist.impl -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth @@ -61,7 +61,7 @@ class RoomListPresenterTests { @Test fun `present - should start with no user and then load user with success`() = runTest { val presenter = createRoomListPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -81,7 +81,7 @@ class RoomListPresenterTests { userAvatarURLString = Result.failure(AN_EXCEPTION), ) val presenter = createRoomListPresenter(matrixClient) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -94,7 +94,7 @@ class RoomListPresenterTests { @Test fun `present - should filter room with success`() = runTest { val presenter = createRoomListPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -115,7 +115,7 @@ class RoomListPresenterTests { roomSummaryDataSource = roomSummaryDataSource ) val presenter = createRoomListPresenter(matrixClient) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -138,7 +138,7 @@ class RoomListPresenterTests { roomSummaryDataSource = roomSummaryDataSource ) val presenter = createRoomListPresenter(matrixClient) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { roomSummaryDataSource.postAllRooms(listOf(aRoomSummaryFilled())) @@ -167,7 +167,7 @@ class RoomListPresenterTests { roomSummaryDataSource = roomSummaryDataSource ) val presenter = createRoomListPresenter(matrixClient) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { roomSummaryDataSource.postAllRooms(listOf(aRoomSummaryFilled())) @@ -213,7 +213,7 @@ class RoomListPresenterTests { givenVerifiedStatus(SessionVerifiedStatus.NotVerified) }, ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val eventSink = awaitItem().eventSink @@ -229,7 +229,7 @@ class RoomListPresenterTests { val inviteStateFlow = MutableStateFlow(InvitesState.NoInvites) val inviteStateDataSource = FakeInviteDataSource(inviteStateFlow) val presenter = createRoomListPresenter(inviteStateDataSource = inviteStateDataSource) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -249,7 +249,7 @@ class RoomListPresenterTests { @Test fun `present - show context menu`() = runTest { val presenter = createRoomListPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -267,7 +267,7 @@ class RoomListPresenterTests { @Test fun `present - hide context menu`() = runTest { val presenter = createRoomListPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -290,7 +290,7 @@ class RoomListPresenterTests { fun `present - leave room calls into leave room presenter`() = runTest { val leaveRoomPresenter = LeaveRoomPresenterFake() val presenter = createRoomListPresenter(leaveRoomPresenter = leaveRoomPresenter) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSourceTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSourceTest.kt index b67a6c6b43..88e69068fd 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSourceTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSourceTest.kt @@ -16,7 +16,7 @@ package io.element.android.features.roomlist.impl.datasource -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth @@ -40,7 +40,7 @@ internal class DefaultInviteStateDataSourceTest { val seenStore = FakeSeenInvitesStore() val dataSource = DefaultInviteStateDataSource(client, seenStore, testCoroutineDispatchers()) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { dataSource.inviteState() }.test { Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NoInvites) @@ -55,7 +55,7 @@ internal class DefaultInviteStateDataSourceTest { val seenStore = FakeSeenInvitesStore() val dataSource = DefaultInviteStateDataSource(client, seenStore, testCoroutineDispatchers()) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { dataSource.inviteState() }.test { skipItems(1) @@ -72,7 +72,7 @@ internal class DefaultInviteStateDataSourceTest { seenStore.publishRoomIds(setOf(A_ROOM_ID)) val dataSource = DefaultInviteStateDataSource(client, seenStore, testCoroutineDispatchers(useUnconfinedTestDispatcher = true)) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { dataSource.inviteState() }.test { skipItems(1) @@ -89,7 +89,7 @@ internal class DefaultInviteStateDataSourceTest { seenStore.publishRoomIds(setOf(A_ROOM_ID)) val dataSource = DefaultInviteStateDataSource(client, seenStore, testCoroutineDispatchers(useUnconfinedTestDispatcher = true)) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { dataSource.inviteState() }.test { skipItems(1) @@ -105,7 +105,7 @@ internal class DefaultInviteStateDataSourceTest { val seenStore = FakeSeenInvitesStore() val dataSource = DefaultInviteStateDataSource(client, seenStore, testCoroutineDispatchers()) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { dataSource.inviteState() }.test { // Initially there are no invites diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt index 0b58c125de..82664f0e03 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionPresenterTests.kt @@ -16,7 +16,7 @@ package io.element.android.features.verifysession.impl -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.ReceiveTurbine import app.cash.turbine.test @@ -36,7 +36,7 @@ class VerifySelfSessionPresenterTests { @Test fun `present - Initial state is received`() = runTest { val presenter = createPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { assertThat(awaitItem().verificationFlowStep).isEqualTo(VerificationStep.Initial) @@ -47,7 +47,7 @@ class VerifySelfSessionPresenterTests { fun `present - Handles requestVerification`() = runTest { val service = FakeSessionVerificationService() val presenter = createPresenter(service) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { requestVerificationAndAwaitVerifyingState(service) @@ -58,7 +58,7 @@ class VerifySelfSessionPresenterTests { fun `present - Handles startSasVerification`() = runTest { val service = FakeSessionVerificationService() val presenter = createPresenter(service) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -77,7 +77,7 @@ class VerifySelfSessionPresenterTests { @Test fun `present - Cancelation on initial state does nothing`() = runTest { val presenter = createPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -92,7 +92,7 @@ class VerifySelfSessionPresenterTests { fun `present - A fail in the flow cancels it`() = runTest { val service = FakeSessionVerificationService() val presenter = createPresenter(service) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val state = requestVerificationAndAwaitVerifyingState(service) @@ -109,7 +109,7 @@ class VerifySelfSessionPresenterTests { fun `present - Canceling the flow once it's verifying cancels it`() = runTest { val service = FakeSessionVerificationService() val presenter = createPresenter(service) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val state = requestVerificationAndAwaitVerifyingState(service) @@ -123,7 +123,7 @@ class VerifySelfSessionPresenterTests { fun `present - When verifying, if we receive another challenge we ignore it`() = runTest { val service = FakeSessionVerificationService() val presenter = createPresenter(service) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { requestVerificationAndAwaitVerifyingState(service) @@ -136,7 +136,7 @@ class VerifySelfSessionPresenterTests { fun `present - Restart after cancelation returns to requesting verification`() = runTest { val service = FakeSessionVerificationService() val presenter = createPresenter(service) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val state = requestVerificationAndAwaitVerifyingState(service) @@ -158,7 +158,7 @@ class VerifySelfSessionPresenterTests { givenEmojiList(emojis) } val presenter = createPresenter(service) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val state = requestVerificationAndAwaitVerifyingState(service) @@ -172,7 +172,7 @@ class VerifySelfSessionPresenterTests { fun `present - When verification is declined, the flow is canceled`() = runTest { val service = FakeSessionVerificationService() val presenter = createPresenter(service) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val state = requestVerificationAndAwaitVerifyingState(service) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f0759ee705..9dc16c1ae6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ android_gradle_plugin = "8.0.2" kotlin = "1.8.22" ksp = "1.8.22-1.0.11" -molecule = "0.11.0" +molecule = "1.0.0" # AndroidX material = "1.9.0" diff --git a/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt index 24b174426c..f501bcee8a 100644 --- a/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt +++ b/libraries/permissions/impl/src/test/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenterTest.kt @@ -18,7 +18,7 @@ package io.element.android.libraries.permissions.impl -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.accompanist.permissions.ExperimentalPermissionsApi @@ -41,7 +41,7 @@ class DefaultPermissionsPresenterTest { permissionsStore, permissionStateProvider ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -64,7 +64,7 @@ class DefaultPermissionsPresenterTest { permissionsStore, permissionStateProvider ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -84,7 +84,7 @@ class DefaultPermissionsPresenterTest { permissionsStore, permissionStateProvider ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -113,7 +113,7 @@ class DefaultPermissionsPresenterTest { permissionsStore, permissionStateProvider ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() @@ -142,7 +142,7 @@ class DefaultPermissionsPresenterTest { permissionsStore, permissionStateProvider ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) @@ -164,7 +164,7 @@ class DefaultPermissionsPresenterTest { permissionsStore, permissionStateProvider ) - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() diff --git a/libraries/permissions/noop/src/test/kotlin/io/element/android/libraries/permissions/noop/NoopPermissionsPresenterTest.kt b/libraries/permissions/noop/src/test/kotlin/io/element/android/libraries/permissions/noop/NoopPermissionsPresenterTest.kt index 9992480436..912edd9294 100644 --- a/libraries/permissions/noop/src/test/kotlin/io/element/android/libraries/permissions/noop/NoopPermissionsPresenterTest.kt +++ b/libraries/permissions/noop/src/test/kotlin/io/element/android/libraries/permissions/noop/NoopPermissionsPresenterTest.kt @@ -16,7 +16,7 @@ package io.element.android.libraries.permissions.noop -import app.cash.molecule.RecompositionClock +import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat @@ -27,7 +27,7 @@ class NoopPermissionsPresenterTest { @Test fun `present - initial state`() = runTest { val presenter = NoopPermissionsPresenter() - moleculeFlow(RecompositionClock.Immediate) { + moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitItem() From 85c597b79e11664b73a4fa2b4c7f88de47860293 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 20 Jul 2023 05:51:05 +0000 Subject: [PATCH 012/251] Update screenshots --- ...ineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png} | 0 ...ineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png} | 0 ...ineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png} | 0 ...ineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png} | 0 ...ineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png} | 0 ...ineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png} | 0 ...ItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png} | 0 ...ItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png} | 0 ...ItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png} | 0 ...ItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png} | 0 ...dHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png} | 0 ...dHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png} | 0 ...eactionsViewCollapsedPreview-D-6_7_null,NEXUS_5,1.0,en].png | 3 --- ...eactionsViewCollapsedPreview-N-6_8_null,NEXUS_5,1.0,en].png | 3 --- ...ReactionsViewExpandedPreview-D-7_8_null,NEXUS_5,1.0,en].png | 3 --- ...ReactionsViewExpandedPreview-N-7_9_null,NEXUS_5,1.0,en].png | 3 --- ...lineItemReactionsViewPreview-D-5_6_null,NEXUS_5,1.0,en].png | 3 --- ...lineItemReactionsViewPreview-N-5_7_null,NEXUS_5,1.0,en].png | 3 --- 18 files changed, 18 deletions(-) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-8_9_null_0,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-8_9_null_1,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-8_9_null_2,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-8_10_null_0,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-8_10_null_1,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-8_10_null_2,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-9_10_null_0,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-9_10_null_1,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-9_11_null_0,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-9_11_null_1,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-10_11_null,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-10_12_null,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png} (100%) delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewCollapsedPreview-D-6_7_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewCollapsedPreview-N-6_8_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewExpandedPreview-D-7_8_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewExpandedPreview-N-7_9_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-5_6_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-5_7_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-8_9_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-8_9_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-8_9_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-8_9_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-8_9_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-8_9_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-8_10_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-8_10_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-8_10_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-8_10_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-8_10_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-8_10_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-9_10_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-9_10_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-9_10_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-9_10_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-9_11_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-9_11_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-9_11_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-9_11_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-10_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-10_11_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-10_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-10_12_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewCollapsedPreview-D-6_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewCollapsedPreview-D-6_7_null,NEXUS_5,1.0,en].png deleted file mode 100644 index c838261a0d..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewCollapsedPreview-D-6_7_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dfccebdef9523a984d9edc2414aa159ed025aec14f891945abed73472e462df8 -size 13641 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewCollapsedPreview-N-6_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewCollapsedPreview-N-6_8_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 001fbca040..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewCollapsedPreview-N-6_8_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9c971ed744305a474f182bf3d6fde281b9034f99cee4f4207460a733ac8490a7 -size 13464 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewExpandedPreview-D-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewExpandedPreview-D-7_8_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 53c52d9a7d..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewExpandedPreview-D-7_8_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:999475ddc51f74a590e5ef2810ab31222127e55cba01fadbeb8cb8e9058ad6f0 -size 27170 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewExpandedPreview-N-7_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewExpandedPreview-N-7_9_null,NEXUS_5,1.0,en].png deleted file mode 100644 index cbed0a0752..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewExpandedPreview-N-7_9_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6fb7202b53005274d98f0aee8014326f02bb62fcd323b38b4d8a447078499dd8 -size 26965 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-5_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-5_6_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 119678a386..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-5_6_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:82550de45d39c52f34fca45f84b6e9eff53dfef6d2e9622dcaf49c707927d665 -size 7873 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-5_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-5_7_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 896fd1051e..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-5_7_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fc39041ac035aa372640ca7b2e9af254564aa10ce7a7f4c55fbc4bca9f6dc6d9 -size 7844 From d40cfe0e98aca62fef20be263ac5dc5b80e1b31f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 11:07:08 +0200 Subject: [PATCH 013/251] Do not build main branch in this workflow. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7d895d0fda..962405e782 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: pull_request: { } push: - branches: [ main, develop ] + branches: [ develop ] # Enrich gradle.properties for CI/CD env: From 05f2af39305659b81dd1621cca3f40058405c116 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 11:08:09 +0200 Subject: [PATCH 014/251] Add workflow to create app bundle. --- .github/workflows/release.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..6e6e383604 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,31 @@ +name: Create release App Bundle + +on: + workflow_dispatch: + push: + branches: [ main ] + +# Enrich gradle.properties for CI/CD +env: + GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx3072m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.daemon.jvm.options="-Xmx2560m" -Dkotlin.incremental=false + CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 2 --no-daemon + +jobs: + release: + name: Create App Bundle + runs-on: ubuntu-latest + concurrency: + group: ${{ github.ref == 'refs/head/main' && format('build-release-main-{0}', github.sha) }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v3 + - name: Configure gradle + uses: gradle/gradle-build-action@v2.6.1 + - name: Create app bundle + run: ./gradlew bundleRelease $CI_GRADLE_ARG_PROPERTIES + - name: Upload bundle as artifact + uses: actions/upload-artifact@v3 + with: + name: elementx-app-bundle-unsigned + path: | + app/build/outputs/bundle/release/app-release.aab From 114f868c1f65acfa337b8af227a8783008ca57ac Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 11:29:55 +0200 Subject: [PATCH 015/251] Need JDK 17 --- .github/workflows/release.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6e6e383604..800f4c2fe4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,6 +19,11 @@ jobs: cancel-in-progress: true steps: - uses: actions/checkout@v3 + - name: Use JDK 17 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' # See 'Supported distributions' for available options + java-version: '17' - name: Configure gradle uses: gradle/gradle-build-action@v2.6.1 - name: Create app bundle From 584a22792014316226cae94023fae9c26833a869 Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Thu, 20 Jul 2023 12:09:40 +0200 Subject: [PATCH 016/251] maplibre-compose allow customisation of location dot style (#929) - And style it according to our designs. Related to: - https://github.com/vector-im/element-meta/issues/1682 --- .../features/location/impl/MapDefaults.kt | 8 +++++ .../maplibre/compose/MapLocationSettings.kt | 9 +++++ .../libraries/maplibre/compose/MapUpdater.kt | 36 +++++++++++-------- .../libraries/maplibre/compose/MapboxMap.kt | 6 ++-- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/MapDefaults.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/MapDefaults.kt index ac99be1f59..4709c78538 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/MapDefaults.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/MapDefaults.kt @@ -20,6 +20,7 @@ import android.Manifest import android.view.Gravity import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.ui.graphics.Color import com.mapbox.mapboxsdk.camera.CameraPosition import com.mapbox.mapboxsdk.geometry.LatLng import io.element.android.libraries.maplibre.compose.MapLocationSettings @@ -53,6 +54,13 @@ object MapDefaults { val locationSettings: MapLocationSettings get() = MapLocationSettings( locationEnabled = false, + backgroundTintColor = Color.White, + foregroundTintColor = Color.Black, + backgroundStaleTintColor = Color.White, + foregroundStaleTintColor = Color.Black, + accuracyColor = Color.Black, + pulseEnabled = true, + pulseColor = Color.Black, ) val centerCameraPosition = CameraPosition.Builder() diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLocationSettings.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLocationSettings.kt index 4b7b7005f2..69e0e9b1d4 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLocationSettings.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapLocationSettings.kt @@ -18,6 +18,8 @@ package io.element.android.libraries.maplibre.compose +import androidx.compose.ui.graphics.Color + internal val DefaultMapLocationSettings = MapLocationSettings() /** @@ -28,4 +30,11 @@ internal val DefaultMapLocationSettings = MapLocationSettings() */ public data class MapLocationSettings( public val locationEnabled: Boolean = false, + public val backgroundTintColor: Color = Color.Unspecified, + public val foregroundTintColor: Color = Color.Unspecified, + public val backgroundStaleTintColor: Color = Color.Unspecified, + public val foregroundStaleTintColor: Color = Color.Unspecified, + public val accuracyColor: Color = Color.Unspecified, + public val pulseEnabled: Boolean = false, + public val pulseColor: Color = Color.Unspecified ) diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapUpdater.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapUpdater.kt index d7d5f9ca11..e4e3565f22 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapUpdater.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapUpdater.kt @@ -39,6 +39,7 @@ internal class MapPropertiesNode( style: Style, context: Context, cameraPositionState: CameraPositionState, + locationSettings: MapLocationSettings, ) : MapNode { init { @@ -46,7 +47,13 @@ internal class MapPropertiesNode( LocationComponentActivationOptions.Builder(context, style) .locationComponentOptions( LocationComponentOptions.builder(context) - .pulseEnabled(true) + .backgroundTintColor(locationSettings.backgroundTintColor.toArgb()) + .foregroundTintColor(locationSettings.foregroundTintColor.toArgb()) + .backgroundStaleTintColor(locationSettings.backgroundStaleTintColor.toArgb()) + .foregroundStaleTintColor(locationSettings.foregroundStaleTintColor.toArgb()) + .accuracyColor(locationSettings.accuracyColor.toArgb()) + .pulseEnabled(locationSettings.pulseEnabled) + .pulseColor(locationSettings.pulseColor.toArgb()) .build() ) .locationEngineRequest( @@ -116,9 +123,9 @@ internal class MapPropertiesNode( @Composable internal inline fun MapUpdater( cameraPositionState: CameraPositionState, - mapLocationSettings: MapLocationSettings, - mapUiSettings: MapUiSettings, - mapSymbolManagerSettings: MapSymbolManagerSettings, + locationSettings: MapLocationSettings, + uiSettings: MapUiSettings, + symbolManagerSettings: MapSymbolManagerSettings, ) { val mapApplier = currentComposer.applier as MapApplier val map = mapApplier.map @@ -132,21 +139,22 @@ internal inline fun MapUpdater( style = style, context = context, cameraPositionState = cameraPositionState, + locationSettings = locationSettings, ) }, update = { - set(mapLocationSettings.locationEnabled) { map.locationComponent.isLocationComponentEnabled = it } + set(locationSettings.locationEnabled) { map.locationComponent.isLocationComponentEnabled = it } - set(mapUiSettings.compassEnabled) { map.uiSettings.isCompassEnabled = it } - set(mapUiSettings.rotationGesturesEnabled) { map.uiSettings.isRotateGesturesEnabled = it } - set(mapUiSettings.scrollGesturesEnabled) { map.uiSettings.isScrollGesturesEnabled = it } - set(mapUiSettings.tiltGesturesEnabled) { map.uiSettings.isTiltGesturesEnabled = it } - set(mapUiSettings.zoomGesturesEnabled) { map.uiSettings.isZoomGesturesEnabled = it } - set(mapUiSettings.logoGravity) { map.uiSettings.logoGravity = it } - set(mapUiSettings.attributionGravity) { map.uiSettings.attributionGravity = it } - set(mapUiSettings.attributionTintColor) { map.uiSettings.setAttributionTintColor(it.toArgb()) } + set(uiSettings.compassEnabled) { map.uiSettings.isCompassEnabled = it } + set(uiSettings.rotationGesturesEnabled) { map.uiSettings.isRotateGesturesEnabled = it } + set(uiSettings.scrollGesturesEnabled) { map.uiSettings.isScrollGesturesEnabled = it } + set(uiSettings.tiltGesturesEnabled) { map.uiSettings.isTiltGesturesEnabled = it } + set(uiSettings.zoomGesturesEnabled) { map.uiSettings.isZoomGesturesEnabled = it } + set(uiSettings.logoGravity) { map.uiSettings.logoGravity = it } + set(uiSettings.attributionGravity) { map.uiSettings.attributionGravity = it } + set(uiSettings.attributionTintColor) { map.uiSettings.setAttributionTintColor(it.toArgb()) } - set(mapSymbolManagerSettings.iconAllowOverlap) { symbolManager.iconAllowOverlap = it } + set(symbolManagerSettings.iconAllowOverlap) { symbolManager.iconAllowOverlap = it } update(cameraPositionState) { this.cameraPositionState = it } } diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt index 3c3cf3e44f..c67be52c1c 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt @@ -124,9 +124,9 @@ public fun MapboxMap( ) { MapUpdater( cameraPositionState = currentCameraPositionState, - mapUiSettings = currentUiSettings, - mapLocationSettings = currentMapLocationSettings, - mapSymbolManagerSettings = currentSymbolManagerSettings, + uiSettings = currentUiSettings, + locationSettings = currentMapLocationSettings, + symbolManagerSettings = currentSymbolManagerSettings, ) CompositionLocalProvider( LocalCameraPositionState provides cameraPositionState, From 188af2ee98d4534bc3e7191216a1c47fcef2fa4a Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Thu, 20 Jul 2023 12:16:40 +0200 Subject: [PATCH 017/251] Correct location event body (#930) - Now sending the correct body format as agreed with design. This won't be show in EX clients though. Related to: - https://github.com/vector-im/element-meta/issues/1682 --- features/location/impl/build.gradle.kts | 1 - .../location/impl/send/SendLocationPresenter.kt | 15 +++------------ .../impl/send/SendLocationPresenterTest.kt | 7 ++----- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/features/location/impl/build.gradle.kts b/features/location/impl/build.gradle.kts index 1158b5f152..e808eed11c 100644 --- a/features/location/impl/build.gradle.kts +++ b/features/location/impl/build.gradle.kts @@ -45,7 +45,6 @@ dependencies { implementation(projects.libraries.uiStrings) implementation(libs.dagger) implementation(projects.anvilannotations) - implementation(projects.services.toolbox.api) anvil(projects.anvilcodegen) ksp(libs.showkase.processor) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt index d06124539c..595e26e32e 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt @@ -36,12 +36,7 @@ import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.services.analytics.api.AnalyticsService -import io.element.android.services.toolbox.api.systemclock.SystemClock import kotlinx.coroutines.launch -import java.time.Instant -import java.time.ZoneOffset -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter import javax.inject.Inject class SendLocationPresenter @Inject constructor( @@ -50,7 +45,6 @@ class SendLocationPresenter @Inject constructor( private val analyticsService: AnalyticsService, private val messageComposerContext: MessageComposerContext, private val locationActions: LocationActions, - private val systemClock: SystemClock, private val buildMeta: BuildMeta, ) : Presenter { @@ -115,7 +109,7 @@ class SendLocationPresenter @Inject constructor( SendLocationState.Mode.PinLocation -> { val geoUri = event.cameraPosition.toGeoUri() room.sendLocation( - body = generateBody(geoUri, systemClock.epochMillis()), + body = generateBody(geoUri), geoUri = geoUri, description = null, zoomLevel = MapDefaults.DEFAULT_ZOOM.toInt(), @@ -134,7 +128,7 @@ class SendLocationPresenter @Inject constructor( SendLocationState.Mode.SenderLocation -> { val geoUri = event.toGeoUri() room.sendLocation( - body = generateBody(geoUri, systemClock.epochMillis()), + body = generateBody(geoUri), geoUri = geoUri, description = null, zoomLevel = MapDefaults.DEFAULT_ZOOM.toInt(), @@ -158,7 +152,4 @@ private fun SendLocationEvents.SendLocation.toGeoUri(): String = location?.toGeo private fun SendLocationEvents.SendLocation.CameraPosition.toGeoUri(): String = "geo:$lat,$lon" -private fun generateBody(uri: String, epochMillis: Long): String { - val timestamp = ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT) - return "Location was shared at $uri as of $timestamp" -} +private fun generateBody(uri: String): String = "Location was shared at $uri" diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt index 85a6a28cd9..0aa89e89ba 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt @@ -34,7 +34,6 @@ import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.SendLocationInvocation import io.element.android.libraries.textcomposer.MessageComposerMode -import io.element.android.services.toolbox.api.systemclock.SystemClock import kotlinx.coroutines.delay import kotlinx.coroutines.test.runTest import org.junit.Test @@ -46,7 +45,6 @@ class SendLocationPresenterTest { private val fakeAnalyticsService = FakeAnalyticsService() private val messageComposerContextFake = MessageComposerContextFake() private val fakeLocationActions = FakeLocationActions() - private val fakeSystemClock = SystemClock { 0L } private val fakeBuildMeta = aBuildMeta(applicationName = "app name") private val sendLocationPresenter: SendLocationPresenter = SendLocationPresenter( permissionsPresenterFactory = object : PermissionsPresenter.Factory { @@ -56,7 +54,6 @@ class SendLocationPresenterTest { analyticsService = fakeAnalyticsService, messageComposerContext = messageComposerContextFake, locationActions = fakeLocationActions, - systemClock = fakeSystemClock, buildMeta = fakeBuildMeta, ) @@ -292,7 +289,7 @@ class SendLocationPresenterTest { Truth.assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1) Truth.assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo( SendLocationInvocation( - body = "Location was shared at geo:3.0,4.0;u=5.0 as of 1970-01-01T00:00:00Z", + body = "Location was shared at geo:3.0,4.0;u=5.0", geoUri = "geo:3.0,4.0;u=5.0", description = null, zoomLevel = 15, @@ -349,7 +346,7 @@ class SendLocationPresenterTest { Truth.assertThat(fakeMatrixRoom.sentLocations.size).isEqualTo(1) Truth.assertThat(fakeMatrixRoom.sentLocations.last()).isEqualTo( SendLocationInvocation( - body = "Location was shared at geo:0.0,1.0 as of 1970-01-01T00:00:00Z", + body = "Location was shared at geo:0.0,1.0", geoUri = "geo:0.0,1.0", description = null, zoomLevel = 15, From 675b14270988b86f0f2866287f1209c0558d3cc9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 12:01:50 +0200 Subject: [PATCH 018/251] Compute version code and version name from separate component --- plugins/src/main/kotlin/Versions.kt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 3a6c591e19..a3dfa86de9 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -17,10 +17,18 @@ import org.gradle.api.JavaVersion import org.gradle.jvm.toolchain.JavaLanguageVersion -object Versions { - const val versionCode = 100200 - const val versionName = "0.2.0" +// Note: 2 digits max for each value +private const val versionMajor = 0 +private const val versionMinor = 1 +// Note: even values are reserved for regular release, odd values for hotfix release. +// When creating a hotfix, you should decrease the value, since the current value +// is the value for the next regular release. +private const val versionPatch = 2 + +object Versions { + val versionCode = (versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch) * 10 + val versionName = "$versionMajor.$versionMinor.$versionPatch" const val compileSdk = 33 const val targetSdk = 33 const val minSdk = 23 From 14507c9cbcf4c8a8c5707421c61a9a065d0d6503 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 13:20:43 +0200 Subject: [PATCH 019/251] Add release script --- tools/release/download_github_artifacts.py | 154 ++++++++++ tools/release/release.sh | 318 +++++++++++++++++++++ 2 files changed, 472 insertions(+) create mode 100755 tools/release/download_github_artifacts.py create mode 100755 tools/release/release.sh diff --git a/tools/release/download_github_artifacts.py b/tools/release/download_github_artifacts.py new file mode 100755 index 0000000000..892a4affa6 --- /dev/null +++ b/tools/release/download_github_artifacts.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +# +# Copyright 2022 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. +# + +import argparse +import hashlib +import json +import os +# Run `pip3 install requests` if not installed yet +import requests + +# This script downloads artifacts from GitHub. +# Ref: https://docs.github.com/en/rest/actions/artifacts#get-an-artifact + +error = False + +### Arguments + +parser = argparse.ArgumentParser(description='Download artifacts from GitHub.') +parser.add_argument('-t', + '--token', + required=True, + help='The GitHub token with read access.') +parser.add_argument('-a', + '--artifactUrl', + required=True, + help='the artifact_url from GitHub.') +parser.add_argument('-f', + '--filename', + help='the filename, if not provided, will use the artifact name.') +parser.add_argument('-i', + '--ignoreErrors', + help='Ignore errors that can be ignored. Build state and number of artifacts.', + action="store_true") +parser.add_argument('-d', + '--directory', + default="", + help='the target directory, where files will be downloaded. If not provided the build number will be used to create a directory.') +parser.add_argument('-v', + '--verbose', + help="increase output verbosity.", + action="store_true") +parser.add_argument('-s', + '--simulate', + help="simulate action, do not create folder or download any file.", + action="store_true") + +args = parser.parse_args() + +if args.verbose: + print("Argument:") + print(args) + +# Split the artifact URL to get information +# Ex: https://github.com/vector-im/element-android/suites/9293388174/artifacts/435942121 +artifactUrl = args.artifactUrl +if not artifactUrl.startswith('https://github.com/'): + print("❌ Invalid parameter --artifactUrl %s. Must start with 'https://github.com/'" % artifactUrl) + exit(1) +if "/artifacts/" not in artifactUrl: + print("❌ Invalid parameter --artifactUrl %s. Must contain '/artifacts/'" % artifactUrl) + exit(1) +artifactItems = artifactUrl.split("/") +if len(artifactItems) != 9: + print("❌ Invalid parameter --artifactUrl %s. Please check the format." % (artifactUrl)) + exit(1) + +gitHubRepoOwner = artifactItems[3] +gitHubRepo = artifactItems[4] +artifactId = artifactItems[8] + +if args.verbose: + print("gitHubRepoOwner: %s, gitHubRepo: %s, artifactId: %s" % (gitHubRepoOwner, gitHubRepo, artifactId)) + +headers = { + 'Authorization': "Bearer %s" % args.token, + 'Accept': 'application/vnd.github+json' +} +base_url = "https://api.github.com/repos/%s/%s/actions/artifacts/%s" % (gitHubRepoOwner, gitHubRepo, artifactId) + +### Fetch build state + +print("Getting artifacts data of project '%s/%s' artifactId '%s'..." % (gitHubRepoOwner, gitHubRepo, artifactId)) + +if args.verbose: + print("Url: %s" % base_url) + +r = requests.get(base_url, headers=headers) +data = json.loads(r.content.decode()) + +if args.verbose: + print("Json data:") + print(data) + +if args.verbose: + print("Create subfolder %s to download artifacts..." % artifactId) + +if args.directory == "": + targetDir = artifactId +else: + targetDir = args.directory + +if not args.simulate: + os.makedirs(targetDir, exist_ok=True) + +url = data.get("archive_download_url") +if args.filename is not None: + filename = args.filename +else: + filename = data.get("name") + ".zip" + +## Print some info about the artifact origin +commitLink = "https://github.com/%s/%s/commit/%s" % (gitHubRepoOwner, gitHubRepo, data.get("workflow_run").get("head_sha")) +print("Preparing to download artifact `%s`, built from branch: `%s` (commit %s)" % (data.get("name"), data.get("workflow_run").get("head_branch"), commitLink)) + +if args.verbose: + print() + print("Artifact url: %s" % url) + +target = targetDir + "/" + filename +sizeInBytes = data.get("size_in_bytes") +print("Downloading %s to '%s' (file size is %s bytes, this may take a while)..." % (filename, targetDir, sizeInBytes)) +if not args.simulate: + # open file to write in binary mode + with open(target, "wb") as file: + # get request + response = requests.get(url, headers=headers) + # write to file + file.write(response.content) + print("Verifying file size...") + # get the file size + size = os.path.getsize(target) + if sizeInBytes != size: + # error = True + print("Warning, file size mismatch: expecting %s and get %s. This is just a warning for now..." % (sizeInBytes, size)) + +if error: + print("❌ Error(s) occurred, please check the log") + exit(1) +else: + print("Done!") diff --git a/tools/release/release.sh b/tools/release/release.sh new file mode 100755 index 0000000000..4c8a291639 --- /dev/null +++ b/tools/release/release.sh @@ -0,0 +1,318 @@ +#!/usr/bin/env bash + +# +# 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. +# + +printf "\n================================================================================\n" +printf "| Welcome to the release script! |\n" +printf "================================================================================\n" + +printf "Checking environment...\n" +envError=0 + +# Check that bundletool is installed +if ! command -v bundletool &> /dev/null +then + printf "Fatal: bundletool is not installed. You can install it running \`brew install bundletool\`\n" + envError=1 +fi + +# Path of the key store (it's a file) +keyStorePath="${ELEMENT_X_KEYSTORE_PATH}" +if [[ -z "${keyStorePath}" ]]; then + printf "Fatal: ELEMENT_X_KEYSTORE_PATH is not defined in the environment.\n" + envError=1 +fi +# Keystore password +keyStorePassword="${ELEMENT_X_KEYSTORE_PASSWORD}" +if [[ -z "${keyStorePassword}" ]]; then + printf "Fatal: ELEMENT_X_KEYSTORE_PASSWORD is not defined in the environment.\n" + envError=1 +fi +# Key password +keyPassword="${ELEMENT_X_KEY_PASSWORD}" +if [[ -z "${keyPassword}" ]]; then + printf "Fatal: ELEMENT_X_KEY_PASSWORD is not defined in the environment.\n" + envError=1 +fi +# GitHub token +gitHubToken="${ELEMENT_GITHUB_TOKEN}" +if [[ -z "${gitHubToken}" ]]; then + printf "Fatal: ELEMENT_GITHUB_TOKEN is not defined in the environment.\n" + envError=1 +fi +# Android home +androidHome="${ANDROID_HOME}" +if [[ -z "${androidHome}" ]]; then + printf "Fatal: ANDROID_HOME is not defined in the environment.\n" + envError=1 +fi +# @elementbot:matrix.org matrix token / Not mandatory +elementBotToken="${ELEMENT_BOT_MATRIX_TOKEN}" +if [[ -z "${elementBotToken}" ]]; then + printf "Warning: ELEMENT_BOT_MATRIX_TOKEN is not defined in the environment.\n" +fi + +if [ ${envError} == 1 ]; then + exit 1 +fi + +minSdkVersion=23 +buildToolsVersion="32.0.0" +buildToolsPath="${androidHome}/build-tools/${buildToolsVersion}" + +if [[ ! -d ${buildToolsPath} ]]; then + printf "Fatal: ${buildToolsPath} folder not found, ensure that you have installed the SDK version ${buildToolsVersion}.\n" + exit 1 +fi + +# Check if git flow is enabled +git flow config >/dev/null 2>&1 +if [[ $? == 0 ]] +then + printf "Git flow is initialized\n" +else + printf "Git flow is not initialized. Initializing...\n" + # All default value, just set 'v' for tag prefix + git flow init -d -t 'v' +fi + +printf "OK\n" + +printf "\n================================================================================\n" +printf "Ensuring main and develop branches are up to date...\n" + +git checkout main +git pull +git checkout develop +git pull + +printf "\n================================================================================\n" +# Guessing version to propose a default version +versionsFile="./plugins/src/main/kotlin/Versions.kt" +versionMajorCandidate=`grep "val versionMajor" ${versionsFile} | cut -d " " -f6` +versionMinorCandidate=`grep "val versionMinor" ${versionsFile} | cut -d " " -f6` +versionPatchCandidate=`grep "val versionPatch" ${versionsFile} | cut -d " " -f6` +versionCandidate="${versionMajorCandidate}.${versionMinorCandidate}.${versionPatchCandidate}" + +read -p "Please enter the release version (example: ${versionCandidate}). Just press enter if ${versionCandidate} is correct. " version +version=${version:-${versionCandidate}} + +# extract major, minor and patch for future use +versionMajor=`echo ${version} | cut -d "." -f1` +versionMinor=`echo ${version} | cut -d "." -f2` +versionPatch=`echo ${version} | cut -d "." -f3` +nextPatchVersion=$((versionPatch + 2)) + +printf "\n================================================================================\n" +printf "Starting the release ${version}\n" +git flow release start ${version} + +# Note: in case the release is already started and the script is started again, checkout the release branch again. +ret=$? +if [[ $ret -ne 0 ]]; then + printf "Mmh, it seems that the release is already started. Checking out the release branch...\n" + git checkout "release/${version}" +fi + +# Ensure version is OK +versionsFileBak="${versionsFile}.bak" +cp ${versionsFile} ${versionsFileBak} +sed "s/private const val versionMajor = .*/private const val versionMajor = ${versionMajor}/" ${versionsFileBak} > ${versionsFile} +sed "s/private const val versionMinor = .*/private const val versionMinor = ${versionMinor}/" ${versionsFile} > ${versionsFileBak} +sed "s/private const val versionPatch = .*/private const val versionPatch = ${versionPatch}/" ${versionsFileBak} > ${versionsFile} +rm ${versionsFileBak} + +# This commit may have no effect because generally we do not change the version during the release. +git commit -a -m "Setting version for the release ${version}" + +printf "\n================================================================================\n" +printf "Building the bundle locally first...\n" +./gradlew clean bundleRelease + +printf "\n================================================================================\n" +printf "Running towncrier...\n" +yes | towncrier build --version "v${version}" + +printf "\n================================================================================\n" +read -p "Check the file CHANGES.md consistency. It's possible to reorder items (most important changes first) or change their section if relevant. Also an opportunity to fix some typo, or rewrite things. Do not commit your change. Press enter when it's done." + +# Get the changes to use it to create the GitHub release +changelogUrlEncoded=`git diff CHANGES.md | grep ^+ | tail -n +2 | cut -c2- | jq -sRr @uri | sed s/\(/%28/g | sed s/\)/%29/g` + +printf "\n================================================================================\n" +printf "Committing...\n" +git commit -a -m "Changelog for version ${version}" + +printf "\n================================================================================\n" +printf "Creating fastlane file...\n" +printf -v versionMajor2Digits "%02d" ${versionMajor} +printf -v versionMinor2Digits "%02d" ${versionMinor} +printf -v versionPatch2Digits "%02d" ${versionPatch} +fastlaneFile="4${versionMajor2Digits}${versionMinor2Digits}${versionPatch2Digits}0.txt" +fastlanePathFile="./fastlane/metadata/android/en-US/changelogs/${fastlaneFile}" +printf "Main changes in this version: TODO.\nFull changelog: https://github.com/vector-im/element-x-android/releases" > ${fastlanePathFile} + +read -p "I have created the file ${fastlanePathFile}, please edit it and press enter when it's done." +git add ${fastlanePathFile} +git commit -a -m "Adding fastlane file for version ${version}" + +printf "\n================================================================================\n" +printf "OK, finishing the release...\n" +git flow release finish "${version}" + +printf "\n================================================================================\n" +read -p "Done, push the branch 'main' and the new tag (yes/no) default to yes? " doPush +doPush=${doPush:-yes} + +if [ ${doPush} == "yes" ]; then + printf "Pushing branch 'main' and tag 'v${version}'...\n" + git push origin main + git push origin "v${version}" +else + printf "Not pushing, do not forget to push manually!\n" +fi + +printf "\n================================================================================\n" +printf "Checking out develop...\n" +git checkout develop + +# Set next version +printf "\n================================================================================\n" +printf "Setting next version on file '${versionsFile}'...\n" +cp ${versionsFile} ${versionsFileBak} +sed "s/private const val versionPatch = .*/private const val versionPatch = ${nextPatchVersion}/" ${versionsFileBak} > ${versionsFile} +rm ${versionsFileBak} + +printf "\n================================================================================\n" +read -p "I have updated the versions to prepare the next release, please check that the change are correct and press enter so I can commit." + +printf "Committing...\n" +git commit -a -m 'version++' + +printf "\n================================================================================\n" +read -p "Done, push the branch 'develop' (yes/no) default to yes? (A rebase may be necessary in case develop got new commits) " doPush +doPush=${doPush:-yes} + +if [ ${doPush} == "yes" ]; then + printf "Pushing branch 'develop'...\n" + git push origin develop +else + printf "Not pushing, do not forget to push manually!\n" +fi + +printf "\n================================================================================\n" +printf "Wait for the GitHub action https://github.com/vector-im/element-x-android/actions/workflows/release.yml?query=branch%%3Amain to build the 'main' branch.\n" +read -p "After GHA is finished, please enter the artifact URL (for 'elementx-app-bundle-unsigned'): " artifactUrl + +printf "\n================================================================================\n" +printf "Downloading the artifact...\n" + + # Download files +targetPath="./tmp/Element/${version}" + +python3 ./tools/release/download_github_artifacts.py \ + --token ${gitHubToken} \ + --artifactUrl ${artifactUrl} \ + --directory ${targetPath} \ + --ignoreErrors + +printf "\n================================================================================\n" +printf "Unzipping the artifact...\n" + +unzip ${targetPath}/elementx-app-bundle-unsigned.zip -d ${targetPath} + +unsignedBundlePath="${targetPath}/app-release.aab" +signedBundlePath="${targetPath}/app-release-signed.aab" + +printf "\n================================================================================\n" +printf "Signing file ${unsignedBundlePath} with build-tools version ${buildToolsVersion} for min SDK version ${minSdkVersion}...\n" + +cp ${unsignedBundlePath} ${signedBundlePath} + +${buildToolsPath}/apksigner sign \ + -v \ + --ks ${keyStorePath} \ + --ks-pass pass:${keyStorePassword} \ + --ks-key-alias elementx \ + --key-pass pass:${keyPassword} \ + --min-sdk-version ${minSdkVersion} \ + ${signedBundlePath} + +printf "\n================================================================================\n" +printf "Please check the information below:\n" + +printf "Version code: " +bundletool dump manifest --bundle=${signedBundlePath} --xpath=/manifest/@android:versionCode +printf "Version name: " +bundletool dump manifest --bundle=${signedBundlePath} --xpath=/manifest/@android:versionName + +printf "\n" +read -p "Does it look correct? Press enter when it's done." + +printf "\n================================================================================\n" +printf "The file ${signedBundlePath} has been signed and can be uploaded to the PlayStore!\n" + +printf "\n================================================================================\n" +read -p "Do you want to install the application to your device? Make sure there is a connected device first. (yes/no) default to yes " doDeploy +doDeploy=${doDeploy:-yes} + +if [ ${doDeploy} == "yes" ]; then + printf "Building apks...\n" + bundletool build-apks --bundle=${signedBundlePath} --output=${targetPath}/elementx.apks \ + --ks=./app/signature/debug.keystore --ks-pass=pass:android --ks-key-alias=androiddebugkey --key-pass=pass:android \ + --overwrite + printf "Installing apk for your device...\n" + bundletool install-apks --apks=${targetPath}/elementx.apks + read -p "Please run the application on your phone to check that the upgrade went well (no init sync, etc.). Press enter when it's done." +else + printf "Apk will not be deployed!\n" +fi + +printf "\n================================================================================\n" +githubCreateReleaseLink="https://github.com/vector-im/element-x-android/releases/new?tag=v${version}&title=Element%20X%20Android%20v${version}&body=${changelogUrlEncoded}" +printf "Creating the release on gitHub.\n" +printf -- "Open this link: %s\n" ${githubCreateReleaseLink} +printf "Then\n" +printf " - copy paste the section of the file CHANGES.md for this release (if not there yet)\n" +printf " - click on the 'Generate releases notes' button\n" +printf " - Add the file ${signedBundlePath} to the GitHub release.\n" +read -p ". Press enter when it's done. " + +printf "\n================================================================================\n" +printf "Message for the Android internal room:\n\n" +message="@room Element X Android ${version} is ready to be tested. You can get it from https://github.com/vector-im/element-x-android/releases/tag/v${version}. Please report any feedback here. Thanks!" +printf "${message}\n\n" + +if [[ -z "${elementBotToken}" ]]; then + read -p "ELEMENT_BOT_MATRIX_TOKEN is not defined in the environment. Cannot send the message for you. Please send it manually, and press enter when it's done " +else + read -p "Send this message to the room (yes/no) default to yes? " doSend + doSend=${doSend:-yes} + if [ ${doSend} == "yes" ]; then + printf "Sending message...\n" + transactionId=`openssl rand -hex 16` + # Element Android internal + matrixRoomId="!LiSLXinTDCsepePiYW:matrix.org" + curl -X PUT --data $"{\"msgtype\":\"m.text\",\"body\":\"${message}\"}" -H "Authorization: Bearer ${elementBotToken}" https://matrix-client.matrix.org/_matrix/client/r0/rooms/${matrixRoomId}/send/m.room.message/\$local.${transactionId} + else + printf "Message not sent, please send it manually!\n" + fi +fi + +printf "\n================================================================================\n" +printf "Congratulation! Kudos for using this script! Have a nice day!\n" +printf "================================================================================\n" From 8648e8ba53f2175de33750f5b1793644893a9318 Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Thu, 20 Jul 2023 14:29:06 +0200 Subject: [PATCH 020/251] Extract maptiler's map ids (#926) Maptiler custom map ids are only useable by the account that create them. So if we hardcode them forkers won't be able to use the maps even if the bring in their own api key (because they can't access our maps with their api key). Requires to set our map ids in `local.properties` for local development: ``` services.maptiler.lightMapId=9bc819c8-e627-474a-a348-ec144fe3d810 services.maptiler.darkMapId=dea61faf-292b-4774-9660-58fcef89a7f3 ``` --- .github/workflows/build.yml | 2 ++ .github/workflows/maestro.yml | 2 ++ .github/workflows/nightly.yml | 2 ++ .github/workflows/release.yml | 4 ++++ features/location/api/build.gradle.kts | 20 ++++++++++++++++--- .../features/location/api/internal/MapUrls.kt | 10 +++++----- 6 files changed, 32 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 962405e782..b300a2d747 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,6 +40,8 @@ jobs: - name: Assemble debug APK env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} + ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} + ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} run: ./gradlew assembleDebug $CI_GRADLE_ARG_PROPERTIES - name: Upload debug APKs uses: actions/upload-artifact@v3 diff --git a/.github/workflows/maestro.yml b/.github/workflows/maestro.yml index 0349e373bb..01783a1eb6 100644 --- a/.github/workflows/maestro.yml +++ b/.github/workflows/maestro.yml @@ -38,6 +38,8 @@ jobs: run: ./gradlew assembleDebug $CI_GRADLE_ARG_PROPERTIES env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} + ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} + ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} - name: Upload debug APKs uses: actions/upload-artifact@v3 with: diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 95c2deb8eb..956eebe71d 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -36,6 +36,8 @@ jobs: ./gradlew assembleNightly appDistributionUploadNightly $CI_GRADLE_ARG_PROPERTIES env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} + ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} + ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} ELEMENT_ANDROID_NIGHTLY_KEYID: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_KEYID }} ELEMENT_ANDROID_NIGHTLY_KEYPASSWORD: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_KEYPASSWORD }} ELEMENT_ANDROID_NIGHTLY_STOREPASSWORD: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_STOREPASSWORD }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 800f4c2fe4..ed2d8d4fbd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,6 +27,10 @@ jobs: - name: Configure gradle uses: gradle/gradle-build-action@v2.6.1 - name: Create app bundle + env: + ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} + ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} + ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} run: ./gradlew bundleRelease $CI_GRADLE_ARG_PROPERTIES - name: Upload bundle as artifact uses: actions/upload-artifact@v3 diff --git a/features/location/api/build.gradle.kts b/features/location/api/build.gradle.kts index 6de297fe77..bef0e77b67 100644 --- a/features/location/api/build.gradle.kts +++ b/features/location/api/build.gradle.kts @@ -22,12 +22,12 @@ plugins { id("kotlin-parcelize") } -fun readLocalProperty(name: String) = Properties().apply { +fun readLocalProperty(name: String): String? = Properties().apply { try { load(rootProject.file("local.properties").reader()) } catch (ignored: java.io.IOException) { } -}[name] +}.getProperty(name) android { namespace = "io.element.android.features.location.api" @@ -37,9 +37,23 @@ android { type = "string", name = "maptiler_api_key", value = System.getenv("ELEMENT_ANDROID_MAPTILER_API_KEY") - ?: readLocalProperty("services.maptiler.apikey") as? String + ?: readLocalProperty("services.maptiler.apikey") ?: "" ) + resValue( + type = "string", + name = "maptiler_light_map_id", + value = System.getenv("ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID") + ?: readLocalProperty("services.maptiler.lightMapId") + ?: "basic-v2" // fall back to maptiler's default light map. + ) + resValue( + type = "string", + name = "maptiler_dark_map_id", + value = System.getenv("ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID") + ?: readLocalProperty("services.maptiler.darkMapId") + ?: "basic-v2-dark" // fall back to maptiler's default dark map. + ) } } diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt index b6f21a4512..2640896a78 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt @@ -35,7 +35,7 @@ fun staticMapUrl( height: Int, darkMode: Boolean, ): String { - return "${baseUrl(darkMode)}/static/${lon},${lat},${zoom}/${width}x${height}@2x.webp?key=${context.apiKey}&attribution=bottomleft" + return "${context.baseUrl(darkMode)}/static/${lon},${lat},${zoom}/${width}x${height}@2x.webp?key=${context.apiKey}&attribution=bottomleft" } /** @@ -60,15 +60,15 @@ private fun tileStyleUrl( context: Context, darkMode: Boolean, ): String { - return "${baseUrl(darkMode)}/style.json?key=${context.apiKey}" + return "${context.baseUrl(darkMode)}/style.json?key=${context.apiKey}" } -private fun baseUrl(darkMode: Boolean) = +private fun Context.baseUrl(darkMode: Boolean) = "https://api.maptiler.com/maps/" + if (darkMode) - "dea61faf-292b-4774-9660-58fcef89a7f3" + getString(R.string.maptiler_dark_map_id) else - "9bc819c8-e627-474a-a348-ec144fe3d810" + getString(R.string.maptiler_light_map_id) private val Context.apiKey: String get() = getString(R.string.maptiler_api_key) From e3f75195c29f91f18321b2b67aa1ddaadb347da0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 14:53:38 +0200 Subject: [PATCH 021/251] Clear all notifications of the session when the user signs out. --- .../DefaultNotificationDrawerManager.kt | 26 ++++++++++++++++--- .../NotificationBroadcastReceiver.kt | 2 +- .../notifications/NotificationEventQueue.kt | 5 ++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt index 9cd4956dca..f78a1fb45d 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt @@ -23,17 +23,18 @@ import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.SingleIn +import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.user.MatrixUser -import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.push.api.notifications.NotificationDrawerManager import io.element.android.libraries.push.api.store.PushDataStore import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent -import io.element.android.services.appnavstate.api.NavigationState import io.element.android.services.appnavstate.api.AppNavigationStateService +import io.element.android.services.appnavstate.api.NavigationState +import io.element.android.services.appnavstate.api.currentSessionId import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -76,9 +77,16 @@ class DefaultNotificationDrawerManager @Inject constructor( } } + private var currentAppNavigationState: NavigationState? = null + private fun onAppNavigationStateChange(navigationState: NavigationState) { when (navigationState) { - NavigationState.Root -> {} + NavigationState.Root -> { + currentAppNavigationState?.currentSessionId()?.let { sessionId -> + // User signed out, clear all notifications related to the session. + clearAllEvents(sessionId) + } + } is NavigationState.Session -> {} is NavigationState.Space -> {} is NavigationState.Room -> { @@ -93,6 +101,7 @@ class DefaultNotificationDrawerManager @Inject constructor( ) } } + currentAppNavigationState = navigationState } private fun createInitialNotificationState(): NotificationState { @@ -133,12 +142,21 @@ class DefaultNotificationDrawerManager @Inject constructor( /** * Clear all known events and refresh the notification drawer. */ - fun clearAllEvents(sessionId: SessionId) { + fun clearAllMessagesEvents(sessionId: SessionId) { updateEvents { it.clearMessagesForSession(sessionId) } } + /** + * Clear all notifications related to the session and refresh the notification drawer. + */ + fun clearAllEvents(sessionId: SessionId) { + updateEvents { + it.clearAllForSession(sessionId) + } + } + /** * Should be called when the application is currently opened and showing timeline for the given roomId. * Used to ignore events related to that room (no need to display notification) and clean any existing notification on this room. diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiver.kt index d5df1001ca..e7edad37fc 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiver.kt @@ -59,7 +59,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() { defaultNotificationDrawerManager.clearMessagesForRoom(sessionId, roomId) } actionIds.dismissSummary -> - defaultNotificationDrawerManager.clearAllEvents(sessionId) + defaultNotificationDrawerManager.clearAllMessagesEvents(sessionId) actionIds.dismissInvite -> if (roomId != null) { defaultNotificationDrawerManager.clearMembershipNotificationForRoom(sessionId, roomId) } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationEventQueue.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationEventQueue.kt index 97b90476b0..6b6730c904 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationEventQueue.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationEventQueue.kt @@ -154,6 +154,11 @@ data class NotificationEventQueue constructor( queue.removeAll { it is NotifiableMessageEvent && it.sessionId == sessionId } } + fun clearAllForSession(sessionId: SessionId) { + Timber.d("clearAllForSession $sessionId") + queue.removeAll { it.sessionId == sessionId } + } + fun clearMessagesForRoom(sessionId: SessionId, roomId: RoomId) { Timber.d("clearMessageEventOfRoom $sessionId, $roomId") queue.removeAll { it is NotifiableMessageEvent && it.sessionId == sessionId && it.roomId == roomId } From 2fb5929e744e6716bdb443624362991a9ccae5ed Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 15:27:57 +0200 Subject: [PATCH 022/251] Ignore compilation warnings, disabled by default. --- build.gradle.kts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index c03881144e..9272514899 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -90,6 +90,14 @@ allprojects { apply { plugin("org.owasp.dependencycheck") } + + tasks.withType { + // Warnings are potential errors, so stop ignoring them + // This is disabled by default, but the CI will enforce this. + // You can override by passing `-PallWarningsAsErrors=true` in the command line + // Or add a line with "allWarningsAsErrors=true" in your ~/.gradle/gradle.properties file + kotlinOptions.allWarningsAsErrors = project.properties["allWarningsAsErrors"] == "true" + } } // To run a sonar analysis: From b422414e05c60877ba901c585ccf375655cd2f1a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 15:29:47 +0200 Subject: [PATCH 023/251] Ci will not ignore compilation warnings. --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b300a2d747..06684c5eb4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,7 +42,7 @@ jobs: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} - run: ./gradlew assembleDebug $CI_GRADLE_ARG_PROPERTIES + run: ./gradlew assembleDebug -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES - name: Upload debug APKs uses: actions/upload-artifact@v3 with: @@ -72,8 +72,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Compile release sources - run: ./gradlew compileReleaseSources $CI_GRADLE_ARG_PROPERTIES + run: ./gradlew compileReleaseSources -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES - name: Compile nightly sources - run: ./gradlew compileNightlySources $CI_GRADLE_ARG_PROPERTIES + run: ./gradlew compileNightlySources -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES - name: Compile samples minimal run: ./gradlew :samples:minimal:assemble $CI_GRADLE_ARG_PROPERTIES From ac61a8c916f5d69175424d847f97946391fedc27 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 15:32:19 +0200 Subject: [PATCH 024/251] Fix warning (comment out temporary dead code) --- .../libraries/matrix/api/auth/AuthenticationException.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt index e670e02f11..48712b7ddf 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt @@ -22,5 +22,6 @@ sealed class AuthenticationException(message: String) : Exception(message) { class SlidingSyncNotAvailable(message: String) : AuthenticationException(message) class SessionMissing(message: String) : AuthenticationException(message) class Generic(message: String) : AuthenticationException(message) - class OidcError(type: String, message: String) : AuthenticationException(message) + // TODO Oidc + // class OidcError(type: String, message: String) : AuthenticationException(message) } From 56b8969376ef5a0916e30c4258f526d39362f89c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 15:33:00 +0200 Subject: [PATCH 025/251] Fix warning ('when' is exhaustive so 'else' is redundant here) --- .../io/element/android/libraries/mediaupload/api/MediaSender.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt index cfa59d65d3..1622ab2eef 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt @@ -82,7 +82,6 @@ class MediaSender @Inject constructor( progressCallback = progressCallback ) } - else -> Result.failure(IllegalStateException("Unexpected MediaUploadInfo format: $uploadInfo")) } } } From 5db2ebe17919488f8616ff74caacd589d93b3a50 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 15:34:55 +0200 Subject: [PATCH 026/251] Fix warning (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q here) --- .../io/element/android/libraries/mediaupload/ThumbnailFactory.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt index a9ed6319cb..2cee2566d1 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt @@ -69,6 +69,7 @@ class ThumbnailFactory @Inject constructor( cancellationSignal ) } else { + @Suppress("DEPRECATION") ThumbnailUtils.createImageThumbnail( file.path, MediaStore.Images.Thumbnails.MINI_KIND, From d1e7f9345813515978b15cd32ac9ba5bd6b74127 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 15:36:23 +0200 Subject: [PATCH 027/251] Fix warning (remove dead code) --- .../libraries/push/impl/PushersManager.kt | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt index 81a86c5345..f3afb940cd 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt @@ -103,33 +103,6 @@ class PushersManager @Inject constructor( return "{\"cs\":\"$secretForUser\"}" } - suspend fun registerEmailForPush(email: String) { - TODO() - /* - val currentSession = activeSessionHolder.getActiveSession() - val appName = appNameProvider.getAppName() - currentSession.pushersService().addEmailPusher( - email = email, - lang = localeProvider.current().language, - emailBranding = appName, - appDisplayName = appName, - deviceDisplayName = currentSession.sessionParams.deviceId ?: "MOBILE" - ) - */ - } - - fun getPusherForCurrentSession() {}/*: Pusher? { - val session = activeSessionHolder.getSafeActiveSession() ?: return null - val deviceId = session.sessionParams.deviceId - return session.pushersService().getPushers().firstOrNull { it.deviceId == deviceId } - } - */ - - suspend fun unregisterEmailPusher(email: String) { - // val currentSession = activeSessionHolder.getSafeActiveSession() ?: return - // currentSession.pushersService().removeEmailPusher(email) - } - override suspend fun unregisterPusher(matrixClient: MatrixClient, pushKey: String, gateway: String) { matrixClient.pushersService().unsetHttpPusher() } From 201d137e79b3a6a9ca5301b1aaa6b7a5b0e485df Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 15:37:50 +0200 Subject: [PATCH 028/251] Fix warning (suppress, no sure I want to delete this class right now) --- .../libraries/push/impl/notifications/FilteredEventDetector.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/FilteredEventDetector.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/FilteredEventDetector.kt index a24f088998..2d4d27472b 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/FilteredEventDetector.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/FilteredEventDetector.kt @@ -27,7 +27,7 @@ class FilteredEventDetector @Inject constructor( * Returns true if the given event should be ignored. * Used to skip notifications if a non expected message is received. */ - fun shouldBeIgnored(notifiableEvent: NotifiableEvent): Boolean { + fun shouldBeIgnored(@Suppress("UNUSED_PARAMETER") notifiableEvent: NotifiableEvent): Boolean { /* TODO EAx val session = activeSessionDataSource.currentValue?.orNull() ?: return false From b62a174494209a8ab3e06648e78c4f9d5102663a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 15:41:09 +0200 Subject: [PATCH 029/251] Fix warning (comment out dead code due to notification actions not active yet.) --- .../NotificationBroadcastReceiver.kt | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiver.kt index d5df1001ca..93add69062 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiver.kt @@ -19,15 +19,12 @@ package io.element.android.libraries.push.impl.notifications import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import androidx.core.app.RemoteInput import io.element.android.libraries.architecture.bindings import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.push.impl.log.notificationLoggerTag -import io.element.android.services.toolbox.api.systemclock.SystemClock import timber.log.Timber import javax.inject.Inject @@ -39,10 +36,6 @@ private val loggerTag = LoggerTag("NotificationBroadcastReceiver", notificationL class NotificationBroadcastReceiver : BroadcastReceiver() { @Inject lateinit var defaultNotificationDrawerManager: DefaultNotificationDrawerManager - - //@Inject lateinit var activeSessionHolder: ActiveSessionHolder - //@Inject lateinit var analyticsTracker: AnalyticsTracker - @Inject lateinit var clock: SystemClock @Inject lateinit var actionIds: NotificationActionIds override fun onReceive(context: Context?, intent: Intent?) { @@ -81,6 +74,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() { } } + @Suppress("UNUSED_PARAMETER") private fun handleJoinRoom(sessionId: SessionId, roomId: RoomId) { /* activeSessionHolder.getSafeActiveSession()?.let { session -> @@ -94,10 +88,10 @@ class NotificationBroadcastReceiver : BroadcastReceiver() { } } } - */ } + @Suppress("UNUSED_PARAMETER") private fun handleRejectRoom(sessionId: SessionId, roomId: RoomId) { /* activeSessionHolder.getSafeActiveSession()?.let { session -> @@ -109,6 +103,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() { */ } + @Suppress("UNUSED_PARAMETER") private fun handleMarkAsRead(sessionId: SessionId, roomId: RoomId) { /* activeSessionHolder.getActiveSession().let { session -> @@ -123,7 +118,9 @@ class NotificationBroadcastReceiver : BroadcastReceiver() { */ } + @Suppress("UNUSED_PARAMETER") private fun handleSmartReply(intent: Intent, context: Context) { + /* val message = getReplyMessage(intent) val sessionId = intent.getStringExtra(KEY_SESSION_ID)?.let(::SessionId) val roomId = intent.getStringExtra(KEY_ROOM_ID)?.let(::RoomId) @@ -134,13 +131,11 @@ class NotificationBroadcastReceiver : BroadcastReceiver() { // Can this happen? should we update notification? return } - /* activeSessionHolder.getActiveSession().let { session -> session.getRoom(roomId)?.let { room -> sendMatrixEvent(message, threadId, session, room, context) } } - */ } @@ -234,6 +229,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() { */ + /* private fun getReplyMessage(intent: Intent?): String? { if (intent != null) { val remoteInput = RemoteInput.getResultsFromIntent(intent) @@ -243,6 +239,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() { } return null } + */ companion object { const val KEY_SESSION_ID = "sessionID" From 527f2394889f66d93968d3b1e7f9f385edd48bc2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 15:41:44 +0200 Subject: [PATCH 030/251] Fix warning (suppress, no sure I want to delete this class right now) --- .../libraries/push/impl/notifications/OutdatedEventDetector.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/OutdatedEventDetector.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/OutdatedEventDetector.kt index 5b15dc78d2..27713399fc 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/OutdatedEventDetector.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/OutdatedEventDetector.kt @@ -28,7 +28,7 @@ class OutdatedEventDetector @Inject constructor( * Used to clean up notifications if a displayed message has been read on an * other device. */ - fun isMessageOutdated(notifiableEvent: NotifiableEvent): Boolean { + fun isMessageOutdated(@Suppress("UNUSED_PARAMETER") notifiableEvent: NotifiableEvent): Boolean { /* TODO EAx val session = activeSessionDataSource.currentValue?.orNull() ?: return false From d5a93a50e09324788fbb9bade7b5f7fe984cf46a Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Thu, 20 Jul 2023 15:42:35 +0200 Subject: [PATCH 031/251] Add screenshots with ~1.5 lines long desc to expanded location view (#923) This will help in catching alignment regressions. Related to: - https://github.com/vector-im/element-meta/issues/1678 --- .../location/impl/show/ShowLocationStateProvider.kt | 7 +++++++ ...howLocationViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...howLocationViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 3 +++ ...owLocationViewLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...owLocationViewLightPreview_0_null_5,NEXUS_5,1.0,en].png | 3 +++ 5 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_5,NEXUS_5,1.0,en].png diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt index 1865c6b9a4..73bb2d37a4 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationStateProvider.kt @@ -50,6 +50,13 @@ class ShowLocationStateProvider : PreviewParameterProvider { isTrackMyLocation = false, eventSink = {}, ), + ShowLocationState( + Location(1.23, 2.34, 4f), + description = "For some reason I decided to to write a small essay that wraps at just two lines!", + hasLocationPermission = false, + isTrackMyLocation = false, + eventSink = {}, + ), ShowLocationState( Location(1.23, 2.34, 4f), description = "For some reason I decided to write a small essay in the location description. " + diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index b56214c49f..6c81af0a3c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c936d2d804bc9e98fcc49430f11ddaa572b05fc8d3a0df93ad6521ee8e78f708 -size 21806 +oid sha256:6d98aabf3bd99793367632e18d5cf678e75fb5ad872a1cb300a7e939ad0c2683 +size 19725 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b56214c49f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c936d2d804bc9e98fcc49430f11ddaa572b05fc8d3a0df93ad6521ee8e78f708 +size 21806 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index 7701a371c3..ad38501385 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0bb88c64bc68b10b1fb709135f445de5a5e4d78623448d0fef97504e025d5f6d -size 24551 +oid sha256:94be795b626868e8afbcc26c3f0161ddcb946a122c02eeed7e823825c9aeba19 +size 22263 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7701a371c3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0bb88c64bc68b10b1fb709135f445de5a5e4d78623448d0fef97504e025d5f6d +size 24551 From 8a82aab0149a0fe5bcf06b33e39c50c00a0ab2f4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 15:45:18 +0200 Subject: [PATCH 032/251] Fix warning (suppress unused param for now) --- .../impl/timeline/components/event/TimelineItemEncryptedView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt index 9755377b39..cf481a0aec 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt @@ -30,7 +30,7 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun TimelineItemEncryptedView( - content: TimelineItemEncryptedContent, + @Suppress("UNUSED_PARAMETER") content: TimelineItemEncryptedContent, extraPadding: ExtraPadding, modifier: Modifier = Modifier ) { From 45f8f134ae580f0de2c6021795d9fd8ad51fe265 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 15:46:09 +0200 Subject: [PATCH 033/251] Fix warning (suppress unused param for now) --- .../factories/event/TimelineItemContentRedactedFactory.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentRedactedFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentRedactedFactory.kt index dfbbd233c7..9419c7c31d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentRedactedFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentRedactedFactory.kt @@ -23,7 +23,7 @@ import javax.inject.Inject class TimelineItemContentRedactedFactory @Inject constructor() { - fun create(content: RedactedContent): TimelineItemEventContent { + fun create(@Suppress("UNUSED_PARAMETER") content: RedactedContent): TimelineItemEventContent { return TimelineItemRedactedContent } } From dc9212c55999d59b71c6a164a8760fd0d56ffaab Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 15:46:41 +0200 Subject: [PATCH 034/251] Fix warning (suppress unused param for now, EAX does not support sticker right now.) --- .../factories/event/TimelineItemContentStickerFactory.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentStickerFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentStickerFactory.kt index b823791912..1607bb77c4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentStickerFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentStickerFactory.kt @@ -23,7 +23,7 @@ import javax.inject.Inject class TimelineItemContentStickerFactory @Inject constructor() { - fun create(content: StickerContent): TimelineItemEventContent { + fun create(@Suppress("UNUSED_PARAMETER") content: StickerContent): TimelineItemEventContent { return TimelineItemUnknownContent } } From 2872e8faca9d702326b23f904d9945618311934d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 15:47:14 +0200 Subject: [PATCH 035/251] Fix warning (suppress unused param for now) --- .../impl/timeline/components/event/TimelineItemRedactedView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemRedactedView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemRedactedView.kt index 44917c7d5b..749151b55f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemRedactedView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemRedactedView.kt @@ -29,7 +29,7 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun TimelineItemRedactedView( - content: TimelineItemRedactedContent, + @Suppress("UNUSED_PARAMETER") content: TimelineItemRedactedContent, extraPadding: ExtraPadding, modifier: Modifier = Modifier ) { From 5edcffca937aa02c887bf5291f9fa18d83ef4a0c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 15:47:42 +0200 Subject: [PATCH 036/251] Fix warning (suppress unused param for now) --- .../event/TimelineItemContentFailedToParseMessageFactory.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFailedToParseMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFailedToParseMessageFactory.kt index 4dacb76523..e764cfa28f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFailedToParseMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFailedToParseMessageFactory.kt @@ -23,7 +23,7 @@ import javax.inject.Inject class TimelineItemContentFailedToParseMessageFactory @Inject constructor() { - fun create(failedToParseMessageLike: FailedToParseMessageLikeContent): TimelineItemEventContent { + fun create(@Suppress("UNUSED_PARAMETER") failedToParseMessageLike: FailedToParseMessageLikeContent): TimelineItemEventContent { return TimelineItemUnknownContent } } From ffaf24751a9fa3eaf2964fdd1bafe6d0df2327fe Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Thu, 20 Jul 2023 15:59:06 +0200 Subject: [PATCH 037/251] Use correct share my location icon in button (#936) As per newer design specs. Related to: - https://github.com/vector-im/element-meta/issues/1678 --- .../location/impl/send/SendLocationView.kt | 8 ++++++-- .../src/main/res/drawable-night/pin_small.xml | 19 +++++++++++++++++++ .../impl/src/main/res/drawable/pin_small.xml | 19 +++++++++++++++++++ ...ewPreview-D-0_1_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...ewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...ewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...ewPreview-D-0_1_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...ewPreview-D-0_1_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...ewPreview-N-0_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...ewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...ewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...ewPreview-N-0_2_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...ewPreview-N-0_2_null_4,NEXUS_5,1.0,en].png | 4 ++-- 13 files changed, 64 insertions(+), 22 deletions(-) create mode 100644 features/location/impl/src/main/res/drawable-night/pin_small.xml create mode 100644 features/location/impl/src/main/res/drawable/pin_small.xml diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt index d0e9dfd5aa..cdaca94b04 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt @@ -28,7 +28,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.LocationOn import androidx.compose.material.icons.filled.LocationSearching import androidx.compose.material.icons.filled.MyLocation import androidx.compose.material3.ExperimentalMaterial3Api @@ -49,6 +48,7 @@ import io.element.android.features.location.api.Location import io.element.android.features.location.api.internal.centerBottomEdge import io.element.android.features.location.api.internal.rememberTileStyleUrl import io.element.android.features.location.impl.MapDefaults +import io.element.android.features.location.impl.R import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.preview.DayNightPreviews @@ -156,7 +156,11 @@ fun SendLocationView( navigateUp() }, leadingContent = { - Icon(Icons.Default.LocationOn, null) + Icon( + resourceId = R.drawable.pin_small, + contentDescription = null, + tint = Color.Unspecified, + ) }, ) Spacer(modifier = Modifier.height(16.dp + navBarPadding)) diff --git a/features/location/impl/src/main/res/drawable-night/pin_small.xml b/features/location/impl/src/main/res/drawable-night/pin_small.xml new file mode 100644 index 0000000000..2e8a54b70e --- /dev/null +++ b/features/location/impl/src/main/res/drawable-night/pin_small.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/features/location/impl/src/main/res/drawable/pin_small.xml b/features/location/impl/src/main/res/drawable/pin_small.xml new file mode 100644 index 0000000000..0e277a1ed2 --- /dev/null +++ b/features/location/impl/src/main/res/drawable/pin_small.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_0,NEXUS_5,1.0,en].png index fe104f1c2f..c092db7ce4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69535debd585127a4ce8b490ef6682c2e6c3c4d16478e6b9e9687ee1c1133637 -size 20879 +oid sha256:84581aac943c5065f1e5438465ee7d1845555e68aa005d8b596542ce3830dc83 +size 21258 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png index 09e7be69f8..57b3030576 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:356756de9f08042c3c2f3033d3f8a39cd9b49c5cfbcfbc274933c3efedd80d3d -size 34534 +oid sha256:2edcc1efa55df97a1dcb1f90e6b19a3ec042647086de5a932ffd6c2d30009e64 +size 34873 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png index 5311a17e6e..3824f88e7b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51ac3c4bb27d78419f73b99cb24327514ce56a64a03bf74ce41f158c2c3bd516 -size 33605 +oid sha256:ae8ee38742d0c0b418186b0a24fdc85d62ab9152f21f82b5d364ae3274bc1d99 +size 33951 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_3,NEXUS_5,1.0,en].png index fe104f1c2f..c092db7ce4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69535debd585127a4ce8b490ef6682c2e6c3c4d16478e6b9e9687ee1c1133637 -size 20879 +oid sha256:84581aac943c5065f1e5438465ee7d1845555e68aa005d8b596542ce3830dc83 +size 21258 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_4,NEXUS_5,1.0,en].png index 960bd96e80..c9c6837a0e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1fa3b5aae9cee5e2fec8929697b0606b655c45b10a65bcd641da315e98c48e1e -size 20951 +oid sha256:46ad7d3a46b54543f226e82fa4199e2e2de7e2e91748d663bed88dfb7afc5b61 +size 21350 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_0,NEXUS_5,1.0,en].png index 643983770c..49bfbea7e2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce74fa2b0364763152e69dcfa4f8d598504b630fa1227f2fa685bc886ccd5afa -size 19434 +oid sha256:6fb273e816484cb326ea4ba00948e749362542c1b964e23c5dc48b306b909136 +size 19843 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png index 173360baef..245fda2df2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12a78fbc2d2e84e93d40d1e075d8b46c5366f3d323dd85f27be286a64231f884 -size 32120 +oid sha256:fd9bcc236e4500d82887c53704b60f47a1178945ae00ef524e312c2c4738b9bf +size 32516 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png index 0fa0b9c5d2..34a0c15d4d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a1bd4187a3713153d6e9ed94594385c3806f59c4d36cc6f2867a38d630551505 -size 31302 +oid sha256:d74f90c7fd3ab16458214cead1326a985bb1317745e8f29bd06d1de13acf067c +size 31692 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_3,NEXUS_5,1.0,en].png index 643983770c..49bfbea7e2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce74fa2b0364763152e69dcfa4f8d598504b630fa1227f2fa685bc886ccd5afa -size 19434 +oid sha256:6fb273e816484cb326ea4ba00948e749362542c1b964e23c5dc48b306b909136 +size 19843 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_4,NEXUS_5,1.0,en].png index ce8b5bf468..652c6b6492 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9390a3b8111ab0d31b3bbf3b6bf8794432d135130b37404ce4a646a74369d85b -size 19502 +oid sha256:91378a7a126d1d9f9f234368930635ce85db3666721dc32b0646effa59d0fdee +size 19991 From e7136715a6e6669fc95c5c2b3fc518fa220b5826 Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Thu, 20 Jul 2023 16:50:24 +0200 Subject: [PATCH 038/251] Update permission request dialog copy. (#931) As per new designs. Part of: - https://github.com/vector-im/element-meta/issues/1682 --- libraries/ui-strings/src/main/res/values/localazy.xml | 4 ++-- ...p_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...p_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...p_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...p_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 10952f4194..b17d8f337f 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -143,8 +143,8 @@ "%1$s could not load the map. Please try again later." "Failed loading messages" "%1$s could not access your location. Please try again later." - "To send a location, allow %1$s to access your location from its settings screen." - "To send a location, allow %1$s to access your location in the next dialog." + "%1$s does not have permission to access your location. You can enable access in Settings." + "%1$s does not have permission to access your location. Enable access below." "Some messages have not been sent" "Sorry, an error occurred" "🔐️ Join me on %1$s" diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png index 57b3030576..84cc8df088 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2edcc1efa55df97a1dcb1f90e6b19a3ec042647086de5a932ffd6c2d30009e64 -size 34873 +oid sha256:bb559f5cd8b391ab4406c903a59376061255142317b035bd084f153779036eb9 +size 36589 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png index 3824f88e7b..a489609443 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae8ee38742d0c0b418186b0a24fdc85d62ab9152f21f82b5d364ae3274bc1d99 -size 33951 +oid sha256:ae73b980357c9def721335ecd69ffb8d921963d422e38c84735f53b6f4a596f2 +size 35101 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png index 245fda2df2..b3f1ae991d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd9bcc236e4500d82887c53704b60f47a1178945ae00ef524e312c2c4738b9bf -size 32516 +oid sha256:fac71b82a06f65e799ca345cc9b2e22af6c0000b9900bdcdc584ca6b0f6c2c4d +size 34178 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png index 34a0c15d4d..521af2091c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d74f90c7fd3ab16458214cead1326a985bb1317745e8f29bd06d1de13acf067c -size 31692 +oid sha256:e66b4ae355ae37f7810db1923c4018a44b1056c6471a3ed675032f036d30ff34 +size 32685 From d84a9d5d2466b79f1a3d80e3956895f481d290cd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 16:12:33 +0200 Subject: [PATCH 039/251] Create sheetStateForPreview to avoid code duplication. --- .../designsystem/preview/SheetState.kt | 27 +++++++++++++++++++ .../theme/components/ModalBottomSheet.kt | 7 ++--- 2 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/SheetState.kt diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/SheetState.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/SheetState.kt new file mode 100644 index 0000000000..a83bc5708c --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/SheetState.kt @@ -0,0 +1,27 @@ +/* + * 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.libraries.designsystem.preview + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.SheetState +import androidx.compose.material3.SheetValue + +@OptIn(ExperimentalMaterial3Api::class) +val sheetStateForPreview = SheetState( + skipPartiallyExpanded = true, + initialValue = SheetValue.Expanded, +) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt index a31e4188c6..57b0612ad7 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheet.kt @@ -25,7 +25,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.BottomSheetDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.SheetState -import androidx.compose.material3.SheetValue import androidx.compose.material3.contentColorFor import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable @@ -38,6 +37,7 @@ import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.designsystem.preview.sheetStateForPreview import io.element.android.libraries.theme.ElementTheme import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -100,10 +100,7 @@ private fun ContentToPreview() { ) { ModalBottomSheet( onDismissRequest = {}, - sheetState = SheetState( - skipPartiallyExpanded = true, - initialValue = SheetValue.Expanded, - ), + sheetState = sheetStateForPreview, ) { Text( text = "Sheet Content", From 7524758d059c85d2753706ad60d4bc9870b6a737 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 16:13:50 +0200 Subject: [PATCH 040/251] Fix warning (there is a TODO) --- .../features/messages/impl/forward/ForwardMessagesView.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt index 230965312e..467a963088 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt @@ -82,6 +82,7 @@ fun ForwardMessagesView( return } + @Suppress("UNUSED_PARAMETER") fun onRoomRemoved(roomSummaryDetails: RoomSummaryDetails) { // TODO toggle selection when multi-selection is enabled state.eventSink(ForwardMessagesEvents.RemoveSelectedRoom) From 80f7b63acc2389457e08e5e7cdd3bcdb47e4e5e3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 16:14:45 +0200 Subject: [PATCH 041/251] Fix warning (we will handle error case later) --- .../event/TimelineItemContentFailedToParseStateFactory.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFailedToParseStateFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFailedToParseStateFactory.kt index 7e2bf90050..f7d3c10483 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFailedToParseStateFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFailedToParseStateFactory.kt @@ -23,6 +23,7 @@ import javax.inject.Inject class TimelineItemContentFailedToParseStateFactory @Inject constructor() { + @Suppress("UNUSED_PARAMETER") fun create(failedToParseState: FailedToParseStateContent): TimelineItemEventContent { return TimelineItemUnknownContent } From 058f35c06a47526334fe161db7bab18a69981bd3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 16:15:26 +0200 Subject: [PATCH 042/251] Fix warning (keep the same format for all methods) --- .../impl/timeline/components/event/TimelineItemUnknownView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemUnknownView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemUnknownView.kt index 852428cb92..f053c5a6a0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemUnknownView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemUnknownView.kt @@ -29,7 +29,7 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun TimelineItemUnknownView( - content: TimelineItemUnknownContent, + @Suppress("UNUSED_PARAMETER") content: TimelineItemUnknownContent, extraPadding: ExtraPadding, modifier: Modifier = Modifier ) { From f77349332e6caefdb5decbe4bac264fc1952b209 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 16:15:44 +0200 Subject: [PATCH 043/251] Fix warning (there is a TODO) --- .../android/features/messages/impl/timeline/TimelineView.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index d7820e707b..d7c259eba2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -94,6 +94,7 @@ fun TimelineView( val lazyListState = rememberLazyListState() + @Suppress("UNUSED_PARAMETER") fun inReplyToClicked(eventId: EventId) { // TODO implement this logic once we have support to 'jump to event X' in sliding sync } From fb587d279caeef941f0598c6811133f41817c1b8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 16:26:28 +0200 Subject: [PATCH 044/251] Stop using deprecated LocalBroadcastManager. We will implement the code when we will work on the troubleshoot notification screen. --- libraries/push/impl/build.gradle.kts | 3 --- .../push/impl/notifications/TestNotificationReceiver.kt | 4 +--- .../libraries/push/impl/push/DefaultPushHandler.kt | 8 +++----- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/libraries/push/impl/build.gradle.kts b/libraries/push/impl/build.gradle.kts index b639f61b8b..b0f3be6deb 100644 --- a/libraries/push/impl/build.gradle.kts +++ b/libraries/push/impl/build.gradle.kts @@ -52,9 +52,6 @@ dependencies { implementation(projects.services.appnavstate.api) implementation(projects.services.toolbox.api) - // TODO Temporary use the deprecated LocalBroadcastManager, to be changed later. - implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.1.0") - testImplementation(libs.test.junit) testImplementation(libs.test.mockk) testImplementation(libs.test.truth) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/TestNotificationReceiver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/TestNotificationReceiver.kt index 42c0fe61af..a7135cee74 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/TestNotificationReceiver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/TestNotificationReceiver.kt @@ -19,12 +19,10 @@ package io.element.android.libraries.push.impl.notifications import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import androidx.localbroadcastmanager.content.LocalBroadcastManager class TestNotificationReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - // Internal broadcast to any one interested - LocalBroadcastManager.getInstance(context).sendBroadcast(intent) + // TODO The test notification has been clicked, notify the ui } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt index 3ad848aeb4..432d249d34 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt @@ -17,10 +17,8 @@ package io.element.android.libraries.push.impl.push import android.content.Context -import android.content.Intent import android.os.Handler import android.os.Looper -import androidx.localbroadcastmanager.content.LocalBroadcastManager import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.meta.BuildMeta @@ -29,9 +27,9 @@ import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.push.impl.PushersManager import io.element.android.libraries.push.impl.log.pushLoggerTag +import io.element.android.libraries.push.impl.notifications.DefaultNotificationDrawerManager import io.element.android.libraries.push.impl.notifications.NotifiableEventResolver import io.element.android.libraries.push.impl.notifications.NotificationActionIds -import io.element.android.libraries.push.impl.notifications.DefaultNotificationDrawerManager import io.element.android.libraries.push.impl.store.DefaultPushDataStore import io.element.android.libraries.pushproviders.api.PushData import io.element.android.libraries.pushproviders.api.PushHandler @@ -82,8 +80,8 @@ class DefaultPushHandler @Inject constructor( // Diagnostic Push if (pushData.eventId == PushersManager.TEST_EVENT_ID) { - val intent = Intent(actionIds.push) - LocalBroadcastManager.getInstance(context).sendBroadcast(intent) + // val intent = Intent(actionIds.push) + // TODO The test push has been received, notify the ui return } From 2449cbbaa31bcc3356655aa5794d2850ae077540 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 17:41:18 +0200 Subject: [PATCH 045/251] Fix warning (bottom sheet still not previewable correctly in screenshot test) --- .../timeline/components/retrysendmenu/RetrySendMessageMenu.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt index d344a5e995..9b0974447e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt @@ -165,6 +165,7 @@ internal fun RetrySendMessageMenuPreviewDark(@PreviewParameter(RetrySendMenuStat } } +@Suppress("UNUSED_PARAMETER") @OptIn(ExperimentalMaterial3Api::class) @Composable private fun ContentToPreview(state: RetrySendMenuState) { From bb3511e5f337db81e4d78fe94da61987640eb080 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 17:44:14 +0200 Subject: [PATCH 046/251] Enable detekt rule `UnusedPrivateMember` and cleanup the code. --- .../EnsureFcmTokenIsRetrievedUseCase.kt | 45 ------------------- .../posthog/PosthogAnalyticsProvider.kt | 2 + tools/detekt/detekt.yml | 3 +- 3 files changed, 3 insertions(+), 47 deletions(-) delete mode 100644 libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/EnsureFcmTokenIsRetrievedUseCase.kt diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/EnsureFcmTokenIsRetrievedUseCase.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/EnsureFcmTokenIsRetrievedUseCase.kt deleted file mode 100644 index d557aa0334..0000000000 --- a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/EnsureFcmTokenIsRetrievedUseCase.kt +++ /dev/null @@ -1,45 +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.libraries.pushproviders.firebase - -import javax.inject.Inject - -// TODO -class EnsureFcmTokenIsRetrievedUseCase @Inject constructor( -// private val unifiedPushHelper: UnifiedPushHelper, -// private val fcmHelper: FcmHelper, - // private val activeSessionHolder: ActiveSessionHolder, -) { - -// fun execute(pushersManager: PushersManager, registerPusher: Boolean) { -// if (unifiedPushHelper.isEmbeddedDistributor()) { -// fcmHelper.ensureFcmTokenIsRetrieved(pushersManager, shouldAddHttpPusher(registerPusher)) -// } -// } - - private fun shouldAddHttpPusher(registerPusher: Boolean) = if (registerPusher) { - /* - TODO EAx - val currentSession = activeSessionHolder.getActiveSession() - val currentPushers = currentSession.pushersService().getPushers() - currentPushers.none { it.deviceId == currentSession.sessionParams.deviceId } - */ - true - } else { - false - } -} diff --git a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt index 92e73195c0..1749633f63 100644 --- a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt +++ b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt @@ -101,9 +101,11 @@ class PosthogAnalyticsProvider @Inject constructor( * We avoid sending nulls as part of the UserProperties as this will reset the values across all devices. * The UserProperties event has nullable properties to allow for clients to opt in. */ + /* private fun Map.toPostHogUserProperties(): Properties { return Properties().apply { putAll(this@toPostHogUserProperties.filter { it.value != null }) } } + */ } diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index a3bad54ab3..7ed94fedb5 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -13,8 +13,7 @@ style: FunctionOnlyReturningConstant: active: false UnusedPrivateMember: - # TODO Enable it - active: false + active: true UnusedParameter: # TODO Enable it active: false From bd2826da3a97166473c9c9ab81d87696d8151079 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 17:48:56 +0200 Subject: [PATCH 047/251] Enable detekt rules `UnusedParameter` and `UnusedPrivateProperty` and cleanup the code. --- .../features/rageshake/impl/logs/VectorFileLogger.kt | 2 +- .../features/rageshake/impl/reporter/DefaultBugReporter.kt | 2 +- .../libraries/matrix/api/permalink/PermalinkBuilder.kt | 1 - .../impl/notifications/DefaultNotificationDrawerManager.kt | 6 ++---- .../push/impl/notifications/NotifiableEventResolver.kt | 6 +----- .../android/libraries/push/impl/push/DefaultPushHandler.kt | 6 +----- .../unifiedpush/UnregisterUnifiedPushUseCase.kt | 2 +- .../analyticsproviders/posthog/PosthogAnalyticsProvider.kt | 4 ++-- tools/detekt/detekt.yml | 6 ++---- 9 files changed, 11 insertions(+), 24 deletions(-) diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLogger.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLogger.kt index eea6c1dbbf..6b5d7c9096 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLogger.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLogger.kt @@ -48,7 +48,7 @@ class VectorFileLogger( } private const val SIZE_20MB = 20 * 1024 * 1024 - private const val SIZE_50MB = 50 * 1024 * 1024 + // private const val SIZE_50MB = 50 * 1024 * 1024 } /* diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt index 5695596650..518be1a045 100755 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt @@ -87,7 +87,7 @@ class DefaultBugReporter @Inject constructor( // filenames private const val LOG_CAT_ERROR_FILENAME = "logcatError.log" private const val LOG_CAT_FILENAME = "logcat.log" - private const val KEY_REQUESTS_FILENAME = "keyRequests.log" + // private const val KEY_REQUESTS_FILENAME = "keyRequests.log" private const val BUFFER_SIZE = 1024 * 1024 * 50 } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkBuilder.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkBuilder.kt index 6a15bfb514..31e28a40db 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkBuilder.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkBuilder.kt @@ -25,7 +25,6 @@ object PermalinkBuilder { private const val ROOM_PATH = "room/" private const val USER_PATH = "user/" - private const val GROUP_PATH = "group/" private val permalinkBaseUrl get() = (MatrixConfiguration.clientPermalinkBaseUrl ?: MatrixConfiguration.matrixToPermalinkBaseUrl).also { var baseUrl = it diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt index 9cd4956dca..3c3d07dc74 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt @@ -23,17 +23,16 @@ import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.SingleIn +import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.user.MatrixUser -import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.push.api.notifications.NotificationDrawerManager -import io.element.android.libraries.push.api.store.PushDataStore import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent -import io.element.android.services.appnavstate.api.NavigationState import io.element.android.services.appnavstate.api.AppNavigationStateService +import io.element.android.services.appnavstate.api.NavigationState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -48,7 +47,6 @@ import javax.inject.Inject */ @SingleIn(AppScope::class) class DefaultNotificationDrawerManager @Inject constructor( - private val pushDataStore: PushDataStore, private val notifiableEventProcessor: NotifiableEventProcessor, private val notificationRenderer: NotificationRenderer, private val notificationEventPersistence: NotificationEventPersistence, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt index dbdddf1ac9..29ac866347 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt @@ -17,7 +17,7 @@ package io.element.android.libraries.push.impl.notifications import io.element.android.libraries.core.log.logger.LoggerTag -import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId @@ -34,7 +34,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessage import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType -import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.push.impl.R import io.element.android.libraries.push.impl.log.pushLoggerTag import io.element.android.libraries.push.impl.notifications.model.FallbackNotifiableEvent @@ -57,9 +56,6 @@ private val loggerTag = LoggerTag("NotifiableEventResolver", pushLoggerTag) */ class NotifiableEventResolver @Inject constructor( private val stringProvider: StringProvider, - // private val noticeEventFormatter: NoticeEventFormatter, - // private val displayableEventFormatter: DisplayableEventFormatter, - private val buildMeta: BuildMeta, private val clock: SystemClock, private val matrixClientProvider: MatrixClientProvider, ) { diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt index 432d249d34..aa1d0032e0 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt @@ -16,20 +16,17 @@ package io.element.android.libraries.push.impl.push -import android.content.Context import android.os.Handler import android.os.Looper import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.di.AppScope -import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.push.impl.PushersManager import io.element.android.libraries.push.impl.log.pushLoggerTag import io.element.android.libraries.push.impl.notifications.DefaultNotificationDrawerManager import io.element.android.libraries.push.impl.notifications.NotifiableEventResolver -import io.element.android.libraries.push.impl.notifications.NotificationActionIds import io.element.android.libraries.push.impl.store.DefaultPushDataStore import io.element.android.libraries.pushproviders.api.PushData import io.element.android.libraries.pushproviders.api.PushHandler @@ -51,8 +48,7 @@ class DefaultPushHandler @Inject constructor( private val defaultPushDataStore: DefaultPushDataStore, private val userPushStoreFactory: UserPushStoreFactory, private val pushClientSecret: PushClientSecret, - private val actionIds: NotificationActionIds, - @ApplicationContext private val context: Context, + // private val actionIds: NotificationActionIds, private val buildMeta: BuildMeta, private val matrixAuthenticationService: MatrixAuthenticationService, ) : PushHandler { diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt index 4efaacbf3a..4bf8217914 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnregisterUnifiedPushUseCase.kt @@ -26,7 +26,7 @@ class UnregisterUnifiedPushUseCase @Inject constructor( @ApplicationContext private val context: Context, //private val pushDataStore: PushDataStore, private val unifiedPushStore: UnifiedPushStore, - private val unifiedPushGatewayResolver: UnifiedPushGatewayResolver, + // private val unifiedPushGatewayResolver: UnifiedPushGatewayResolver, ) { suspend fun execute(clientSecret: String /*pushersManager: PushersManager?*/) { diff --git a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt index 1749633f63..2db593cc78 100644 --- a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt +++ b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt @@ -29,8 +29,8 @@ import io.element.android.services.analyticsproviders.posthog.log.analyticsTag import timber.log.Timber import javax.inject.Inject -private val REUSE_EXISTING_ID: String? = null -private val IGNORED_OPTIONS: Options? = null +// private val REUSE_EXISTING_ID: String? = null +// private val IGNORED_OPTIONS: Options? = null @ContributesMultibinding(AppScope::class) class PosthogAnalyticsProvider @Inject constructor( diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 7ed94fedb5..320c6a03cf 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -15,11 +15,9 @@ style: UnusedPrivateMember: active: true UnusedParameter: - # TODO Enable it - active: false + active: true UnusedPrivateProperty: - # TODO Enable it - active: false + active: true ThrowsCount: active: false LoopWithTooManyJumpStatements: From 2bd1c7593743d0f46c870c6b68257a50afef570d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 17:50:23 +0200 Subject: [PATCH 048/251] Enable detekt rules `VariableNaming` and cleanup the code. --- .../features/rageshake/impl/reporter/DefaultBugReporter.kt | 6 +++--- tools/detekt/detekt.yml | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt index 518be1a045..65dc48aca8 100755 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt @@ -103,7 +103,7 @@ class DefaultBugReporter @Inject constructor( .adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)) */ - private val LOGCAT_CMD_ERROR = arrayOf( + private val logcatCommandError = arrayOf( "logcat", // /< Run 'logcat' command "-d", // /< Dump the log rather than continue outputting it "-v", // formatting @@ -114,7 +114,7 @@ class DefaultBugReporter @Inject constructor( "*:S" // /< Everything else silent, so don't pick it.. ) - private val LOGCAT_CMD_DEBUG = arrayOf("logcat", "-d", "-v", "threadtime", "*:*") + private val logcatCommandDebug = arrayOf("logcat", "-d", "-v", "threadtime", "*:*") /** * Send a bug report. @@ -500,7 +500,7 @@ class DefaultBugReporter @Inject constructor( val logcatProc: Process try { - logcatProc = Runtime.getRuntime().exec(if (isErrorLogCat) LOGCAT_CMD_ERROR else LOGCAT_CMD_DEBUG) + logcatProc = Runtime.getRuntime().exec(if (isErrorLogCat) logcatCommandError else logcatCommandDebug) } catch (e1: IOException) { return } diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 320c6a03cf..ea02b86b9f 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -69,8 +69,7 @@ complexity: naming: VariableNaming: - # TODO Enable it - active: false + active: true TopLevelPropertyNaming: # TODO Enable it active: false From 8f715cbc9260bb897b3a5eb8e0fe45494fb8c98a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 17:52:48 +0200 Subject: [PATCH 049/251] Enable detekt rules `TopLevelPropertyNaming` and cleanup the code. --- .../messages/impl/timeline/TimelinePresenter.kt | 6 +++--- .../impl/timeline/components/html/HtmlDocument.kt | 8 ++++---- .../features/preferences/impl/about/ElementLegal.kt | 12 ++++++------ .../features/roomlist/impl/RoomListPresenter.kt | 4 ++-- tools/detekt/detekt.yml | 3 +-- 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index cb8b536be1..082f18e449 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -42,8 +42,8 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import javax.inject.Inject -private const val backPaginationEventLimit = 20 -private const val backPaginationPageSize = 50 +private const val BACK_PAGINATION_EVENT_LIMIT = 20 +private const val BACK_PAGINATION_PAGE_SIZE = 50 class TimelinePresenter @Inject constructor( private val timelineItemsFactory: TimelineItemsFactory, @@ -164,6 +164,6 @@ class TimelinePresenter @Inject constructor( } private fun CoroutineScope.paginateBackwards() = launch { - timeline.paginateBackwards(backPaginationEventLimit, backPaginationPageSize) + timeline.paginateBackwards(BACK_PAGINATION_EVENT_LIMIT, BACK_PAGINATION_PAGE_SIZE) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt index cb0264cf5d..76df891a93 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt @@ -61,7 +61,7 @@ import org.jsoup.nodes.Element import org.jsoup.nodes.Node import org.jsoup.nodes.TextNode -private const val chipId = "chip" +private const val CHIP_ID = "chip" @Composable fun HtmlDocument( @@ -544,13 +544,13 @@ private fun AnnotatedString.Builder.appendLink(link: Element) { pop() } is PermalinkData.RoomEmailInviteLink -> { - appendInlineContent(chipId, link.ownText()) + appendInlineContent(CHIP_ID, link.ownText()) } is PermalinkData.RoomLink -> { - appendInlineContent(chipId, link.ownText()) + appendInlineContent(CHIP_ID, link.ownText()) } is PermalinkData.UserLink -> { - appendInlineContent(chipId, link.ownText()) + appendInlineContent(CHIP_ID, link.ownText()) } } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/ElementLegal.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/ElementLegal.kt index 81af611716..e09e0df8f8 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/ElementLegal.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/ElementLegal.kt @@ -19,17 +19,17 @@ package io.element.android.features.preferences.impl.about import androidx.annotation.StringRes import io.element.android.libraries.ui.strings.CommonStrings -private const val CopyrightUrl = "https://element.io/copyright" -private const val UsePolicyUrl = "https://element.io/acceptable-use-policy-terms" -private const val PrivacyUrl = "https://element.io/privacy" +private const val COPYRIGHT_URL = "https://element.io/copyright" +private const val USE_POLICY_URL = "https://element.io/acceptable-use-policy-terms" +private const val PRIVACY_URL = "https://element.io/privacy" sealed class ElementLegal( @StringRes val titleRes: Int, val url: String, ) { - object Copyright : ElementLegal(CommonStrings.common_copyright, CopyrightUrl) - object AcceptableUsePolicy : ElementLegal(CommonStrings.common_acceptable_use_policy, UsePolicyUrl) - object PrivacyPolicy : ElementLegal(CommonStrings.common_privacy_policy, PrivacyUrl) + object Copyright : ElementLegal(CommonStrings.common_copyright, COPYRIGHT_URL) + object AcceptableUsePolicy : ElementLegal(CommonStrings.common_acceptable_use_policy, USE_POLICY_URL) + object PrivacyPolicy : ElementLegal(CommonStrings.common_privacy_policy, PRIVACY_URL) } fun getAllLegals(): List { diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt index 21f946b3fd..5a82b20723 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt @@ -44,7 +44,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import javax.inject.Inject -private const val extendedRangeSize = 40 +private const val EXTENDED_RANGE_SIZE = 40 class RoomListPresenter @Inject constructor( private val client: MatrixClient, @@ -130,7 +130,7 @@ class RoomListPresenter @Inject constructor( private fun updateVisibleRange(range: IntRange) { if (range.isEmpty()) return - val midExtendedRangeSize = extendedRangeSize / 2 + val midExtendedRangeSize = EXTENDED_RANGE_SIZE / 2 val extendedRangeStart = (range.first - midExtendedRangeSize).coerceAtLeast(0) // Safe to give bigger size than room list val extendedRangeEnd = range.last + midExtendedRangeSize diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index ea02b86b9f..e6fd004484 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -71,8 +71,7 @@ naming: VariableNaming: active: true TopLevelPropertyNaming: - # TODO Enable it - active: false + active: true FunctionNaming: active: true ignoreAnnotated: ['Composable'] From 2c4308ddbaf215608040e33b78f585353cb4373d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 17:53:29 +0200 Subject: [PATCH 050/251] Remove detekt rules about ViewModels. --- tools/detekt/detekt.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index e6fd004484..322b1be50a 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -145,8 +145,3 @@ Compose: active: true UnstableCollections: active: true - ViewModelForwarding: - ## TODO Set to true later - active: false - ViewModelInjection: - active: true From 55538c2773970511e223f9c8373337120415716b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 18:00:23 +0200 Subject: [PATCH 051/251] Enable detekt rules `UnusedImports` and cleanup the code. --- .../android/appnav/intent/IntentResolver.kt | 2 - .../appnav/room/LoadingRoomNodeView.kt | 1 - .../impl/components/RoomPrivacyOption.kt | 1 - .../impl/root/CreateRoomRootPresenterTests.kt | 2 - .../ftue/impl/DefaultFtueStateTests.kt | 1 - .../location/impl/show/ShowLocationView.kt | 1 - .../accountprovider/AccountProviderView.kt | 1 - .../features/messages/impl/MessagesView.kt | 2 - .../impl/actionlist/ActionListPresenter.kt | 1 - .../impl/media/viewer/MediaViewerView.kt | 1 - .../impl/timeline/components/EmojiPicker.kt | 6 +-- .../timeline/components/MessageEventBubble.kt | 1 - .../components/MessageStateEventContainer.kt | 1 - .../components/event/TimelineItemVideoView.kt | 1 - .../components/group/GroupHeaderView.kt | 1 - .../retrysendmenu/RetrySendMessageMenu.kt | 1 - .../TimelineEncryptedHistoryBannerView.kt | 1 - .../virtual/TimelineItemDaySeparatorView.kt | 1 - .../messages/MessagesPresenterTest.kt | 40 ------------------- .../actionlist/ActionListPresenterTest.kt | 1 - .../features/messages/fixtures/media.kt | 1 - .../api/ui/ConnectivityIndicatorView.kt | 1 - .../api/detection/RageshakeDetectionState.kt | 1 - .../roomdetails/impl/RoomDetailsView.kt | 1 - .../impl/members/RoomMemberListPresenter.kt | 3 -- .../roomlist/impl/RoomListContextMenu.kt | 1 - .../libraries/matrix/impl/auth/OidcConfig.kt | 2 +- .../libraries/matrix/impl/media/ImageInfo.kt | 1 - .../TimelineEncryptedHistoryPostProcessor.kt | 2 - .../android/libraries/matrix/test/TestData.kt | 1 - .../libraries/mediaupload/ImageCompressor.kt | 1 - .../posthog/PosthogAnalyticsProvider.kt | 1 - .../tests/uitests/ShowkaseNavigation.kt | 1 - tools/detekt/detekt.yml | 2 + 34 files changed, 6 insertions(+), 80 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt b/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt index 6a3d8ff9dd..b567395c1e 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt @@ -21,8 +21,6 @@ import io.element.android.features.login.api.oidc.OidcAction import io.element.android.features.login.api.oidc.OidcIntentResolver import io.element.android.libraries.deeplink.DeeplinkData import io.element.android.libraries.deeplink.DeeplinkParser -import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.core.SessionId import timber.log.Timber import javax.inject.Inject diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt index e8d68a3e94..ae5ae4f8db 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt @@ -37,7 +37,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom import io.element.android.libraries.designsystem.components.avatar.AvatarSize diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt index ee664673f8..1d900cea8c 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt @@ -30,7 +30,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import io.element.android.features.createroom.impl.configureroom.RoomPrivacyItem import io.element.android.features.createroom.impl.configureroom.roomPrivacyItems import io.element.android.libraries.designsystem.preview.ElementPreviewDark diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt index 2f439b5107..002e8c77fd 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootPresenterTests.kt @@ -27,8 +27,6 @@ import io.element.android.features.createroom.impl.userlist.FakeUserListPresente import io.element.android.features.createroom.impl.userlist.UserListDataStore import io.element.android.features.createroom.impl.userlist.aUserListState import io.element.android.libraries.architecture.Async -import io.element.android.libraries.core.meta.BuildMeta -import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueStateTests.kt b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueStateTests.kt index ce1683e8e5..cfd489ea2a 100644 --- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueStateTests.kt +++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueStateTests.kt @@ -25,7 +25,6 @@ import io.element.android.services.analytics.api.AnalyticsService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel -import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt index 7726bbf9cc..0e114a43b9 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt @@ -40,7 +40,6 @@ import com.mapbox.mapboxsdk.camera.CameraPosition import com.mapbox.mapboxsdk.geometry.LatLng import io.element.android.features.location.api.internal.rememberTileStyleUrl import io.element.android.features.location.impl.MapDefaults -import io.element.android.features.location.impl.send.SendLocationState import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt index 3362beac40..378859225e 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt @@ -30,7 +30,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 6d8f2792e0..b68b0eea7a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -43,13 +43,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontStyle -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import io.element.android.features.messages.impl.actionlist.ActionListEvents import io.element.android.features.messages.impl.actionlist.ActionListView import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt index 56e9f48dde..2d18018746 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt @@ -30,7 +30,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.canBeCopied import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.meta.BuildMeta -import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt index 5964c2ced7..5fb14a6ca0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt @@ -57,7 +57,6 @@ import io.element.android.features.messages.impl.media.local.LocalMediaView import io.element.android.features.messages.impl.media.local.MediaInfo import io.element.android.features.messages.impl.media.local.rememberLocalMediaViewState import io.element.android.libraries.architecture.Async -import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.RetryDialog import io.element.android.libraries.designsystem.preview.ElementPreviewDark diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/EmojiPicker.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/EmojiPicker.kt index 182965a4e5..6e121685f2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/EmojiPicker.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/EmojiPicker.kt @@ -41,7 +41,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import com.vanniktech.emoji.Emoji import com.vanniktech.emoji.google.GoogleEmojiProvider import io.element.android.libraries.designsystem.preview.ElementPreviewDark @@ -62,7 +61,7 @@ fun EmojiPicker( val emojiProvider = remember { GoogleEmojiProvider() } val categories = remember { emojiProvider.categories } val pagerState = rememberPagerState() - Column (modifier) { + Column(modifier) { TabRow( selectedTabIndex = pagerState.currentPage, ) { @@ -109,7 +108,8 @@ fun EmojiPicker( Text( text = item.unicode, style = ElementTheme.typography.fontHeadingSmRegular, - ) } + ) + } } } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt index 932dce913c..446846db83 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt @@ -35,7 +35,6 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp 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 diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt index 4bb66f0eac..69c73a68e1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt @@ -33,7 +33,6 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.designsystem.theme.components.Surface private val CORNER_RADIUS = 8.dp diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt index aeb2e7145e..8f8d767835 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt @@ -18,7 +18,6 @@ package io.element.android.features.messages.impl.timeline.components.event import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.runtime.Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt index b2d5dbc7f6..a631e34266 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt @@ -35,7 +35,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.theme.components.Icon diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt index 9b0974447e..cae55de241 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/retrysendmenu/RetrySendMessageMenu.kt @@ -31,7 +31,6 @@ 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.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt index 055e4bb876..65af6bdbf5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineEncryptedHistoryBannerView.kt @@ -30,7 +30,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.element.android.features.messages.impl.R import io.element.android.libraries.designsystem.preview.DayNightPreviews diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt index c76d59806d..23e393ae8f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemDaySeparatorView.kt @@ -24,7 +24,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index 95848d835f..9d081cf3c7 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -20,7 +20,6 @@ import android.net.Uri import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test -import com.google.common.collect.Iterables.skip import com.google.common.truth.Truth.assertThat import io.element.android.features.analytics.test.FakeAnalyticsService import io.element.android.features.messages.fixtures.aMessageEvent @@ -46,8 +45,6 @@ import io.element.android.features.messages.utils.messagesummary.FakeMessageSumm import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.core.meta.BuildMeta -import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.featureflag.test.FakeFeatureFlagService @@ -84,7 +81,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() assertThat(initialState.roomId).isEqualTo(A_ROOM_ID) } @@ -98,11 +94,9 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID)) assertThat(room.myReactions.count()).isEqualTo(1) - // No crashes when sending a reaction failed room.givenToggleReactionResult(Result.failure(IllegalStateException("Failed to send reaction"))) initialState.eventSink.invoke(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID)) @@ -119,7 +113,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID)) assertThat(room.myReactions.count()).isEqualTo(1) @@ -136,7 +129,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Forward, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -152,7 +144,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Copy, event)) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -166,10 +157,8 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent())) - val finalState = awaitItem() assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -182,7 +171,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent(eventId = null))) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -197,7 +185,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() val mediaMessage = aMessageEvent( content = TimelineItemImageContent( @@ -214,7 +201,6 @@ class MessagesPresenterTest { ) ) initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, mediaMessage)) - val finalState = awaitItem() assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java) val replyMode = finalState.composerState.mode as MessageComposerMode.Reply @@ -229,7 +215,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() val mediaMessage = aMessageEvent( content = TimelineItemVideoContent( @@ -247,7 +232,6 @@ class MessagesPresenterTest { ) ) initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, mediaMessage)) - val finalState = awaitItem() assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java) val replyMode = finalState.composerState.mode as MessageComposerMode.Reply @@ -262,7 +246,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() val mediaMessage = aMessageEvent( content = TimelineItemFileContent( @@ -275,7 +258,6 @@ class MessagesPresenterTest { ) ) initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, mediaMessage)) - val finalState = awaitItem() assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java) val replyMode = finalState.composerState.mode as MessageComposerMode.Reply @@ -290,10 +272,8 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Edit, aMessageEvent())) - val finalState = awaitItem() assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Edit::class.java) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -308,7 +288,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Redact, aMessageEvent())) assertThat(matrixRoom.redactEventEventIdParam).isEqualTo(AN_EVENT_ID) @@ -323,7 +302,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.ReportContent, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -337,7 +315,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.Dismiss) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -351,7 +328,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Developer, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -366,17 +342,13 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() - // Initially the composer doesn't have focus, so we don't show the alert assertThat(initialState.showReinvitePrompt).isFalse() - // When the input field is focused we show the alert initialState.composerState.eventSink(MessageComposerEvents.FocusChanged(true)) val focusedState = awaitItem() assertThat(focusedState.showReinvitePrompt).isTrue() - // If it's dismissed then we stop showing the alert initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Cancel)) val dismissedState = awaitItem() @@ -391,10 +363,8 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() assertThat(initialState.showReinvitePrompt).isFalse() - initialState.composerState.eventSink(MessageComposerEvents.FocusChanged(true)) val focusedState = awaitItem() assertThat(focusedState.showReinvitePrompt).isFalse() @@ -408,10 +378,8 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() assertThat(initialState.showReinvitePrompt).isFalse() - initialState.composerState.eventSink(MessageComposerEvents.FocusChanged(true)) val focusedState = awaitItem() assertThat(focusedState.showReinvitePrompt).isFalse() @@ -436,11 +404,9 @@ class MessagesPresenterTest { val initialState = awaitItem() skipItems(1) initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) - skipItems(1) val loadingState = awaitItem() assertThat(loadingState.inviteProgress.isLoading()).isTrue() - val newState = awaitItem() assertThat(newState.inviteProgress.isSuccess()).isTrue() assertThat(room.invitedUserId).isEqualTo(A_SESSION_ID_2) @@ -466,11 +432,9 @@ class MessagesPresenterTest { val initialState = awaitItem() skipItems(1) initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) - skipItems(1) val loadingState = awaitItem() assertThat(loadingState.inviteProgress.isLoading()).isTrue() - val newState = awaitItem() assertThat(newState.inviteProgress.isSuccess()).isTrue() assertThat(room.invitedUserId).isEqualTo(A_SESSION_ID_2) @@ -488,11 +452,9 @@ class MessagesPresenterTest { val initialState = awaitItem() skipItems(1) initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) - skipItems(1) val loadingState = awaitItem() assertThat(loadingState.inviteProgress.isLoading()).isTrue() - val newState = awaitItem() assertThat(newState.inviteProgress.isFailure()).isTrue() } @@ -520,7 +482,6 @@ class MessagesPresenterTest { skipItems(1) val loadingState = awaitItem() assertThat(loadingState.inviteProgress.isLoading()).isTrue() - val newState = awaitItem() assertThat(newState.inviteProgress.isFailure()).isTrue() } @@ -534,7 +495,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - assertThat(awaitItem().userHasPermissionToSendMessage).isTrue() } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt index b814a51c42..a2baa9dff7 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt @@ -30,7 +30,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent -import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.core.aBuildMeta import kotlinx.collections.immutable.persistentListOf diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt index 1357c05913..00701c0a75 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt @@ -21,7 +21,6 @@ import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.media.local.LocalMedia import io.element.android.features.messages.impl.media.local.MediaInfo import io.element.android.features.messages.impl.media.local.anImageInfo -import io.element.android.libraries.core.mimetype.MimeTypes fun aLocalMedia( uri: Uri, diff --git a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt index 7d01d668c9..bf05dbc5f5 100644 --- a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt +++ b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt @@ -42,7 +42,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementPreviewDark diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionState.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionState.kt index 5d6df8bac9..04f38597ed 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionState.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/detection/RageshakeDetectionState.kt @@ -17,7 +17,6 @@ package io.element.android.features.rageshake.api.detection import androidx.compose.runtime.Immutable -import androidx.compose.runtime.Stable import io.element.android.features.rageshake.api.preferences.RageshakePreferencesState @Immutable diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index 9aa8ea41c3..63868c8227 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -28,7 +28,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt index 0787563aed..49ae479d40 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenter.kt @@ -18,7 +18,6 @@ package io.element.android.features.roomdetails.impl.members import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -31,7 +30,6 @@ import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.matrix.api.room.MatrixRoom -import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.RoomMembershipState import io.element.android.libraries.matrix.api.room.powerlevels.canInvite import kotlinx.collections.immutable.toImmutableList @@ -101,6 +99,5 @@ class RoomMemberListPresenter @Inject constructor( }, ) } - } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt index 740c14aa8d..700235f3e2 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt @@ -30,7 +30,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.VectorIcons diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfig.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfig.kt index 1ba5063df9..b5115ffad4 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfig.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfig.kt @@ -16,8 +16,8 @@ package io.element.android.libraries.matrix.impl.auth -import io.element.android.libraries.matrix.api.auth.OidcConfig // TODO Oidc +// import io.element.android.libraries.matrix.api.auth.OidcConfig // import org.matrix.rustcomponents.sdk.OidcClientMetadata /* diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt index b66cec96fd..99b806ed4a 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/ImageInfo.kt @@ -17,7 +17,6 @@ package io.element.android.libraries.matrix.impl.media import io.element.android.libraries.matrix.api.media.ImageInfo -import org.matrix.rustcomponents.sdk.MediaSource import org.matrix.rustcomponents.sdk.ImageInfo as RustImageInfo fun RustImageInfo.map(): ImageInfo = ImageInfo( diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt index ca5c7342f8..0fe12e6391 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt @@ -22,7 +22,6 @@ import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTime import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.getAndUpdate import java.util.Date -import java.util.UUID class TimelineEncryptedHistoryPostProcessor( private val lastLoginTimestamp: Date?, @@ -70,5 +69,4 @@ class TimelineEncryptedHistoryPostProcessor( val timestamp = (item as? MatrixTimelineItem.Event)?.event?.timestamp ?: return false return timestamp <= lastLoginTimestamp!!.time } - } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt index 3da47a8563..677774afe4 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt @@ -24,7 +24,6 @@ import io.element.android.libraries.matrix.api.core.SpaceId import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UserId -import java.util.UUID const val A_USER_NAME = "alice" const val A_PASSWORD = "password" diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt index 2b8669fe42..94e71fef59 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt @@ -19,7 +19,6 @@ package io.element.android.libraries.mediaupload import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory -import com.vanniktech.blurhash.BlurHash import io.element.android.libraries.androidutils.bitmap.calculateInSampleSize import io.element.android.libraries.androidutils.bitmap.resizeToMax import io.element.android.libraries.androidutils.bitmap.rotateToMetadataOrientation diff --git a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt index 2db593cc78..3b2d392896 100644 --- a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt +++ b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt @@ -16,7 +16,6 @@ package io.element.android.services.analyticsproviders.posthog -import com.posthog.android.Options import com.posthog.android.PostHog import com.posthog.android.Properties import com.squareup.anvil.annotations.ContributesMultibinding diff --git a/tests/uitests/src/main/kotlin/io/element/android/tests/uitests/ShowkaseNavigation.kt b/tests/uitests/src/main/kotlin/io/element/android/tests/uitests/ShowkaseNavigation.kt index 8a33430340..cb7795c05a 100644 --- a/tests/uitests/src/main/kotlin/io/element/android/tests/uitests/ShowkaseNavigation.kt +++ b/tests/uitests/src/main/kotlin/io/element/android/tests/uitests/ShowkaseNavigation.kt @@ -18,7 +18,6 @@ package io.element.android.tests.uitests import android.app.Activity import android.content.Intent -import com.airbnb.android.showkase.models.Showkase import com.airbnb.android.showkase.ui.ShowkaseBrowserActivity fun openShowkase(activity: Activity) { diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 322b1be50a..7db4114585 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -16,6 +16,8 @@ style: active: true UnusedParameter: active: true + UnusedImports: + active: true UnusedPrivateProperty: active: true ThrowsCount: From c72f1a09a72f42d45e4f645829b66ce7b1a47c70 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 18:05:37 +0200 Subject: [PATCH 052/251] Enable detekt rules `UnnecessaryParentheses` and cleanup the code. --- .../location/api/internal/ModifierCenterBottomEdge.kt | 2 +- .../impl/screens/loginpassword/LoginPasswordState.kt | 3 ++- .../impl/timeline/components/event/ExtraPadding.kt | 2 +- .../impl/timeline/components/html/HtmlDocument.kt | 2 +- .../attachments/AttachmentsPreviewPresenterTest.kt | 2 +- .../impl/invite/RoomInviteMembersPresenter.kt | 2 +- .../members/RoomMemberListPresenterTests.kt | 8 ++++---- .../roomlist/impl/components/RoomSummaryRow.kt | 10 +++++----- .../libraries/androidutils/system/SystemUtils.kt | 2 +- .../designsystem/atomic/atoms/ElementLogoAtom.kt | 2 +- .../impl/timeline/item/event/EventMessageMapper.kt | 2 +- .../matrix/ui/components/SelectedUsersList.kt | 4 ++-- .../android/libraries/mediaupload/ImageCompressor.kt | 2 +- .../push/impl/notifications/RoomGroupMessageCreator.kt | 2 +- .../notifications/channels/NotificationChannels.kt | 2 +- tools/detekt/detekt.yml | 3 +++ 16 files changed, 27 insertions(+), 23 deletions(-) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/ModifierCenterBottomEdge.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/ModifierCenterBottomEdge.kt index 7ad299adae..736f24e609 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/ModifierCenterBottomEdge.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/ModifierCenterBottomEdge.kt @@ -29,7 +29,7 @@ fun Modifier.centerBottomEdge(scope: BoxScope): Modifier = with(scope) { Modifier.align { size, space, _ -> IntOffset( x = (space.width - size.width) / 2, - y = (space.height / 2) - size.height, + y = space.height / 2 - size.height, ) } ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordState.kt index c8fa2f4ad3..cf4e0d8ee5 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordState.kt @@ -30,7 +30,8 @@ data class LoginPasswordState( ) { val submitEnabled: Boolean get() = loginAction !is Async.Failure && - ((formState.login.isNotEmpty() && formState.password.isNotEmpty())) + formState.login.isNotEmpty() && + formState.password.isNotEmpty() } @Parcelize diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/ExtraPadding.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/ExtraPadding.kt index d941b8a814..65101183d9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/ExtraPadding.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/ExtraPadding.kt @@ -65,7 +65,7 @@ fun TimelineItem.Event.toExtraPadding(): ExtraPadding { fun ExtraPadding.getStr(fontSize: TextUnit): String { if (nbChars == 0) return "" val timestampFontSize = ElementTheme.typography.fontBodyXsRegular.fontSize // 11.sp - val nbOfSpaces = ((timestampFontSize.value / fontSize.value) * nbChars).toInt() + 1 + val nbOfSpaces = (timestampFontSize.value / fontSize.value * nbChars).toInt() + 1 // A space and some unbreakable spaces return " " + "\u00A0".repeat(nbOfSpaces) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt index 76df891a93..82d73cc1a0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt @@ -351,7 +351,7 @@ private fun HtmlMxReply( Surface( modifier = modifier .padding(bottom = 4.dp) - .offset(x = -(8.dp)), + .offset(x = (-8).dp), color = MaterialTheme.colorScheme.background, shape = shape, ) { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt index a96d7104a3..fb8b3b1948 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt @@ -86,7 +86,7 @@ class AttachmentsPreviewPresenterTest { val loadingState = awaitItem() assertThat(loadingState.sendActionState).isEqualTo(SendActionState.Sending.Processing) val failureState = awaitItem() - assertThat(failureState.sendActionState).isEqualTo((SendActionState.Failure(failure))) + assertThat(failureState.sendActionState).isEqualTo(SendActionState.Failure(failure)) assertThat(room.sendMediaCount).isEqualTo(0) failureState.eventSink(AttachmentsPreviewEvents.ClearSendState) val clearedState = awaitItem() diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenter.kt index 00cb04b118..cc0886c3af 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenter.kt @@ -93,7 +93,7 @@ class RoomInviteMembersPresenter @Inject constructor( value = if (value.contains(user)) { value.filterNot { it == user } } else { - (value + user) + value + user }.toImmutableList() } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt index 9d21e668f0..5a3141ea09 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt @@ -72,7 +72,7 @@ class RoomMemberListPresenterTests { loadedState.eventSink(RoomMemberListEvents.OnSearchActiveChanged(true)) val searchActiveState = awaitItem() - Truth.assertThat((searchActiveState.isSearchActive)).isTrue() + Truth.assertThat(searchActiveState.isSearchActive).isTrue() } } @@ -88,7 +88,7 @@ class RoomMemberListPresenterTests { val searchActiveState = awaitItem() loadedState.eventSink(RoomMemberListEvents.UpdateSearchQuery("something")) val searchQueryUpdatedState = awaitItem() - Truth.assertThat((searchQueryUpdatedState.searchQuery)).isEqualTo("something") + Truth.assertThat(searchQueryUpdatedState.searchQuery).isEqualTo("something") val searchSearchResultDelivered = awaitItem() Truth.assertThat(searchSearchResultDelivered.searchResults).isInstanceOf(SearchBarResultState.NoResults::class.java) } @@ -106,9 +106,9 @@ class RoomMemberListPresenterTests { val searchActiveState = awaitItem() loadedState.eventSink(RoomMemberListEvents.UpdateSearchQuery("Alice")) val searchQueryUpdatedState = awaitItem() - Truth.assertThat((searchQueryUpdatedState.searchQuery)).isEqualTo("Alice") + Truth.assertThat(searchQueryUpdatedState.searchQuery).isEqualTo("Alice") val searchSearchResultDelivered = awaitItem() - Truth.assertThat((searchSearchResultDelivered.searchResults)).isInstanceOf(SearchBarResultState.Results::class.java) + Truth.assertThat(searchSearchResultDelivered.searchResults).isInstanceOf(SearchBarResultState.Results::class.java) Truth.assertThat((searchSearchResultDelivered.searchResults as SearchBarResultState.Results).results.joined.first().displayName) .isEqualTo("Alice") diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt index b32f169e6b..aab174c6a4 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt @@ -155,7 +155,7 @@ private fun RowScope.NameAndTimestampRow(room: RoomListRoomSummary) { @Composable private fun RowScope.LastMessageAndIndicatorRow(room: RoomListRoomSummary) { // Last Message - val attributedLastMessage = (room.lastMessage as? AnnotatedString) + val attributedLastMessage = room.lastMessage as? AnnotatedString ?: AnnotatedString(room.lastMessage.orEmpty().toString()) Text( modifier = Modifier @@ -186,10 +186,10 @@ class PercentRectangleSizeShape(private val percent: Float) : Shape { val halfPercent = percent / 2f val path = Path().apply { val rect = Rect( - 0f, - size.height * halfPercent, - size.width, - size.height - (size.height * halfPercent) + left = 0f, + top = size.height * halfPercent, + right = size.width, + bottom = size.height * (1 - halfPercent) ) addRect(rect) close() diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt index 65a5dc9e0d..8347990303 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt @@ -59,7 +59,7 @@ fun Context.isAnimationEnabled(): Boolean { } @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O) -fun supportNotificationChannels() = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) +fun supportNotificationChannels() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O /** * Return the application label of the provided package. If not found, the package is returned. diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/ElementLogoAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/ElementLogoAtom.kt index 94e50d5953..8f96ed6b6e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/ElementLogoAtom.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/ElementLogoAtom.kt @@ -138,7 +138,7 @@ fun Modifier.shapeShadow( val paint = Paint() val frameworkPaint = paint.asFrameworkPaint() if (blurRadius != 0.dp) { - frameworkPaint.maskFilter = (BlurMaskFilter(blurRadius.toPx(), BlurMaskFilter.Blur.NORMAL)) + frameworkPaint.maskFilter = BlurMaskFilter(blurRadius.toPx(), BlurMaskFilter.Blur.NORMAL) } frameworkPaint.color = color.toArgb() diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt index 9f4df1f6b3..96b61fd558 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt @@ -45,7 +45,7 @@ class EventMessageMapper { fun map(message: Message): MessageContent = message.use { val type = it.msgtype().use(this::mapMessageType) val inReplyToId = it.inReplyTo()?.eventId?.let(::EventId) - val inReplyToEvent: InReplyTo? = (it.inReplyTo()?.event)?.use { details -> + val inReplyToEvent: InReplyTo? = it.inReplyTo()?.event?.use { details -> when (details) { is RepliedToEventDetails.Ready -> { val senderProfile = details.senderProfile as? ProfileDetails.Ready diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt index c0ed0460dc..9938fde395 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt @@ -88,7 +88,7 @@ fun SelectedUsersList( val maxVisibleUsers = rowWidth / userWidthWithSpacing // Round down the number of visible users to end with a state where one is half visible - val targetFraction = (userWidth / 2) / userWidthWithSpacing + val targetFraction = userWidth / 2 / userWidthWithSpacing val targetUsers = floor(maxVisibleUsers - targetFraction) + targetFraction // Work out how much extra spacing we need to reduce the number of users that much, then split it evenly amongst the visible users @@ -153,7 +153,7 @@ private fun ContentToPreview() { SelectedUsersList( selectedUsers = aMatrixUserList().take(6).toImmutableList(), modifier = Modifier - .width((200 + (i * 20)).dp) + .width((200 + (i * 20).dp) .border(1.dp, Color.Red) ) } diff --git a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt index 94e71fef59..938072433a 100644 --- a/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt +++ b/libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ImageCompressor.kt @@ -91,7 +91,7 @@ class ImageCompressor @Inject constructor( ) { val (width, height) = when (resizeMode) { is ResizeMode.Approximate -> resizeMode.desiredWidth to resizeMode.desiredHeight - is ResizeMode.Strict -> (resizeMode.maxWidth / 2) to (resizeMode.maxHeight / 2) + is ResizeMode.Strict -> resizeMode.maxWidth / 2 to resizeMode.maxHeight / 2 is ResizeMode.None -> return } // Read bounds only diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt index 5f2f6db263..989ba2ad09 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt @@ -69,7 +69,7 @@ class RoomGroupMessageCreator @Inject constructor( val lastMessageTimestamp = events.last().timestamp val smartReplyErrors = events.filter { it.isSmartReplyError() } - val messageCount = (events.size - smartReplyErrors.size) + val messageCount = events.size - smartReplyErrors.size val meta = RoomNotification.Message.Meta( summaryLine = createRoomMessagesGroupSummaryLine(events, roomName, roomIsDirect = !roomIsGroup), messageCount = messageCount, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt index 0624b863ed..ab115262de 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/channels/NotificationChannels.kt @@ -162,7 +162,7 @@ class NotificationChannels @Inject constructor( private const val CALL_NOTIFICATION_CHANNEL_ID = "CALL_NOTIFICATION_CHANNEL_ID_V2" @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O) - private fun supportNotificationChannels() = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + private fun supportNotificationChannels() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O fun openSystemSettingsForSilentCategory(activity: Activity) { activity.startNotificationChannelSettingsIntent(SILENT_NOTIFICATION_CHANNEL_ID) diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 7db4114585..facace06a1 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -16,6 +16,9 @@ style: active: true UnusedParameter: active: true + UnnecessaryParentheses: + active: true + allowForUnclearPrecedence: false UnusedImports: active: true UnusedPrivateProperty: From 4a3ba10ad51a5d5b5a8b75cf4d5f770c1bb2fcd6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 18:08:37 +0200 Subject: [PATCH 053/251] Enable more detekt rules --- tools/detekt/detekt.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index facace06a1..0123535b48 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -16,9 +16,15 @@ style: active: true UnusedParameter: active: true + UnnecessaryInnerClass: + active: true + UnnecessaryLet: + active: true UnnecessaryParentheses: active: true allowForUnclearPrecedence: false + UntilInsteadOfRangeTo: + active: true UnusedImports: active: true UnusedPrivateProperty: From c56977ed693130d06f20c8f20d64bf39cbd35a4e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 19:15:56 +0200 Subject: [PATCH 054/251] Extract git flow init from release script (so that it can be run standalone) and do not use `-t` which does not seem to be standard. --- tools/gitflow/gitflow-init.sh | 19 +++++++++++++++++++ tools/release/release.sh | 3 +-- 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100755 tools/gitflow/gitflow-init.sh diff --git a/tools/gitflow/gitflow-init.sh b/tools/gitflow/gitflow-init.sh new file mode 100755 index 0000000000..5068a37575 --- /dev/null +++ b/tools/gitflow/gitflow-init.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# +# 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. + +git flow init -d +git config gitflow.prefix.versiontag v diff --git a/tools/release/release.sh b/tools/release/release.sh index 4c8a291639..e51ce259c0 100755 --- a/tools/release/release.sh +++ b/tools/release/release.sh @@ -86,8 +86,7 @@ then printf "Git flow is initialized\n" else printf "Git flow is not initialized. Initializing...\n" - # All default value, just set 'v' for tag prefix - git flow init -d -t 'v' + ./tools/gitflow/gitflow-init.sh fi printf "OK\n" From 1bc80a526c2baacf929a8070388a792797f0f0b1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 19:22:53 +0200 Subject: [PATCH 055/251] Move download_github_artifacts.py to its own folder. --- tools/{release => github}/download_github_artifacts.py | 0 tools/release/release.sh | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename tools/{release => github}/download_github_artifacts.py (100%) diff --git a/tools/release/download_github_artifacts.py b/tools/github/download_github_artifacts.py similarity index 100% rename from tools/release/download_github_artifacts.py rename to tools/github/download_github_artifacts.py diff --git a/tools/release/release.sh b/tools/release/release.sh index e51ce259c0..60955e1f94 100755 --- a/tools/release/release.sh +++ b/tools/release/release.sh @@ -223,7 +223,7 @@ printf "Downloading the artifact...\n" # Download files targetPath="./tmp/Element/${version}" -python3 ./tools/release/download_github_artifacts.py \ +python3 ./tools/github/download_github_artifacts.py \ --token ${gitHubToken} \ --artifactUrl ${artifactUrl} \ --directory ${targetPath} \ From aa26959748da3256174e8957fce7552131603bdb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 19:40:57 +0200 Subject: [PATCH 056/251] Change how we detect if git flow is init. --- tools/release/release.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/release/release.sh b/tools/release/release.sh index 60955e1f94..54ee8117ec 100755 --- a/tools/release/release.sh +++ b/tools/release/release.sh @@ -80,8 +80,8 @@ if [[ ! -d ${buildToolsPath} ]]; then fi # Check if git flow is enabled -git flow config >/dev/null 2>&1 -if [[ $? == 0 ]] +gitFlowDevelop=`git config gitflow.branch.develop` +if [[ ${gitFlowDevelop} != "" ]] then printf "Git flow is initialized\n" else From fce5234c7fae197275b522bc63aacfdc51573d24 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 20:01:35 +0200 Subject: [PATCH 057/251] Fix compilation issue --- .../android/libraries/matrix/ui/components/SelectedUsersList.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt index 9938fde395..f9649e1b34 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedUsersList.kt @@ -153,7 +153,7 @@ private fun ContentToPreview() { SelectedUsersList( selectedUsers = aMatrixUserList().take(6).toImmutableList(), modifier = Modifier - .width((200 + (i * 20).dp) + .width((200 + i * 20).dp) .border(1.dp, Color.Red) ) } From 63a675d1b37decc6d5db11117352b42697fc9660 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 22:01:45 +0200 Subject: [PATCH 058/251] Remove feature flag CollapseRoomStateEvents. It was not used anyway. --- .../element/android/libraries/featureflag/api/FeatureFlags.kt | 4 ---- .../featureflag/impl/BuildtimeFeatureFlagProvider.kt | 1 - 2 files changed, 5 deletions(-) diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt index 920be09389..073bffe1e6 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt @@ -22,10 +22,6 @@ enum class FeatureFlags( override val description: String? = null, override val defaultValue: Boolean = true ) : Feature { - CollapseRoomStateEvents( - key = "feature.collapseroomstateevents", - title = "Collapse room state events", - ), ShowStartChatFlow( key = "feature.showstartchatflow", title = "Show start chat flow", diff --git a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt index ae498e67df..d6fdb1621d 100644 --- a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt +++ b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt @@ -29,7 +29,6 @@ class BuildtimeFeatureFlagProvider @Inject constructor() : override suspend fun isFeatureEnabled(feature: Feature): Boolean { return if (feature is FeatureFlags) { when (feature) { - FeatureFlags.CollapseRoomStateEvents -> false FeatureFlags.ShowStartChatFlow -> false FeatureFlags.ShowMediaUploadingFlow -> false } From 062e7553db9a52ea803f2eb88f56d032e4c24ab6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 22:07:32 +0200 Subject: [PATCH 059/251] Remove feature flag ShowStartChatFlow. It was just used in test. --- .../libraries/featureflag/api/FeatureFlags.kt | 4 ---- .../impl/BuildtimeFeatureFlagProvider.kt | 1 - .../impl/DefaultFeatureFlagServiceTest.kt | 22 +++++++++---------- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt index 073bffe1e6..64b6162053 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt @@ -22,10 +22,6 @@ enum class FeatureFlags( override val description: String? = null, override val defaultValue: Boolean = true ) : Feature { - ShowStartChatFlow( - key = "feature.showstartchatflow", - title = "Show start chat flow", - ), ShowMediaUploadingFlow( key = "feature.showmediauploadingflow", title = "Show media uploading flow", diff --git a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt index d6fdb1621d..fe17433f17 100644 --- a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt +++ b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt @@ -29,7 +29,6 @@ class BuildtimeFeatureFlagProvider @Inject constructor() : override suspend fun isFeatureEnabled(feature: Feature): Boolean { return if (feature is FeatureFlags) { when (feature) { - FeatureFlags.ShowStartChatFlow -> false FeatureFlags.ShowMediaUploadingFlow -> false } } else { diff --git a/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt b/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt index 5f7f01423a..0351b20716 100644 --- a/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt +++ b/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt @@ -26,14 +26,14 @@ class DefaultFeatureFlagServiceTest { @Test fun `given service without provider when feature is checked then it returns the default value`() = runTest { val featureFlagService = DefaultFeatureFlagService(emptySet()) - val isFeatureEnabled = featureFlagService.isFeatureEnabled(FeatureFlags.ShowStartChatFlow) - assertThat(isFeatureEnabled).isEqualTo(FeatureFlags.ShowStartChatFlow.defaultValue) + val isFeatureEnabled = featureFlagService.isFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow) + assertThat(isFeatureEnabled).isEqualTo(FeatureFlags.ShowMediaUploadingFlow.defaultValue) } @Test fun `given service without provider when set enabled feature is called then it returns false`() = runTest { val featureFlagService = DefaultFeatureFlagService(emptySet()) - val result = featureFlagService.setFeatureEnabled(FeatureFlags.ShowStartChatFlow, true) + val result = featureFlagService.setFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow, true) assertThat(result).isEqualTo(false) } @@ -41,7 +41,7 @@ class DefaultFeatureFlagServiceTest { fun `given service with a runtime provider when set enabled feature is called then it returns true`() = runTest { val featureFlagProvider = FakeRuntimeFeatureFlagProvider(0) val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider)) - val result = featureFlagService.setFeatureEnabled(FeatureFlags.ShowStartChatFlow, true) + val result = featureFlagService.setFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow, true) assertThat(result).isEqualTo(true) } @@ -49,10 +49,10 @@ class DefaultFeatureFlagServiceTest { fun `given service with a runtime provider and feature enabled when feature is checked then it returns the correct value`() = runTest { val featureFlagProvider = FakeRuntimeFeatureFlagProvider(0) val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider)) - featureFlagService.setFeatureEnabled(FeatureFlags.ShowStartChatFlow, true) - assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.ShowStartChatFlow)).isEqualTo(true) - featureFlagService.setFeatureEnabled(FeatureFlags.ShowStartChatFlow, false) - assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.ShowStartChatFlow)).isEqualTo(false) + featureFlagService.setFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow, true) + assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow)).isEqualTo(true) + featureFlagService.setFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow, false) + assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow)).isEqualTo(false) } @Test @@ -60,8 +60,8 @@ class DefaultFeatureFlagServiceTest { val lowPriorityfeatureFlagProvider = FakeRuntimeFeatureFlagProvider(LOW_PRIORITY) val highPriorityfeatureFlagProvider = FakeRuntimeFeatureFlagProvider(HIGH_PRIORITY) val featureFlagService = DefaultFeatureFlagService(setOf(lowPriorityfeatureFlagProvider, highPriorityfeatureFlagProvider)) - lowPriorityfeatureFlagProvider.setFeatureEnabled(FeatureFlags.ShowStartChatFlow, false) - highPriorityfeatureFlagProvider.setFeatureEnabled(FeatureFlags.ShowStartChatFlow, true) - assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.ShowStartChatFlow)).isEqualTo(true) + lowPriorityfeatureFlagProvider.setFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow, false) + highPriorityfeatureFlagProvider.setFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow, true) + assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow)).isEqualTo(true) } } From cd3e6c42e21a80c6fba5640b545938c3fd46da37 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 22:20:08 +0200 Subject: [PATCH 060/251] Replace FeatureFlags.ShowMediaUploadingFlow by FeatureFlags.LocationSharing because it has more chance to be disabled. I do not want to remove all our feature flags... --- .../messagecomposer/AttachmentsBottomSheet.kt | 34 +++++++++++-------- .../MessageComposerPresenter.kt | 22 ++++++------ .../messagecomposer/MessageComposerState.kt | 1 + .../MessageComposerStateProvider.kt | 25 +++++++++----- .../MessageComposerPresenterTest.kt | 2 +- .../libraries/featureflag/api/FeatureFlags.kt | 6 ++-- .../impl/BuildtimeFeatureFlagProvider.kt | 2 +- .../impl/DefaultFeatureFlagServiceTest.kt | 22 ++++++------ 8 files changed, 64 insertions(+), 50 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt index c8324ec677..b554ef98f4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt @@ -83,7 +83,7 @@ internal fun AttachmentsBottomSheet( onDismissRequest = { isVisible = false } ) { AttachmentSourcePickerMenu( - eventSink = state.eventSink, + state = state, onSendLocationClicked = onSendLocationClicked, ) } @@ -93,7 +93,7 @@ internal fun AttachmentsBottomSheet( @OptIn(ExperimentalMaterialApi::class) @Composable internal fun AttachmentSourcePickerMenu( - eventSink: (MessageComposerEvents) -> Unit, + state: MessageComposerState, onSendLocationClicked: () -> Unit, modifier: Modifier = Modifier, ) { @@ -102,33 +102,35 @@ internal fun AttachmentSourcePickerMenu( // .navigationBarsPadding() - FIXME after https://issuetracker.google.com/issues/275849044 ) { ListItem( - modifier = Modifier.clickable { eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) }, + modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) }, icon = { Icon(Icons.Default.Collections, null) }, text = { Text(stringResource(R.string.screen_room_attachment_source_gallery)) }, ) ListItem( - modifier = Modifier.clickable { eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) }, + modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) }, icon = { Icon(Icons.Default.AttachFile, null) }, text = { Text(stringResource(R.string.screen_room_attachment_source_files)) }, ) ListItem( - modifier = Modifier.clickable { eventSink(MessageComposerEvents.PickAttachmentSource.PhotoFromCamera) }, + modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.PhotoFromCamera) }, icon = { Icon(Icons.Default.PhotoCamera, null) }, text = { Text(stringResource(R.string.screen_room_attachment_source_camera_photo)) }, ) ListItem( - modifier = Modifier.clickable { eventSink(MessageComposerEvents.PickAttachmentSource.VideoFromCamera) }, + modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.VideoFromCamera) }, icon = { Icon(Icons.Default.Videocam, null) }, text = { Text(stringResource(R.string.screen_room_attachment_source_camera_video)) }, ) - ListItem( - modifier = Modifier.clickable { - eventSink(MessageComposerEvents.PickAttachmentSource.Location) - onSendLocationClicked() - }, - icon = { Icon(Icons.Default.LocationOn, null) }, - text = { Text(stringResource(R.string.screen_room_attachment_source_location)) }, - ) + if (state.canShareLocation) { + ListItem( + modifier = Modifier.clickable { + state.eventSink(MessageComposerEvents.PickAttachmentSource.Location) + onSendLocationClicked() + }, + icon = { Icon(Icons.Default.LocationOn, null) }, + text = { Text(stringResource(R.string.screen_room_attachment_source_location)) }, + ) + } } } @@ -136,7 +138,9 @@ internal fun AttachmentSourcePickerMenu( @Composable internal fun AttachmentSourcePickerMenuPreview() = ElementPreview { AttachmentSourcePickerMenu( - eventSink = {}, + state = aMessageComposerState( + canShareLocation = true, + ), onSendLocationClicked = {}, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index 020236e890..49f66c140c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -74,6 +74,11 @@ class MessageComposerPresenter @Inject constructor( mutableStateOf(AttachmentsState.None) } + var canShareLocation = false + LaunchedEffect(Unit) { + canShareLocation = featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing) + } + val galleryMediaPicker = mediaPickerProvider.registerGalleryPicker { uri, mimeType -> handlePickedMedia(attachmentsState, uri, mimeType) } @@ -140,23 +145,23 @@ class MessageComposerPresenter @Inject constructor( ) ) } - MessageComposerEvents.AddAttachment -> localCoroutineScope.launchIfMediaPickerEnabled { + MessageComposerEvents.AddAttachment -> localCoroutineScope.launch { showAttachmentSourcePicker = true } MessageComposerEvents.DismissAttachmentMenu -> showAttachmentSourcePicker = false - MessageComposerEvents.PickAttachmentSource.FromGallery -> localCoroutineScope.launchIfMediaPickerEnabled { + MessageComposerEvents.PickAttachmentSource.FromGallery -> localCoroutineScope.launch { showAttachmentSourcePicker = false galleryMediaPicker.launch() } - MessageComposerEvents.PickAttachmentSource.FromFiles -> localCoroutineScope.launchIfMediaPickerEnabled { + MessageComposerEvents.PickAttachmentSource.FromFiles -> localCoroutineScope.launch { showAttachmentSourcePicker = false filesPicker.launch() } - MessageComposerEvents.PickAttachmentSource.PhotoFromCamera -> localCoroutineScope.launchIfMediaPickerEnabled { + MessageComposerEvents.PickAttachmentSource.PhotoFromCamera -> localCoroutineScope.launch { showAttachmentSourcePicker = false cameraPhotoPicker.launch() } - MessageComposerEvents.PickAttachmentSource.VideoFromCamera -> localCoroutineScope.launchIfMediaPickerEnabled { + MessageComposerEvents.PickAttachmentSource.VideoFromCamera -> localCoroutineScope.launch { showAttachmentSourcePicker = false cameraVideoPicker.launch() } @@ -173,17 +178,12 @@ class MessageComposerPresenter @Inject constructor( hasFocus = hasFocus.value, mode = messageComposerContext.composerMode, showAttachmentSourcePicker = showAttachmentSourcePicker, + canShareLocation = canShareLocation, attachmentsState = attachmentsState.value, eventSink = ::handleEvents ) } - private fun CoroutineScope.launchIfMediaPickerEnabled(action: suspend () -> Unit) = launch { - if (featureFlagService.isFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow)) { - action() - } - } - private fun CoroutineScope.sendMessage( text: String, updateComposerMode: (newComposerMode: MessageComposerMode) -> Unit, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt index 28ec14ffeb..32faaf9d81 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt @@ -28,6 +28,7 @@ data class MessageComposerState( val hasFocus: Boolean, val mode: MessageComposerMode, val showAttachmentSourcePicker: Boolean, + val canShareLocation: Boolean, val attachmentsState: AttachmentsState, val eventSink: (MessageComposerEvents) -> Unit ) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt index 1934154824..a1fbb7ffa0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt @@ -26,12 +26,21 @@ open class MessageComposerStateProvider : PreviewParameterProvider false + FeatureFlags.LocationSharing -> false } } else { false diff --git a/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt b/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt index 0351b20716..ea9b03acdf 100644 --- a/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt +++ b/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt @@ -26,14 +26,14 @@ class DefaultFeatureFlagServiceTest { @Test fun `given service without provider when feature is checked then it returns the default value`() = runTest { val featureFlagService = DefaultFeatureFlagService(emptySet()) - val isFeatureEnabled = featureFlagService.isFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow) - assertThat(isFeatureEnabled).isEqualTo(FeatureFlags.ShowMediaUploadingFlow.defaultValue) + val isFeatureEnabled = featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing) + assertThat(isFeatureEnabled).isEqualTo(FeatureFlags.LocationSharing.defaultValue) } @Test fun `given service without provider when set enabled feature is called then it returns false`() = runTest { val featureFlagService = DefaultFeatureFlagService(emptySet()) - val result = featureFlagService.setFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow, true) + val result = featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, true) assertThat(result).isEqualTo(false) } @@ -41,7 +41,7 @@ class DefaultFeatureFlagServiceTest { fun `given service with a runtime provider when set enabled feature is called then it returns true`() = runTest { val featureFlagProvider = FakeRuntimeFeatureFlagProvider(0) val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider)) - val result = featureFlagService.setFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow, true) + val result = featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, true) assertThat(result).isEqualTo(true) } @@ -49,10 +49,10 @@ class DefaultFeatureFlagServiceTest { fun `given service with a runtime provider and feature enabled when feature is checked then it returns the correct value`() = runTest { val featureFlagProvider = FakeRuntimeFeatureFlagProvider(0) val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider)) - featureFlagService.setFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow, true) - assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow)).isEqualTo(true) - featureFlagService.setFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow, false) - assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow)).isEqualTo(false) + featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, true) + assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing)).isEqualTo(true) + featureFlagService.setFeatureEnabled(FeatureFlags.LocationSharing, false) + assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing)).isEqualTo(false) } @Test @@ -60,8 +60,8 @@ class DefaultFeatureFlagServiceTest { val lowPriorityfeatureFlagProvider = FakeRuntimeFeatureFlagProvider(LOW_PRIORITY) val highPriorityfeatureFlagProvider = FakeRuntimeFeatureFlagProvider(HIGH_PRIORITY) val featureFlagService = DefaultFeatureFlagService(setOf(lowPriorityfeatureFlagProvider, highPriorityfeatureFlagProvider)) - lowPriorityfeatureFlagProvider.setFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow, false) - highPriorityfeatureFlagProvider.setFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow, true) - assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow)).isEqualTo(true) + lowPriorityfeatureFlagProvider.setFeatureEnabled(FeatureFlags.LocationSharing, false) + highPriorityfeatureFlagProvider.setFeatureEnabled(FeatureFlags.LocationSharing, true) + assertThat(featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing)).isEqualTo(true) } } From 1c90c32c4e709aba4ebf89ed73f030ca972724e4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 22:21:31 +0200 Subject: [PATCH 061/251] Enable LocationSharing flag for the release. --- .../libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt index 05534ad53a..d226885495 100644 --- a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt +++ b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/BuildtimeFeatureFlagProvider.kt @@ -29,7 +29,7 @@ class BuildtimeFeatureFlagProvider @Inject constructor() : override suspend fun isFeatureEnabled(feature: Feature): Boolean { return if (feature is FeatureFlags) { when (feature) { - FeatureFlags.LocationSharing -> false + FeatureFlags.LocationSharing -> true } } else { false From f0d02996dfab08890156ed020c76298cc5dfd084 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 22:23:54 +0200 Subject: [PATCH 062/251] canShareLocation must be a MutableState. --- .../impl/messagecomposer/MessageComposerPresenter.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index 49f66c140c..ec16d3342d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -74,9 +74,9 @@ class MessageComposerPresenter @Inject constructor( mutableStateOf(AttachmentsState.None) } - var canShareLocation = false + val canShareLocation = remember { mutableStateOf(false) } LaunchedEffect(Unit) { - canShareLocation = featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing) + canShareLocation.value = featureFlagService.isFeatureEnabled(FeatureFlags.LocationSharing) } val galleryMediaPicker = mediaPickerProvider.registerGalleryPicker { uri, mimeType -> @@ -178,7 +178,7 @@ class MessageComposerPresenter @Inject constructor( hasFocus = hasFocus.value, mode = messageComposerContext.composerMode, showAttachmentSourcePicker = showAttachmentSourcePicker, - canShareLocation = canShareLocation, + canShareLocation = canShareLocation.value, attachmentsState = attachmentsState.value, eventSink = ::handleEvents ) From a41fffbe4c6fb171487bf8d823387a6a11486dce Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 23:17:29 +0200 Subject: [PATCH 063/251] Must skip 1 item due to the location feature flag value emitting 1 item. --- .../messages/MessagesPresenterTest.kt | 60 ++++++++----------- .../MessageComposerPresenterTest.kt | 19 ++++++ 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index 95848d835f..e0af846656 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -84,7 +84,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() assertThat(initialState.roomId).isEqualTo(A_ROOM_ID) } @@ -98,7 +98,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID)) assertThat(room.myReactions.count()).isEqualTo(1) @@ -119,7 +119,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID)) assertThat(room.myReactions.count()).isEqualTo(1) @@ -136,7 +136,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Forward, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -152,7 +152,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Copy, event)) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -166,10 +166,9 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent())) - val finalState = awaitItem() assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -182,7 +181,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent(eventId = null))) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -197,7 +196,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() val mediaMessage = aMessageEvent( content = TimelineItemImageContent( @@ -214,7 +213,6 @@ class MessagesPresenterTest { ) ) initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, mediaMessage)) - val finalState = awaitItem() assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java) val replyMode = finalState.composerState.mode as MessageComposerMode.Reply @@ -229,7 +227,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() val mediaMessage = aMessageEvent( content = TimelineItemVideoContent( @@ -262,7 +260,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() val mediaMessage = aMessageEvent( content = TimelineItemFileContent( @@ -275,7 +273,6 @@ class MessagesPresenterTest { ) ) initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, mediaMessage)) - val finalState = awaitItem() assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Reply::class.java) val replyMode = finalState.composerState.mode as MessageComposerMode.Reply @@ -290,10 +287,9 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Edit, aMessageEvent())) - val finalState = awaitItem() assertThat(finalState.composerState.mode).isInstanceOf(MessageComposerMode.Edit::class.java) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -308,7 +304,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Redact, aMessageEvent())) assertThat(matrixRoom.redactEventEventIdParam).isEqualTo(AN_EVENT_ID) @@ -323,7 +319,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.ReportContent, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -337,7 +333,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.Dismiss) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -351,7 +347,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Developer, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -366,17 +362,14 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() - // Initially the composer doesn't have focus, so we don't show the alert assertThat(initialState.showReinvitePrompt).isFalse() - // When the input field is focused we show the alert initialState.composerState.eventSink(MessageComposerEvents.FocusChanged(true)) val focusedState = awaitItem() assertThat(focusedState.showReinvitePrompt).isTrue() - // If it's dismissed then we stop showing the alert initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Cancel)) val dismissedState = awaitItem() @@ -391,10 +384,9 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() assertThat(initialState.showReinvitePrompt).isFalse() - initialState.composerState.eventSink(MessageComposerEvents.FocusChanged(true)) val focusedState = awaitItem() assertThat(focusedState.showReinvitePrompt).isFalse() @@ -408,10 +400,9 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) val initialState = awaitItem() assertThat(initialState.showReinvitePrompt).isFalse() - initialState.composerState.eventSink(MessageComposerEvents.FocusChanged(true)) val focusedState = awaitItem() assertThat(focusedState.showReinvitePrompt).isFalse() @@ -433,14 +424,13 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() skipItems(1) initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) - skipItems(1) val loadingState = awaitItem() assertThat(loadingState.inviteProgress.isLoading()).isTrue() - val newState = awaitItem() assertThat(newState.inviteProgress.isSuccess()).isTrue() assertThat(room.invitedUserId).isEqualTo(A_SESSION_ID_2) @@ -463,14 +453,13 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() skipItems(1) initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) - skipItems(1) val loadingState = awaitItem() assertThat(loadingState.inviteProgress.isLoading()).isTrue() - val newState = awaitItem() assertThat(newState.inviteProgress.isSuccess()).isTrue() assertThat(room.invitedUserId).isEqualTo(A_SESSION_ID_2) @@ -485,14 +474,13 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() skipItems(1) initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) - skipItems(1) val loadingState = awaitItem() assertThat(loadingState.inviteProgress.isLoading()).isTrue() - val newState = awaitItem() assertThat(newState.inviteProgress.isFailure()).isTrue() } @@ -514,13 +502,13 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() skipItems(1) initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) skipItems(1) val loadingState = awaitItem() assertThat(loadingState.inviteProgress.isLoading()).isTrue() - val newState = awaitItem() assertThat(newState.inviteProgress.isFailure()).isTrue() } @@ -534,7 +522,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - + skipItems(1) assertThat(awaitItem().userHasPermissionToSendMessage).isTrue() } } @@ -549,7 +537,7 @@ class MessagesPresenterTest { }.test { // Default value assertThat(awaitItem().userHasPermissionToSendMessage).isTrue() - skipItems(1) + skipItems(2) assertThat(awaitItem().userHasPermissionToSendMessage).isFalse() } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt index 92daee6dfb..f0871f035a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt @@ -81,6 +81,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.isFullScreen).isFalse() assertThat(initialState.text).isEqualTo("") @@ -97,6 +98,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessageComposerEvents.ToggleFullScreenState) val fullscreenState = awaitItem() @@ -113,6 +115,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessageComposerEvents.UpdateText(A_MESSAGE)) val withMessageState = awaitItem() @@ -131,6 +134,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) var state = awaitItem() val mode = anEditMode() state.eventSink.invoke(MessageComposerEvents.SetMode(mode)) @@ -149,6 +153,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) var state = awaitItem() val mode = aReplyMode() state.eventSink.invoke(MessageComposerEvents.SetMode(mode)) @@ -166,6 +171,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) var state = awaitItem() val mode = aQuoteMode() state.eventSink.invoke(MessageComposerEvents.SetMode(mode)) @@ -183,6 +189,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessageComposerEvents.UpdateText(A_MESSAGE)) val withMessageState = awaitItem() @@ -205,6 +212,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.text).isEqualTo("") val mode = anEditMode() @@ -236,6 +244,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.text).isEqualTo("") val mode = anEditMode(eventId = null, transactionId = A_TRANSACTION_ID) @@ -267,6 +276,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.text).isEqualTo("") val mode = aReplyMode() @@ -294,6 +304,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() assertThat(initialState.showAttachmentSourcePicker).isEqualTo(false) initialState.eventSink(MessageComposerEvents.AddAttachment) @@ -307,6 +318,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() initialState.eventSink(MessageComposerEvents.AddAttachment) skipItems(1) @@ -341,6 +353,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) val previewingState = awaitItem() @@ -375,6 +388,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) val previewingState = awaitItem() @@ -393,6 +407,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) // No crashes here, otherwise it fails @@ -413,6 +428,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) val sendingState = awaitItem() @@ -434,6 +450,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.PhotoFromCamera) val previewingState = awaitItem() @@ -450,6 +467,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.VideoFromCamera) val previewingState = awaitItem() @@ -467,6 +485,7 @@ class MessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { + skipItems(1) val initialState = awaitItem() initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) val sendingState = awaitItem() From e03408a806e1d0ade0391eecbcdca09a72a3afad Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Jul 2023 23:18:18 +0200 Subject: [PATCH 064/251] Test new field `canShareLocation` --- .../messages/textcomposer/MessageComposerPresenterTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt index f0871f035a..5c75c963e5 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt @@ -87,6 +87,7 @@ class MessageComposerPresenterTest { assertThat(initialState.text).isEqualTo("") assertThat(initialState.mode).isEqualTo(MessageComposerMode.Normal("")) assertThat(initialState.showAttachmentSourcePicker).isFalse() + assertThat(initialState.canShareLocation).isTrue() assertThat(initialState.attachmentsState).isEqualTo(AttachmentsState.None) assertThat(initialState.isSendButtonVisible).isFalse() } From 68c943e08dd9a28d1e463963039824d0e45c36a9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Jul 2023 04:57:25 +0000 Subject: [PATCH 065/251] Update dependency app.cash.molecule:molecule-runtime to v1.1.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9dc16c1ae6..7ebf397ac7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ android_gradle_plugin = "8.0.2" kotlin = "1.8.22" ksp = "1.8.22-1.0.11" -molecule = "1.0.0" +molecule = "1.1.0" # AndroidX material = "1.9.0" From ac108c45823cb04303cddd14f3c2ab48c498878c Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 21 Jul 2023 10:20:37 +0200 Subject: [PATCH 066/251] Introduce Disposable extension to destroy all disposable in an Iterable --- .../libraries/matrix/impl/util/Disposables.kt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/Disposables.kt diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/Disposables.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/Disposables.kt new file mode 100644 index 0000000000..ac92a2e026 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/Disposables.kt @@ -0,0 +1,24 @@ +/* + * 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.libraries.matrix.impl.util + +import org.matrix.rustcomponents.sdk.Disposable + +/** + * Call destroy on all elements of the iterable. + */ +internal fun Iterable.destroyAll() = forEach { it.destroy() } From 0fd5032dba7646aab568079b4298bf9021b32817 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 10:24:11 +0200 Subject: [PATCH 067/251] Nightly build: upload the application bundle to Firebase instead of the universal APK. --- .github/workflows/nightly.yml | 12 +++++++----- app/build.gradle.kts | 7 ++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 956eebe71d..c893459f04 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,4 +1,4 @@ -name: Build and release nightly APK +name: Build and release nightly application on: workflow_dispatch: @@ -12,7 +12,7 @@ env: jobs: nightly: - name: Build and publish nightly APK to Firebase + name: Build and publish nightly bundle to Firebase runs-on: ubuntu-latest if: ${{ github.repository == 'vector-im/element-x-android' }} steps: @@ -31,9 +31,9 @@ jobs: sed 's/CHANGES\.md/CHANGES_NIGHTLY\.md/' towncrier.toml.bak > towncrier.toml rm towncrier.toml.bak yes n | towncrier build --version nightly - - name: Build and upload Nightly APK + - name: Build and upload Nightly bundle run: | - ./gradlew assembleNightly appDistributionUploadNightly $CI_GRADLE_ARG_PROPERTIES + ./gradlew bundleNightly appDistributionUploadNightly $CI_GRADLE_ARG_PROPERTIES env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} @@ -44,7 +44,9 @@ jobs: FIREBASE_TOKEN: ${{ secrets.ELEMENT_ANDROID_NIGHTLY_FIREBASE_TOKEN }} - name: Additionally upload Nightly APK to browserstack for testing continue-on-error: true # don't block anything by this upload failing (for now) - run: curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_PASSWORD" -X POST "https://api-cloud.browserstack.com/app-automate/upload" -F "file=@app/build/outputs/apk/nightly/app-universal-nightly.apk" -F "custom_id=element-x-android-nightly" + run: | + ./gradlew assembleNightly $CI_GRADLE_ARG_PROPERTIES + curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_PASSWORD" -X POST "https://api-cloud.browserstack.com/app-automate/upload" -F "file=@app/build/outputs/apk/nightly/app-universal-nightly.apk" -F "custom_id=element-x-android-nightly" env: BROWSERSTACK_USERNAME: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_USERNAME }} BROWSERSTACK_PASSWORD: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_ACCESS_KEY }} diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6a28adecf1..79d6af8307 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -124,11 +124,8 @@ android { } firebaseAppDistribution { - artifactType = "APK" - // We upload the universal APK to fix this error: - // "App Distribution found more than 1 output file for this variant. - // Please contact firebase-support@google.com for help using APK splits with App Distribution." - artifactPath = "$rootDir/app/build/outputs/apk/nightly/app-universal-nightly.apk" + artifactType = "AAB" + artifactPath = "$rootDir/app/build/outputs/bundle/nightly/app-nightly.aab" // This file will be generated by the GitHub action releaseNotesFile = "CHANGES_NIGHTLY.md" groups = "external-testers" From 7f56e9e06a4056173dc573668ac6b80a441bcd25 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 10:28:47 +0200 Subject: [PATCH 068/251] Upload smaller APK to Maestro Cloud. --- .github/workflows/maestro.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maestro.yml b/.github/workflows/maestro.yml index 01783a1eb6..d3274d0b14 100644 --- a/.github/workflows/maestro.yml +++ b/.github/workflows/maestro.yml @@ -49,7 +49,9 @@ jobs: - uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} - app-file: app/build/outputs/apk/debug/app-universal-debug.apk + # Doc says (https://github.com/mobile-dev-inc/action-maestro-cloud#android): + # app-file should point to an x86 compatible APK file, so upload the x86_64 one (much smaller than the universal APK). + app-file: app/build/outputs/apk/debug/app-x86_64-debug.apk env: | USERNAME=maestroelement PASSWORD=${{ secrets.MATRIX_MAESTRO_ACCOUNT_PASSWORD }} From c93650a7804e787781e9a48fdb6bb16ec6f63be1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 10:49:38 +0200 Subject: [PATCH 069/251] Fix version code issue. --- plugins/src/main/kotlin/Versions.kt | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index a3dfa86de9..37ffeacfa7 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -17,6 +17,33 @@ import org.gradle.api.JavaVersion import org.gradle.jvm.toolchain.JavaLanguageVersion +/** + * Version codes are quite sensitive, because there is a mix between bundle and APKs, and we have to take into + * account the future upgrade of Element Android. + * Max versionCode allowed by the PlayStore (for information): + * 2_100_000_000 + * Current version code of EAx on the PlayStore, for the first uploaded beta (we cannot go below): + * ----1_001_000 + * Current version code of EAx on the nightly: + * ----1_001_000 + * Current version of Element Android (at some point EAx will replace this app) (v1.6.3) + * ----40_106_03a where a stands for the architecture: 1, 2, 3, 4 and 0 for the universal APK + * Current version of EAx distributed with Firebase app distribution: + * ----1_002_000 + * Latest version of EAx distributed with Firebase app distribution (downgrading, so that's a problem) + * -------10_200 + * Version when running the current debug build + * -------10_200 + * + * So adding 4_000_000 to the current version Code computed here should be fine, we will have: + * Release version: + * ---40_001_020 + * Nightly version: + * ---40_001_020 + * Debug version: + * ---40_010_200 + */ + // Note: 2 digits max for each value private const val versionMajor = 0 private const val versionMinor = 1 @@ -27,7 +54,7 @@ private const val versionMinor = 1 private const val versionPatch = 2 object Versions { - val versionCode = (versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch) * 10 + val versionCode = 4_000_000 + versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch val versionName = "$versionMajor.$versionMinor.$versionPatch" const val compileSdk = 33 const val targetSdk = 33 From aa99a77cb6ecfe0e0155f12d098b355cb4a545e7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 12:07:02 +0200 Subject: [PATCH 070/251] Add missing env vars, now that we are building the APK in this task. --- .github/workflows/nightly.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index c893459f04..8f4cea0a55 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -48,5 +48,8 @@ jobs: ./gradlew assembleNightly $CI_GRADLE_ARG_PROPERTIES curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_PASSWORD" -X POST "https://api-cloud.browserstack.com/app-automate/upload" -F "file=@app/build/outputs/apk/nightly/app-universal-nightly.apk" -F "custom_id=element-x-android-nightly" env: + ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} + ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} + ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} BROWSERSTACK_USERNAME: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_USERNAME }} BROWSERSTACK_PASSWORD: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_ACCESS_KEY }} From 0ef012ac7baa8d22ac64d0f61818996848ebacf8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 12:10:04 +0200 Subject: [PATCH 071/251] Clarify the computation of versionCode. --- plugins/src/main/kotlin/Versions.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 37ffeacfa7..2e89afa70f 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -35,7 +35,12 @@ import org.gradle.jvm.toolchain.JavaLanguageVersion * Version when running the current debug build * -------10_200 * - * So adding 4_000_000 to the current version Code computed here should be fine, we will have: + * So adding 4_000_000 to the current version Code computed here should be fine, and since the versionCode + * is multiplied by 10 in app/build.gradle.kts#L168: + * ``` + * output.versionCode.set((output.versionCode.get() ?: 0) * 10 + abiCode)) + * ``` + * we will have: * Release version: * ---40_001_020 * Nightly version: From 03b2b0d4847d917e4ab7580d70f2816f29edeede Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 12:44:12 +0200 Subject: [PATCH 072/251] Add a shortcut to ensure that ./gradlew check will prevent having warning in the codebase. We may add more instructions in the future in this file. --- tools/quality/check.sh | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100755 tools/quality/check.sh diff --git a/tools/quality/check.sh b/tools/quality/check.sh new file mode 100755 index 0000000000..c3f87b3018 --- /dev/null +++ b/tools/quality/check.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +# +# Copyright 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. +# + +# List of tasks to run before creating a PR, to limit the risk of getting rejected by the CI. +# Can be used as a git hook if you want. + +# exit when any command fails +set -e + +# First run the quickest script +./tools/check/check_code_quality.sh + +# Build, test and check the project, with warning as errors +# It also check that the minimal app is compiling. +./gradlew check -PallWarningsAsErrors=true From de7866be19b01bb3631b80752675395f2112421d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 12:51:10 +0200 Subject: [PATCH 073/251] Ensure the release script will exist in case of failure. --- tools/release/release.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/release/release.sh b/tools/release/release.sh index 54ee8117ec..9934f43610 100755 --- a/tools/release/release.sh +++ b/tools/release/release.sh @@ -16,6 +16,9 @@ # limitations under the License. # +# exit when any command fails +set -e + printf "\n================================================================================\n" printf "| Welcome to the release script! |\n" printf "================================================================================\n" From a87ae86398921441a28d5a3ee7a4bf2e9f7ed0e0 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 21 Jul 2023 10:24:57 +0200 Subject: [PATCH 074/251] Deadlock: makes sure timelineListener TaskHandle.cancel is called (and memory is released correctly) --- .../matrix/impl/room/RustMatrixRoom.kt | 8 +++--- .../impl/timeline/RoomTimelineExtensions.kt | 25 ++++++++++++++++--- .../impl/timeline/RustMatrixTimeline.kt | 9 ++++--- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 16434aa3c8..b185c34205 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -122,10 +122,12 @@ class RustMatrixRoom( innerRoom.timelineDiffFlow { initialList -> _timeline.postItems(initialList) }.onEach { diff -> - if (diff.eventOrigin() == EventItemOrigin.SYNC) { - _syncUpdateFlow.value = systemClock.epochMillis() + diff.use { + if (diff.eventOrigin() == EventItemOrigin.SYNC) { + _syncUpdateFlow.value = systemClock.epochMillis() + } + _timeline.postDiff(diff) } - _timeline.postDiff(diff) }.launchIn(this) innerRoom.backPaginationStatusFlow() diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt index d6febb32dc..29b75a1dca 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt @@ -16,28 +16,45 @@ package io.element.android.libraries.matrix.impl.timeline +import io.element.android.libraries.matrix.impl.util.destroyAll import io.element.android.libraries.matrix.impl.util.mxCallbackFlow import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.trySendBlocking import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.buffer +import kotlinx.coroutines.flow.callbackFlow import org.matrix.rustcomponents.sdk.BackPaginationStatus import org.matrix.rustcomponents.sdk.BackPaginationStatusListener import org.matrix.rustcomponents.sdk.Room +import org.matrix.rustcomponents.sdk.RoomTimelineListenerResult import org.matrix.rustcomponents.sdk.TimelineDiff import org.matrix.rustcomponents.sdk.TimelineItem import org.matrix.rustcomponents.sdk.TimelineListener +import timber.log.Timber internal fun Room.timelineDiffFlow(onInitialList: suspend (List) -> Unit): Flow = - mxCallbackFlow { + callbackFlow { + val roomId = id() + Timber.d("Open timelineDiffFlow for room $roomId") val listener = object : TimelineListener { override fun onUpdate(diff: TimelineDiff) { trySendBlocking(diff) } } - val result = addTimelineListener(listener) - onInitialList(result.items) - result.itemsStream + var result: RoomTimelineListenerResult? = null + try { + result = addTimelineListener(listener) + onInitialList(result.items) + } catch (exception: Exception) { + Timber.d(exception, "Catch failure in timelineDiffFlow of room $roomId") + } + awaitClose { + Timber.d("Close timelineDiffFlow for room $roomId") + result?.itemsStream?.cancel() + result?.itemsStream?.destroy() + result?.items?.destroyAll() + } }.buffer(Channel.UNLIMITED) internal fun Room.backPaginationStatusFlow(): Flow = diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt index e213fb623c..5e7f4a2282 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt @@ -26,12 +26,14 @@ import io.element.android.libraries.matrix.impl.timeline.item.event.EventMessage import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper -import kotlinx.coroutines.CompletableDeferred import io.element.android.libraries.matrix.impl.timeline.postprocessor.TimelineEncryptedHistoryPostProcessor +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.ensureActive import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -46,8 +48,8 @@ import org.matrix.rustcomponents.sdk.Room import org.matrix.rustcomponents.sdk.TimelineDiff import org.matrix.rustcomponents.sdk.TimelineItem import timber.log.Timber -import java.util.concurrent.atomic.AtomicBoolean import java.util.Date +import java.util.concurrent.atomic.AtomicBoolean private const val INITIAL_MAX_SIZE = 50 @@ -99,9 +101,10 @@ class RustMatrixTimeline( encryptedHistoryPostProcessor.process(items) } - internal suspend fun postItems(items: List) { + internal suspend fun postItems(items: List) = coroutineScope { // Split the initial items in multiple list as there is no pagination in the cached data, so we can post timelineItems asap. items.chunked(INITIAL_MAX_SIZE).reversed().forEach { + ensureActive() timelineDiffProcessor.postItems(it) } isInit.set(true) From c0b8388fadacbb7a251c53cf977a43151fdc0a01 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 21 Jul 2023 14:12:54 +0200 Subject: [PATCH 075/251] Session.getRoom : suspend the whole method --- .../libraries/matrix/impl/RustMatrixClient.kt | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 640e0772a9..c0bdcbb77e 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -150,7 +150,7 @@ class RustMatrixClient constructor( }.launchIn(sessionCoroutineScope) } - override suspend fun getRoom(roomId: RoomId): MatrixRoom? { + override suspend fun getRoom(roomId: RoomId): MatrixRoom? = withContext(sessionDispatcher) { // Check if already in memory... var cachedPairOfRoom = pairOfRoom(roomId) if (cachedPairOfRoom == null) { @@ -158,24 +158,27 @@ class RustMatrixClient constructor( roomSummaryDataSource.awaitAllRoomsAreLoaded() cachedPairOfRoom = pairOfRoom(roomId) } - if (cachedPairOfRoom == null) return null - val (roomListItem, fullRoom) = cachedPairOfRoom - return RustMatrixRoom( - sessionId = sessionId, - roomListItem = roomListItem, - innerRoom = fullRoom, - sessionCoroutineScope = sessionCoroutineScope, - coroutineDispatchers = dispatchers, - systemClock = clock, - roomContentForwarder = roomContentForwarder, - sessionData = sessionStore.getSession(sessionId.value)!!, - ) + return@withContext if (cachedPairOfRoom == null) { + null + } else { + val (roomListItem, fullRoom) = cachedPairOfRoom + RustMatrixRoom( + sessionId = sessionId, + roomListItem = roomListItem, + innerRoom = fullRoom, + sessionCoroutineScope = sessionCoroutineScope, + coroutineDispatchers = dispatchers, + systemClock = clock, + roomContentForwarder = roomContentForwarder, + sessionData = sessionStore.getSession(sessionId.value)!!, + ) + } } - private suspend fun pairOfRoom(roomId: RoomId): Pair? = withContext(sessionDispatcher) { + private fun pairOfRoom(roomId: RoomId): Pair? { val cachedRoomListItem = roomListService.roomOrNull(roomId.value) val fullRoom = cachedRoomListItem?.fullRoom() - if (cachedRoomListItem == null || fullRoom == null) { + return if (cachedRoomListItem == null || fullRoom == null) { Timber.d("No room cached for $roomId") null } else { From 2f92203d85329876771fa6f75f4f6050aca6b3ba Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 21 Jul 2023 15:19:19 +0200 Subject: [PATCH 076/251] Room: avoid calling displayName/avatarData on each recomposition --- .../appnav/room/LoadingRoomNodeView.kt | 25 +-------- .../messages/impl/MessagesPresenter.kt | 14 +++-- .../features/messages/impl/MessagesState.kt | 4 +- .../messages/impl/MessagesStateProvider.kt | 8 ++- .../features/messages/impl/MessagesView.kt | 55 +++++++++++++------ .../IconTitlePlaceholdersRowMolecule.kt | 43 +++++++++++++++ 6 files changed, 99 insertions(+), 50 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt index e8d68a3e94..aa01b259de 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt @@ -16,19 +16,13 @@ package io.element.android.appnav.room -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -37,9 +31,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView -import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom +import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreviewDark @@ -48,7 +41,6 @@ import io.element.android.libraries.designsystem.theme.components.CircularProgre import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.designsystem.theme.placeholderBackground import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @@ -103,20 +95,7 @@ private fun LoadingRoomTopBar( BackButton(onClick = onBackClicked) }, title = { - Row( - verticalAlignment = Alignment.CenterVertically - ) { - Box( - modifier = Modifier - .size(AvatarSize.TimelineRoom.dp) - .align(Alignment.CenterVertically) - .background(color = ElementTheme.colors.placeholderBackground, shape = CircleShape) - ) - Spacer(modifier = Modifier.width(8.dp)) - PlaceholderAtom(width = 20.dp, height = 7.dp) - Spacer(modifier = Modifier.width(7.dp)) - PlaceholderAtom(width = 45.dp, height = 7.dp) - } + IconTitlePlaceholdersRowMolecule(iconSize = AvatarSize.TimelineRoom.dp) }, windowInsets = WindowInsets(0.dp), ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index a0a3e3a286..acaaf54c9e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -24,7 +24,6 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable @@ -76,6 +75,7 @@ import io.element.android.libraries.matrix.ui.room.canSendMessageAsState import io.element.android.libraries.textcomposer.MessageComposerMode import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import timber.log.Timber class MessagesPresenter @AssistedInject constructor( @@ -109,11 +109,13 @@ class MessagesPresenter @AssistedInject constructor( val syncUpdateFlow = room.syncUpdateFlow.collectAsState() val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value) - val roomName by produceState(initialValue = room.displayName, key1 = syncUpdateFlow.value) { - value = room.displayName - } - val roomAvatar by produceState(initialValue = room.avatarData(), key1 = syncUpdateFlow.value) { - value = room.avatarData() + var roomName: Async by remember { mutableStateOf(Async.Uninitialized) } + var roomAvatar: Async by remember { mutableStateOf(Async.Uninitialized) } + LaunchedEffect(syncUpdateFlow.value) { + withContext(dispatchers.io) { + roomName = Async.Success(room.displayName) + roomAvatar = Async.Success(room.avatarData()) + } } var hasDismissedInviteDialog by rememberSaveable { mutableStateOf(false) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt index 8a067a3a26..a042ec1ac4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt @@ -30,8 +30,8 @@ import io.element.android.libraries.matrix.api.core.RoomId @Immutable data class MessagesState( val roomId: RoomId, - val roomName: String, - val roomAvatar: AvatarData, + val roomName: Async, + val roomAvatar: Async, val userHasPermissionToSendMessage: Boolean, val composerState: MessageComposerState, val timelineState: TimelineState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index d0ddcf68f4..582cd2e7ab 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -34,6 +34,10 @@ open class MessagesStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMessagesState(), + aMessagesState().copy( + roomName = Async.Uninitialized, + roomAvatar = Async.Uninitialized, + ), aMessagesState().copy(hasNetworkConnection = false), aMessagesState().copy(composerState = aMessageComposerState().copy(showAttachmentSourcePicker = true)), aMessagesState().copy(userHasPermissionToSendMessage = false), @@ -43,8 +47,8 @@ open class MessagesStateProvider : PreviewParameterProvider { fun aMessagesState() = MessagesState( roomId = RoomId("!id:domain"), - roomName = "Room name", - roomAvatar = AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom), + roomName = Async.Success("Room name"), + roomAvatar = Async.Success(AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom)), userHasPermissionToSendMessage = true, composerState = aMessageComposerState().copy( text = "Hello", diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 6d8f2792e0..d8a8446c8c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -43,13 +43,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontStyle -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import io.element.android.features.messages.impl.actionlist.ActionListEvents import io.element.android.features.messages.impl.actionlist.ActionListView import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction @@ -64,10 +62,12 @@ import io.element.android.features.messages.impl.timeline.components.retrysendme import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView import io.element.android.libraries.androidutils.ui.hideKeyboard +import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.ProgressDialogType import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.preview.ElementPreviewDark @@ -137,8 +137,8 @@ fun MessagesView( Column { ConnectivityIndicatorView(isOnline = state.hasNetworkConnection) MessagesViewTopBar( - roomTitle = state.roomName, - roomAvatar = state.roomAvatar, + roomName = state.roomName.dataOrNull(), + roomAvatar = state.roomAvatar.dataOrNull(), onBackPressed = onBackPressed, onRoomDetailsClicked = onRoomDetailsClicked, ) @@ -289,29 +289,50 @@ fun MessagesViewContent( @OptIn(ExperimentalMaterial3Api::class) @Composable fun MessagesViewTopBar( - roomTitle: String, - roomAvatar: AvatarData, + roomName: String?, + roomAvatar: AvatarData?, modifier: Modifier = Modifier, onRoomDetailsClicked: () -> Unit = {}, onBackPressed: () -> Unit = {}, ) { + @Composable + fun RoomAvatarAndNameRow( + roomName: String, + roomAvatar: AvatarData, + modifier: Modifier = Modifier + ) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically + ) { + Avatar(roomAvatar) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = roomName, + style = ElementTheme.typography.fontBodyLgMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + TopAppBar( modifier = modifier, navigationIcon = { BackButton(onClick = onBackPressed) }, title = { - Row( - modifier = Modifier.clickable { onRoomDetailsClicked() }, - verticalAlignment = Alignment.CenterVertically - ) { - Avatar(roomAvatar) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = roomTitle, - style = ElementTheme.typography.fontBodyLgMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis + val titleModifier = Modifier.clickable { onRoomDetailsClicked() } + if (roomName != null && roomAvatar != null) { + RoomAvatarAndNameRow( + roomName = roomName, + roomAvatar = roomAvatar, + modifier = titleModifier + ) + } else { + IconTitlePlaceholdersRowMolecule( + iconSize = AvatarSize.TimelineRoom.dp, + modifier = titleModifier ) } }, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt new file mode 100644 index 0000000000..ea98a0283d --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt @@ -0,0 +1,43 @@ +package io.element.android.libraries.designsystem.atomic.molecules + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom +import io.element.android.libraries.designsystem.theme.placeholderBackground +import io.element.android.libraries.theme.ElementTheme + +@Composable +fun IconTitlePlaceholdersRowMolecule( + iconSize: Dp, + modifier: Modifier = Modifier, + horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, + verticalAlignment: Alignment.Vertical = Alignment.CenterVertically, +) { + Row( + modifier = modifier, + horizontalArrangement = horizontalArrangement, + verticalAlignment = verticalAlignment, + ) { + Box( + modifier = Modifier + .size(iconSize) + .align(Alignment.CenterVertically) + .background(color = ElementTheme.colors.placeholderBackground, shape = CircleShape) + ) + Spacer(modifier = Modifier.width(8.dp)) + PlaceholderAtom(width = 20.dp, height = 7.dp) + Spacer(modifier = Modifier.width(7.dp)) + PlaceholderAtom(width = 45.dp, height = 7.dp) + } +} From 71676c3fafd89f2f47c0fcea961954288f011ff5 Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Fri, 21 Jul 2023 15:37:08 +0200 Subject: [PATCH 077/251] Static images improvements (#933) 1. On devices less than xhdpi request a 1x image from MapTiler (such devices are generally old, slower and with little memory so avoiding to get the 2x image only to have to shrink it later could help). 2. Coerce too big width/height combos within the API limits keeping the aspect ratio (this will allow requests on big horizontal displays to succeed). 3. Don't crash when given weird width/height combos (i.e. zero or negative). 4. Introduce interfaces to hide this whole logic and make it easier for forks to implement their own. Related to: - https://github.com/vector-im/element-meta/issues/1678 --- docs/maps.md | 17 +- .../features/location/api/StaticMapView.kt | 17 +- .../location/api/internal/MapTilerConfig.kt | 30 +++ .../internal/MapTilerStaticMapUrlBuilder.kt | 93 +++++++++ .../MapTilerTileServerStyleUriBuilder.kt | 39 ++++ .../features/location/api/internal/MapUrls.kt | 74 ------- .../api/internal/StaticMapUrlBuilder.kt | 36 ++++ .../api/internal/TileServerStyleUriBuilder.kt | 50 +++++ .../MapTilerStaticMapUrlBuilderTest.kt | 191 ++++++++++++++++++ .../MapTilerTileServerStyleUriBuilderTest.kt | 43 ++++ .../location/impl/show/ShowLocationView.kt | 1 - 11 files changed, 501 insertions(+), 90 deletions(-) create mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerConfig.kt create mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt create mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt delete mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt create mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt create mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt create mode 100644 features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilderTest.kt create mode 100644 features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt diff --git a/docs/maps.md b/docs/maps.md index cc00905986..789d455f22 100644 --- a/docs/maps.md +++ b/docs/maps.md @@ -27,16 +27,21 @@ Place your API key in `local.properties` with the key services.maptiler.apikey=abCd3fGhijK1mN0pQr5t ``` +Optionally you can also place your custom MapTyler style ids for light and dark maps +in the `local.properties` with the keys `services.maptiler.lightMapId` and +`services.maptiler.darkMapId`. If you don't specify these, the default MapTiler "basic-v2" +styles will be used. + ## Making releasable builds with MapTiler To insert the MapTiler API key when building an APK, set the `ELEMENT_ANDROID_MAPTILER_API_KEY` environment variable in your build -environment. +environment. +If you've added custom styles also set the `ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID` +and `ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID` environment variables accordingly. ## Using other map sources or MapTiler styles -If you wish to use an alternative map provider, or custom MapTiler styles, -you can customise the functions in -`features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt`. -We've kept this file small and self contained to minimise the chances of merge -collisions in forks. +If you wish to use an alternative map provider, you can provide your own implementations of +`TileServerStyleUriBuilder` and `StaticMapUrlBuilder` in +`features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/`. diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt index 03aa21b4c8..39b3e8a9a1 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt @@ -29,16 +29,16 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import coil.compose.AsyncImagePainter import coil.compose.rememberAsyncImagePainter import coil.request.ImageRequest import io.element.android.features.location.api.internal.StaticMapPlaceholder +import io.element.android.features.location.api.internal.StaticMapUrlBuilder import io.element.android.features.location.api.internal.centerBottomEdge -import io.element.android.features.location.api.internal.staticMapUrl import io.element.android.libraries.designsystem.preview.DayNightPreviews import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.text.toDp import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.theme.ElementTheme import timber.log.Timber @@ -65,23 +65,22 @@ fun StaticMapView( ) { val context = LocalContext.current var retryHash by remember { mutableStateOf(0) } + val builder = remember { StaticMapUrlBuilder(context) } val painter = rememberAsyncImagePainter( model = if (constraints.isZero) { // Avoid building a URL if any of the size constraints is zero (else it will thrown an exception). null } else { - ImageRequest.Builder(LocalContext.current) + ImageRequest.Builder(context) .data( - staticMapUrl( - context = context, + builder.build( lat = lat, lon = lon, zoom = zoom, darkMode = darkMode, - // Size the map based on DP rather than pixels, as otherwise the features and attribution - // end up being illegibly tiny on high density displays. - width = constraints.maxWidth.toDp().value.toInt(), - height = constraints.maxHeight.toDp().value.toInt(), + width = constraints.maxWidth, + height = constraints.maxHeight, + density = LocalDensity.current.density, ) ) .size(width = constraints.maxWidth, height = constraints.maxHeight) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerConfig.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerConfig.kt new file mode 100644 index 0000000000..9e20d5179b --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerConfig.kt @@ -0,0 +1,30 @@ +/* + * 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.location.api.internal + +import android.content.Context +import io.element.android.features.location.api.R + +internal const val MAPTILER_BASE_URL = "https://api.maptiler.com/maps" + +internal fun Context.mapId(darkMode: Boolean) = when (darkMode) { + true -> getString(R.string.maptiler_dark_map_id) + false -> getString(R.string.maptiler_light_map_id) +} + +internal val Context.apiKey: String + get() = getString(R.string.maptiler_api_key) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt new file mode 100644 index 0000000000..72fcaeb06c --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt @@ -0,0 +1,93 @@ +/* + * 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.location.api.internal + +import android.content.Context +import kotlin.math.roundToInt + +/** + * Builds an URL for MapTiler's Static Maps API. + * + * https://docs.maptiler.com/cloud/api/static-maps/ + */ +internal class MapTilerStaticMapUrlBuilder( + private val apiKey: String, + private val lightMapId: String, + private val darkMapId: String, +) : StaticMapUrlBuilder { + + constructor(context: Context) : this( + apiKey = context.apiKey, + lightMapId = context.mapId(darkMode = false), + darkMapId = context.mapId(darkMode = true), + ) + + override fun build( + lat: Double, + lon: Double, + zoom: Double, + darkMode: Boolean, + width: Int, + height: Int, + density: Float + ): String { + val mapId = if (darkMode) darkMapId else lightMapId + val zoom = zoom.coerceIn(zoomRange) + + // Request @2x density for xhdpi and above (xhdpi == 320dpi == 2x density). + val is2x = density >= 2 + + // Scale requested width/height according to the reported display density. + val (width, height) = coerceWidthAndHeight( + width = (width / density).roundToInt(), + height = (height / density).roundToInt(), + is2x = is2x, + ) + + val scale = if (is2x) "@2x" else "" + + // Since Maptiler doesn't support arbitrary dpi scaling, we stick to 2x sized + // images even on displays with density higher than 2x, thereby yielding an + // image smaller than the available space in pixels. + // The resulting image will have to be scaled to fit the available space in order + // to keep the perceived content size constant at the expense of sharpness. + return "$MAPTILER_BASE_URL/${mapId}/static/${lon},${lat},${zoom}/${width}x${height}${scale}.webp?key=${apiKey}&attribution=bottomleft" + } +} + +private fun coerceWidthAndHeight(width: Int, height: Int, is2x: Boolean): Pair { + if (width <= 0 || height <= 0) { + // This effectively yields an URL which asks for a 0x0 image which will result in an HTTP error, + // but it's better than e.g. asking for a 1x1 image which would be unreadable and increase usage costs. + return 0 to 0 + } + val aspectRatio = width.toDouble() / height.toDouble() + val range = if (is2x) widthHeightRange2x else widthHeightRange + return if (width >= height) { + width.coerceIn(range).let { width -> + width to (width / aspectRatio).roundToInt() + } + } else { + height.coerceIn(range).let { height -> + (height * aspectRatio).roundToInt() to height + } + } +} + +private val widthHeightRange = 1..2048 // API will error if outside 1-2048 range @1x. +private val widthHeightRange2x = 1..1024 // API will error if outside 1-1024 range @2x. +private val zoomRange = 0.0..22.0 // API will error if outside 0-22 range. diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt new file mode 100644 index 0000000000..6972e45330 --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt @@ -0,0 +1,39 @@ +/* + * 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. + */ + +@file:JvmName("TileServerStyleUriBuilderKt") + +package io.element.android.features.location.api.internal + +import android.content.Context + +internal class MapTilerTileServerStyleUriBuilder( + private val apiKey: String, + private val lightMapId: String, + private val darkMapId: String, +) : TileServerStyleUriBuilder { + + constructor(context: Context) : this( + apiKey = context.apiKey, + lightMapId = context.mapId(darkMode = false), + darkMapId = context.mapId(darkMode = true), + ) + + override fun build(darkMode: Boolean): String { + val mapId = if (darkMode) darkMapId else lightMapId + return "${MAPTILER_BASE_URL}/${mapId}/style.json?key=${apiKey}" + } +} diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt deleted file mode 100644 index 2640896a78..0000000000 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt +++ /dev/null @@ -1,74 +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.location.api.internal - -import android.content.Context -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.platform.LocalContext -import io.element.android.features.location.api.R -import io.element.android.libraries.theme.ElementTheme - -/** - * Provides the URL to an image that contains a statically-generated map of the given location. - */ -fun staticMapUrl( - context: Context, - lat: Double, - lon: Double, - zoom: Double, - width: Int, - height: Int, - darkMode: Boolean, -): String { - return "${context.baseUrl(darkMode)}/static/${lon},${lat},${zoom}/${width}x${height}@2x.webp?key=${context.apiKey}&attribution=bottomleft" -} - -/** - * Utility function to remember the tile server URL based on the current theme. - */ -@Composable -fun rememberTileStyleUrl(): String { - val context = LocalContext.current - val darkMode = !ElementTheme.isLightTheme - return remember(darkMode) { - tileStyleUrl( - context = context, - darkMode = darkMode - ) - } -} - -/** - * Provides the URL to a MapLibre style document, used for rendering dynamic maps. - */ -private fun tileStyleUrl( - context: Context, - darkMode: Boolean, -): String { - return "${context.baseUrl(darkMode)}/style.json?key=${context.apiKey}" -} - -private fun Context.baseUrl(darkMode: Boolean) = - "https://api.maptiler.com/maps/" + - if (darkMode) - getString(R.string.maptiler_dark_map_id) - else - getString(R.string.maptiler_light_map_id) - -private val Context.apiKey: String - get() = getString(R.string.maptiler_api_key) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt new file mode 100644 index 0000000000..8c29b5c13d --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt @@ -0,0 +1,36 @@ +/* + * 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.location.api.internal + +import android.content.Context + +/** + * Builds an URL for a 3rd party service provider static maps API. + */ +interface StaticMapUrlBuilder { + fun build( + lat: Double, + lon: Double, + zoom: Double, + darkMode: Boolean, + width: Int, + height: Int, + density: Float, + ): String +} + +fun StaticMapUrlBuilder(context: Context): StaticMapUrlBuilder = MapTilerStaticMapUrlBuilder(context = context) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt new file mode 100644 index 0000000000..ece32cbf5a --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt @@ -0,0 +1,50 @@ +/* + * 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.location.api.internal + +import android.content.Context +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import io.element.android.libraries.theme.ElementTheme + +/** + * Builds a style URI for a MapLibre compatible tile server. + * + * Used for rendering dynamic maps. + */ +interface TileServerStyleUriBuilder { + fun build( + darkMode: Boolean, + ): String +} + +fun TileServerStyleUriBuilder(context: Context): TileServerStyleUriBuilder = MapTilerTileServerStyleUriBuilder(context = context) + +/** + * Provides and remembers a style URI for a MapLibre compatible tile server. + * + * Used for rendering dynamic maps. + */ +@Composable +fun rememberTileStyleUrl(): String { + val context = LocalContext.current + val darkMode = !ElementTheme.isLightTheme + return remember(darkMode) { + TileServerStyleUriBuilder(context).build(darkMode) + } +} diff --git a/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilderTest.kt b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilderTest.kt new file mode 100644 index 0000000000..c359777ee7 --- /dev/null +++ b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilderTest.kt @@ -0,0 +1,191 @@ +/* + * 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.location.api.internal + +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +class MapTilerStaticMapUrlBuilderTest { + + private val builder = MapTilerStaticMapUrlBuilder( + apiKey = "anApiKey", + lightMapId = "aLightMapId", + darkMapId = "aDarkMapId", + ) + + @Test + fun `static map 1x density`() { + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 800, + height = 600, + density = 1f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/800x600.webp?key=anApiKey&attribution=bottomleft") + } + + @Test + fun `static map 1,5x density`() { + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 1200, + height = 900, + density = 1.5f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/800x600.webp?key=anApiKey&attribution=bottomleft") + } + + @Test + fun `static map 2x density`() { + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 1600, + height = 1200, + density = 2f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/800x600@2x.webp?key=anApiKey&attribution=bottomleft") + } + + @Test + fun `static map 3x density`() { + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 2400, + height = 1800, + density = 3f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/800x600@2x.webp?key=anApiKey&attribution=bottomleft") + } + + @Test + fun `too big image is coerced keeping aspect ratio`() { + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 4096, + height = 2048, + density = 1f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/2048x1024.webp?key=anApiKey&attribution=bottomleft") + + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 2048, + height = 4096, + density = 1f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/1024x2048.webp?key=anApiKey&attribution=bottomleft") + + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 4096, + height = 2048, + density = 2f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/1024x512@2x.webp?key=anApiKey&attribution=bottomleft") + + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 2048, + height = 4096, + density = 2f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/512x1024@2x.webp?key=anApiKey&attribution=bottomleft") + + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = Int.MAX_VALUE, + height = Int.MAX_VALUE, + density = 2f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/1024x1024@2x.webp?key=anApiKey&attribution=bottomleft") + } + + @Test + fun `too small image is coerced to 0x0`() { + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 0, + height = 0, + density = 1f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/0x0.webp?key=anApiKey&attribution=bottomleft") + + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 0, + height = 0, + density = 2f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/0x0@2x.webp?key=anApiKey&attribution=bottomleft") + + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = Int.MIN_VALUE, + height = Int.MIN_VALUE, + density = 1f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/0x0.webp?key=anApiKey&attribution=bottomleft") + } +} diff --git a/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt new file mode 100644 index 0000000000..abff83c582 --- /dev/null +++ b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt @@ -0,0 +1,43 @@ +/* + * 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.location.api.internal + +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +class MapTilerTileServerStyleUriBuilderTest { + + private val builder = MapTilerTileServerStyleUriBuilder( + apiKey = "anApiKey", + lightMapId = "aLightMapId", + darkMapId = "aDarkMapId", + ) + + @Test + fun `light map uri`() { + assertThat( + builder.build(darkMode = false) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/style.json?key=anApiKey") + } + + @Test + fun `dark map uri`() { + assertThat( + builder.build(darkMode = true) + ).isEqualTo("https://api.maptiler.com/maps/aDarkMapId/style.json?key=anApiKey") + } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt index 7726bbf9cc..0e114a43b9 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt @@ -40,7 +40,6 @@ import com.mapbox.mapboxsdk.camera.CameraPosition import com.mapbox.mapboxsdk.geometry.LatLng import io.element.android.features.location.api.internal.rememberTileStyleUrl import io.element.android.features.location.impl.MapDefaults -import io.element.android.features.location.impl.send.SendLocationState import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight From 45954b4723a3a76df2845f67533f882e16108883 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 16:04:55 +0200 Subject: [PATCH 078/251] Fix code quality check failures after new rules from #935 has been merged on develop. --- .../api/internal/MapTilerStaticMapUrlBuilder.kt | 14 +++++++------- .../timeline/components/TimelineItemEventRow.kt | 1 - .../components/TimelineItemReactionsLayout.kt | 2 +- .../members/RoomMemberListPresenterTests.kt | 14 +++++--------- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt index 72fcaeb06c..927e890248 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt @@ -46,13 +46,13 @@ internal class MapTilerStaticMapUrlBuilder( density: Float ): String { val mapId = if (darkMode) darkMapId else lightMapId - val zoom = zoom.coerceIn(zoomRange) + val finalZoom = zoom.coerceIn(zoomRange) // Request @2x density for xhdpi and above (xhdpi == 320dpi == 2x density). val is2x = density >= 2 // Scale requested width/height according to the reported display density. - val (width, height) = coerceWidthAndHeight( + val (finalWidth, finalHeight) = coerceWidthAndHeight( width = (width / density).roundToInt(), height = (height / density).roundToInt(), is2x = is2x, @@ -65,7 +65,7 @@ internal class MapTilerStaticMapUrlBuilder( // image smaller than the available space in pixels. // The resulting image will have to be scaled to fit the available space in order // to keep the perceived content size constant at the expense of sharpness. - return "$MAPTILER_BASE_URL/${mapId}/static/${lon},${lat},${zoom}/${width}x${height}${scale}.webp?key=${apiKey}&attribution=bottomleft" + return "$MAPTILER_BASE_URL/${mapId}/static/${lon},${lat},${finalZoom}/${finalWidth}x${finalHeight}${scale}.webp?key=${apiKey}&attribution=bottomleft" } } @@ -78,12 +78,12 @@ private fun coerceWidthAndHeight(width: Int, height: Int, is2x: Boolean): Pair= height) { - width.coerceIn(range).let { width -> - width to (width / aspectRatio).roundToInt() + width.coerceIn(range).let { coercedWidth -> + coercedWidth to (coercedWidth / aspectRatio).roundToInt() } } else { - height.coerceIn(range).let { height -> - (height * aspectRatio).roundToInt() to height + height.coerceIn(range).let { coercedHeight -> + (coercedHeight * aspectRatio).roundToInt() to coercedHeight } } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt index 5143b92f07..a74b37efc5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt @@ -59,7 +59,6 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import androidx.constraintlayout.compose.ConstrainScope import androidx.constraintlayout.compose.ConstraintLayout -import com.google.accompanist.flowlayout.FlowMainAxisAlignment import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.aTimelineItemReactions import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt index 8fe0ab1bef..851389c6bd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt @@ -91,7 +91,7 @@ fun TimelineItemReactionsLayout( lastRow.forEachIndexed { i, placeable -> val horizontalSpacing = if (i == 0) 0 else itemSpacing.toPx().toInt() rowX += placeable.width + horizontalSpacing - if (rowX > (constraints.maxWidth - (buttonsWidth + horizontalSpacing))) { + if (rowX > constraints.maxWidth - (buttonsWidth + horizontalSpacing)) { val lastRowWithButton = lastRow.take(i) + listOf(expandButton, addMoreButton) rows[rows.size - 1] = lastRowWithButton return rows diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt index 5a3141ea09..9de035d017 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/RoomMemberListPresenterTests.kt @@ -52,7 +52,6 @@ class RoomMemberListPresenterTests { Truth.assertThat(initialState.searchQuery).isEmpty() Truth.assertThat(initialState.searchResults).isInstanceOf(SearchBarResultState.NotSearching::class.java) Truth.assertThat(initialState.isSearchActive).isFalse() - val loadedState = awaitItem() Truth.assertThat(loadedState.roomMembers).isInstanceOf(Async.Success::class.java) Truth.assertThat((loadedState.roomMembers as Async.Success).data.invited).isEqualTo(listOf(aVictor(), aWalter())) @@ -66,11 +65,9 @@ class RoomMemberListPresenterTests { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() + skipItems(1) val loadedState = awaitItem() - loadedState.eventSink(RoomMemberListEvents.OnSearchActiveChanged(true)) - val searchActiveState = awaitItem() Truth.assertThat(searchActiveState.isSearchActive).isTrue() } @@ -82,11 +79,11 @@ class RoomMemberListPresenterTests { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() + skipItems(1) val loadedState = awaitItem() loadedState.eventSink(RoomMemberListEvents.OnSearchActiveChanged(true)) val searchActiveState = awaitItem() - loadedState.eventSink(RoomMemberListEvents.UpdateSearchQuery("something")) + searchActiveState.eventSink(RoomMemberListEvents.UpdateSearchQuery("something")) val searchQueryUpdatedState = awaitItem() Truth.assertThat(searchQueryUpdatedState.searchQuery).isEqualTo("something") val searchSearchResultDelivered = awaitItem() @@ -100,18 +97,17 @@ class RoomMemberListPresenterTests { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() + skipItems(1) val loadedState = awaitItem() loadedState.eventSink(RoomMemberListEvents.OnSearchActiveChanged(true)) val searchActiveState = awaitItem() - loadedState.eventSink(RoomMemberListEvents.UpdateSearchQuery("Alice")) + searchActiveState.eventSink(RoomMemberListEvents.UpdateSearchQuery("Alice")) val searchQueryUpdatedState = awaitItem() Truth.assertThat(searchQueryUpdatedState.searchQuery).isEqualTo("Alice") val searchSearchResultDelivered = awaitItem() Truth.assertThat(searchSearchResultDelivered.searchResults).isInstanceOf(SearchBarResultState.Results::class.java) Truth.assertThat((searchSearchResultDelivered.searchResults as SearchBarResultState.Results).results.joined.first().displayName) .isEqualTo("Alice") - } } From 04b3035d6990a59093f08e7f2e08fbdfb0d2d0e2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 16:19:16 +0200 Subject: [PATCH 079/251] Revert change from #941, upload APK to Firebase App Distribution for now. We need a matching and released PlayStore application to be able to upload an AAB, and we do not have that for now. --- .github/workflows/nightly.yml | 13 +++++++------ app/build.gradle.kts | 9 +++++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 8f4cea0a55..a097e0ecba 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -31,9 +31,10 @@ jobs: sed 's/CHANGES\.md/CHANGES_NIGHTLY\.md/' towncrier.toml.bak > towncrier.toml rm towncrier.toml.bak yes n | towncrier build --version nightly - - name: Build and upload Nightly bundle + - name: Build and upload Nightly application run: | - ./gradlew bundleNightly appDistributionUploadNightly $CI_GRADLE_ARG_PROPERTIES +# ./gradlew bundleNightly appDistributionUploadNightly $CI_GRADLE_ARG_PROPERTIES + ./gradlew assembleNightly appDistributionUploadNightly $CI_GRADLE_ARG_PROPERTIES env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} @@ -45,11 +46,11 @@ jobs: - name: Additionally upload Nightly APK to browserstack for testing continue-on-error: true # don't block anything by this upload failing (for now) run: | - ./gradlew assembleNightly $CI_GRADLE_ARG_PROPERTIES +# ./gradlew assembleNightly $CI_GRADLE_ARG_PROPERTIES curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_PASSWORD" -X POST "https://api-cloud.browserstack.com/app-automate/upload" -F "file=@app/build/outputs/apk/nightly/app-universal-nightly.apk" -F "custom_id=element-x-android-nightly" env: - ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} - ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} - ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} +# ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} +# ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} +# ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} BROWSERSTACK_USERNAME: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_USERNAME }} BROWSERSTACK_PASSWORD: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_ACCESS_KEY }} diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 79d6af8307..19bb2ea84b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -124,8 +124,13 @@ android { } firebaseAppDistribution { - artifactType = "AAB" - artifactPath = "$rootDir/app/build/outputs/bundle/nightly/app-nightly.aab" + artifactType = "APK" + // We upload the universal APK to fix this error: + // "App Distribution found more than 1 output file for this variant. + // Please contact firebase-support@google.com for help using APK splits with App Distribution." + artifactPath = "$rootDir/app/build/outputs/apk/nightly/app-universal-nightly.apk" + // artifactType = "AAB" + // artifactPath = "$rootDir/app/build/outputs/bundle/nightly/app-nightly.aab" // This file will be generated by the GitHub action releaseNotesFile = "CHANGES_NIGHTLY.md" groups = "external-testers" From 287b930dcdb3bcd52c022913ad13b78f1f9857ee Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 16:33:02 +0200 Subject: [PATCH 080/251] GitHub does not like comment like this. Just remove them. --- .github/workflows/nightly.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index a097e0ecba..85c9ed1422 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -33,7 +33,6 @@ jobs: yes n | towncrier build --version nightly - name: Build and upload Nightly application run: | -# ./gradlew bundleNightly appDistributionUploadNightly $CI_GRADLE_ARG_PROPERTIES ./gradlew assembleNightly appDistributionUploadNightly $CI_GRADLE_ARG_PROPERTIES env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} @@ -46,11 +45,7 @@ jobs: - name: Additionally upload Nightly APK to browserstack for testing continue-on-error: true # don't block anything by this upload failing (for now) run: | -# ./gradlew assembleNightly $CI_GRADLE_ARG_PROPERTIES curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_PASSWORD" -X POST "https://api-cloud.browserstack.com/app-automate/upload" -F "file=@app/build/outputs/apk/nightly/app-universal-nightly.apk" -F "custom_id=element-x-android-nightly" env: -# ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} -# ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} -# ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} BROWSERSTACK_USERNAME: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_USERNAME }} BROWSERSTACK_PASSWORD: ${{ secrets.ELEMENT_ANDROID_BROWSERSTACK_ACCESS_KEY }} From 43d28c5a3d0ef4e538f09ba419f86df0ce03802a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 18:12:17 +0200 Subject: [PATCH 081/251] No need to sort AnalyticsProvider (it was copied from PushProvider) --- .../android/features/analytics/test/FakeAnalyticsService.kt | 2 +- .../android/services/analytics/api/AnalyticsService.kt | 5 ++++- .../services/analytics/impl/DefaultAnalyticsService.kt | 4 ++-- .../services/analyticsproviders/api/AnalyticsProvider.kt | 5 ----- .../analyticsproviders/posthog/PosthogAnalyticsProvider.kt | 1 - .../services/analyticsproviders/posthog/PosthogConfig.kt | 1 - 6 files changed, 7 insertions(+), 11 deletions(-) diff --git a/features/analytics/test/src/main/kotlin/io/element/android/features/analytics/test/FakeAnalyticsService.kt b/features/analytics/test/src/main/kotlin/io/element/android/features/analytics/test/FakeAnalyticsService.kt index 6e84c58d2a..9e3fe8bb59 100644 --- a/features/analytics/test/src/main/kotlin/io/element/android/features/analytics/test/FakeAnalyticsService.kt +++ b/features/analytics/test/src/main/kotlin/io/element/android/features/analytics/test/FakeAnalyticsService.kt @@ -33,7 +33,7 @@ class FakeAnalyticsService( private val didAskUserConsentFlow = MutableStateFlow(didAskUserConsent) val capturedEvents = mutableListOf() - override fun getAvailableAnalyticsProviders(): List = emptyList() + override fun getAvailableAnalyticsProviders(): Set = emptySet() override fun getUserConsent(): Flow = isEnabledFlow diff --git a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsService.kt b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsService.kt index 9c6fb2d522..309a885ad2 100644 --- a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsService.kt +++ b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsService.kt @@ -22,7 +22,10 @@ import io.element.android.services.analyticsproviders.api.trackers.ErrorTracker import kotlinx.coroutines.flow.Flow interface AnalyticsService: AnalyticsTracker, ErrorTracker { - fun getAvailableAnalyticsProviders(): List + /** + * Get the available analytics providers. + */ + fun getAvailableAnalyticsProviders(): Set /** * Return a Flow of Boolean, true if the user has given their consent. diff --git a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt index 5639f954ac..42acd29b56 100644 --- a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt +++ b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/DefaultAnalyticsService.kt @@ -56,8 +56,8 @@ class DefaultAnalyticsService @Inject constructor( observeSessions() } - override fun getAvailableAnalyticsProviders(): List { - return analyticsProviders.sortedBy { it.index } + override fun getAvailableAnalyticsProviders(): Set { + return analyticsProviders } override fun getUserConsent(): Flow { diff --git a/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/AnalyticsProvider.kt b/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/AnalyticsProvider.kt index 548f47d7ad..807c8d1413 100644 --- a/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/AnalyticsProvider.kt +++ b/services/analyticsproviders/api/src/main/kotlin/io/element/android/services/analyticsproviders/api/AnalyticsProvider.kt @@ -20,11 +20,6 @@ import io.element.android.services.analyticsproviders.api.trackers.AnalyticsTrac import io.element.android.services.analyticsproviders.api.trackers.ErrorTracker interface AnalyticsProvider: AnalyticsTracker, ErrorTracker { - /** - * Allow to sort providers, from lower index to higher index. - */ - val index: Int - /** * User friendly name. */ diff --git a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt index 3b2d392896..fb2e341e1e 100644 --- a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt +++ b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogAnalyticsProvider.kt @@ -35,7 +35,6 @@ import javax.inject.Inject class PosthogAnalyticsProvider @Inject constructor( private val postHogFactory: PostHogFactory, ) : AnalyticsProvider { - override val index = PosthogConfig.index override val name = PosthogConfig.name private var posthog: PostHog? = null diff --git a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogConfig.kt b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogConfig.kt index 877fb7dc9a..96d8659b11 100644 --- a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogConfig.kt +++ b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/PosthogConfig.kt @@ -17,7 +17,6 @@ package io.element.android.services.analyticsproviders.posthog object PosthogConfig { - const val index = 0 const val name = "Posthog" const val postHogHost = "https://posthog.element.dev" const val postHogApiKey = "phc_VtA1L35nw3aeAtHIx1ayrGdzGkss7k1xINeXcoIQzXN" From a30837864e5909bee36f3c9d4b6e19c6d354b038 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 18:14:53 +0200 Subject: [PATCH 082/251] add posthog to dict. --- .idea/dictionaries/shared.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/.idea/dictionaries/shared.xml b/.idea/dictionaries/shared.xml index aafe02a2c8..2fc10f455b 100644 --- a/.idea/dictionaries/shared.xml +++ b/.idea/dictionaries/shared.xml @@ -8,6 +8,7 @@ measurables onboarding placeables + posthog showkase snackbar swipeable From 5a367c64c4ebbcefb3731f6ac0c0dda9358b2669 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 18:27:10 +0200 Subject: [PATCH 083/251] Restore NoopAnalyticsService (not used but can be useful for forks). --- services/analytics/noop/build.gradle.kts | 3 +- .../analytics/noop/NoopAnalyticsService.kt | 48 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 services/analytics/noop/src/main/kotlin/io/element/android/services/analytics/noop/NoopAnalyticsService.kt diff --git a/services/analytics/noop/build.gradle.kts b/services/analytics/noop/build.gradle.kts index a5678f5cb3..000434a05c 100644 --- a/services/analytics/noop/build.gradle.kts +++ b/services/analytics/noop/build.gradle.kts @@ -19,7 +19,7 @@ plugins { } android { - namespace = "io.element.android.services.analytics.impl" + namespace = "io.element.android.services.analytics.noop" } anvil { @@ -28,6 +28,7 @@ anvil { dependencies { implementation(libs.dagger) + implementation(projects.libraries.architecture) implementation(projects.libraries.di) api(projects.services.analytics.api) } diff --git a/services/analytics/noop/src/main/kotlin/io/element/android/services/analytics/noop/NoopAnalyticsService.kt b/services/analytics/noop/src/main/kotlin/io/element/android/services/analytics/noop/NoopAnalyticsService.kt new file mode 100644 index 0000000000..f82e7ff550 --- /dev/null +++ b/services/analytics/noop/src/main/kotlin/io/element/android/services/analytics/noop/NoopAnalyticsService.kt @@ -0,0 +1,48 @@ +/* + * 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.services.analytics.noop + +import com.squareup.anvil.annotations.ContributesBinding +import im.vector.app.features.analytics.itf.VectorAnalyticsEvent +import im.vector.app.features.analytics.itf.VectorAnalyticsScreen +import im.vector.app.features.analytics.plan.UserProperties +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.SingleIn +import io.element.android.services.analytics.api.AnalyticsService +import io.element.android.services.analyticsproviders.api.AnalyticsProvider +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import javax.inject.Inject + +@SingleIn(AppScope::class) +@ContributesBinding(AppScope::class) +class NoopAnalyticsService @Inject constructor( +) : AnalyticsService { + override fun getAvailableAnalyticsProviders(): Set = emptySet() + override fun getUserConsent(): Flow = flowOf(false) + override suspend fun setUserConsent(userConsent: Boolean) = Unit + override fun didAskUserConsent(): Flow = flowOf(true) + override suspend fun setDidAskUserConsent() = Unit + override fun getAnalyticsId(): Flow = flowOf("") + override suspend fun setAnalyticsId(analyticsId: String) = Unit + override suspend fun onSignOut() = Unit + override suspend fun reset() = Unit + override fun capture(event: VectorAnalyticsEvent) = Unit + override fun screen(screen: VectorAnalyticsScreen) = Unit + override fun updateUserProperties(userProperties: UserProperties) = Unit + override fun trackError(throwable: Throwable) = Unit +} From f3e26276b20781d6c565928be666d233fbfafc59 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 18:35:22 +0200 Subject: [PATCH 084/251] Update tag value and make it internal. --- .../analyticsproviders/posthog/log/AnalyticsLoggerTag.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/log/AnalyticsLoggerTag.kt b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/log/AnalyticsLoggerTag.kt index 8e64ca100d..a298bda1c5 100644 --- a/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/log/AnalyticsLoggerTag.kt +++ b/services/analyticsproviders/posthog/src/main/kotlin/io/element/android/services/analyticsproviders/posthog/log/AnalyticsLoggerTag.kt @@ -18,4 +18,4 @@ package io.element.android.services.analyticsproviders.posthog.log import io.element.android.libraries.core.log.logger.LoggerTag -val analyticsTag = LoggerTag("Analytics") +internal val analyticsTag = LoggerTag("Posthog") From c93625080e1bfbff8a2682986daff69d4a78ccb0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 18:58:10 +0200 Subject: [PATCH 085/251] Implement Sentry module as an AnalyticsProvider. --- gradle/libs.versions.toml | 2 +- .../sentry/build.gradle.kts | 35 ++++++++ .../sentry/SentryAnalyticsProvider.kt | 79 +++++++++++++++++++ .../analyticsproviders/sentry/SentryConfig.kt | 24 ++++++ .../sentry/log/AnalyticsLoggerTag.kt | 21 +++++ 5 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 services/analyticsproviders/sentry/build.gradle.kts create mode 100644 services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryAnalyticsProvider.kt create mode 100644 services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryConfig.kt create mode 100644 services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/log/AnalyticsLoggerTag.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7ebf397ac7..fe9a5aa182 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -163,7 +163,7 @@ maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:2.0.0" # Analytics posthog = "com.posthog.android:posthog:2.0.3" -sentry_android = "io.sentry:sentry-android:6.26.0" +sentry = "io.sentry:sentry-android:6.26.0" matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:42b2faa417c1e95f430bf8f6e379adba25ad5ef8" # Di diff --git a/services/analyticsproviders/sentry/build.gradle.kts b/services/analyticsproviders/sentry/build.gradle.kts new file mode 100644 index 0000000000..34c444eb7d --- /dev/null +++ b/services/analyticsproviders/sentry/build.gradle.kts @@ -0,0 +1,35 @@ +/* + * 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. + */ +plugins { + id("io.element.android-library") + alias(libs.plugins.anvil) +} + +android { + namespace = "io.element.android.services.analyticsproviders.sentry" +} + +anvil { + generateDaggerFactories.set(true) +} + +dependencies { + implementation(libs.dagger) + implementation(libs.sentry) + implementation(projects.libraries.core) + implementation(projects.libraries.di) + implementation(projects.services.analyticsproviders.api) +} diff --git a/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryAnalyticsProvider.kt b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryAnalyticsProvider.kt new file mode 100644 index 0000000000..6bc4df426f --- /dev/null +++ b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryAnalyticsProvider.kt @@ -0,0 +1,79 @@ +/* + * 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.services.analyticsproviders.sentry + +import android.content.Context +import com.squareup.anvil.annotations.ContributesMultibinding +import im.vector.app.features.analytics.itf.VectorAnalyticsEvent +import im.vector.app.features.analytics.itf.VectorAnalyticsScreen +import im.vector.app.features.analytics.plan.UserProperties +import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.core.meta.BuildType +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.ApplicationContext +import io.element.android.services.analyticsproviders.api.AnalyticsProvider +import io.element.android.services.analyticsproviders.sentry.log.analyticsTag +import io.sentry.Sentry +import io.sentry.SentryOptions +import io.sentry.android.core.SentryAndroid +import timber.log.Timber +import javax.inject.Inject + +@ContributesMultibinding(AppScope::class) +class SentryAnalyticsProvider @Inject constructor( + @ApplicationContext private val context: Context, + private val buildMeta: BuildMeta, +) : AnalyticsProvider { + override val name = SentryConfig.name + + override fun init() { + Timber.tag(analyticsTag.value).d("Initializing Sentry") + if (Sentry.isEnabled()) return + SentryAndroid.init(context) { options -> + options.dsn = SentryConfig.dns + options.beforeSend = SentryOptions.BeforeSendCallback { event, _ -> event } + options.tracesSampleRate = 1.0 + options.isEnableUserInteractionTracing = true + options.environment = buildMeta.buildType.toSentryEnv() + options.diagnosticLevel + } + } + + override fun stop() { + Timber.tag(analyticsTag.value).d("Stopping Sentry") + Sentry.close() + } + + override fun capture(event: VectorAnalyticsEvent) { + } + + override fun screen(screen: VectorAnalyticsScreen) { + } + + override fun updateUserProperties(userProperties: UserProperties) { + } + + override fun trackError(throwable: Throwable) { + Sentry.captureException(throwable) + } +} + +private fun BuildType.toSentryEnv() = when (this) { + BuildType.RELEASE -> SentryConfig.envRelease + BuildType.NIGHTLY, + BuildType.DEBUG -> SentryConfig.envDebug +} diff --git a/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryConfig.kt b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryConfig.kt new file mode 100644 index 0000000000..9d85290bf6 --- /dev/null +++ b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryConfig.kt @@ -0,0 +1,24 @@ +/* + * 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.services.analyticsproviders.sentry + +object SentryConfig { + const val name = "Sentry" + const val dns = "https://f6acc9cfc2024641b28c87ad95e73e66@sentry.tools.element.io/49" + const val envDebug = "DEBUG" + const val envRelease = "RELEASE" +} diff --git a/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/log/AnalyticsLoggerTag.kt b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/log/AnalyticsLoggerTag.kt new file mode 100644 index 0000000000..f792009ee4 --- /dev/null +++ b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/log/AnalyticsLoggerTag.kt @@ -0,0 +1,21 @@ +/* + * 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.services.analyticsproviders.sentry.log + +import io.element.android.libraries.core.log.logger.LoggerTag + +internal val analyticsTag = LoggerTag("Sentry") From c5c130ab671736c7244db8672286c612191f0ca2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 19:19:17 +0200 Subject: [PATCH 086/251] Ensure the application react correctly if analytics is disabled at compilation time. --- features/preferences/impl/build.gradle.kts | 1 + .../impl/root/PreferencesRootPresenter.kt | 4 ++++ .../preferences/impl/root/PreferencesRootState.kt | 1 + .../impl/root/PreferencesRootStateProvider.kt | 1 + .../preferences/impl/root/PreferencesRootView.kt | 12 +++++++----- .../impl/root/PreferencesRootPresenterTest.kt | 3 +++ .../main/kotlin/extension/DependencyHandleScope.kt | 2 ++ 7 files changed, 19 insertions(+), 5 deletions(-) diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts index f183f7f1fa..5ca0c719fe 100644 --- a/features/preferences/impl/build.gradle.kts +++ b/features/preferences/impl/build.gradle.kts @@ -47,6 +47,7 @@ dependencies { implementation(projects.features.ftue.api) implementation(projects.libraries.matrixui) implementation(projects.features.logout.api) + implementation(projects.services.analytics.api) implementation(projects.services.toolbox.api) implementation(libs.datetime) implementation(libs.accompanist.placeholder) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt index 66ff62ee2b..0cd2e7f7db 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt @@ -35,6 +35,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.getCurrentUser import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus +import io.element.android.services.analytics.api.AnalyticsService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import javax.inject.Inject @@ -43,6 +44,7 @@ class PreferencesRootPresenter @Inject constructor( private val logoutPresenter: LogoutPreferencePresenter, private val matrixClient: MatrixClient, private val sessionVerificationService: SessionVerificationService, + private val analyticsService: AnalyticsService, private val buildType: BuildType, private val versionFormatter: VersionFormatter, private val snackbarDispatcher: SnackbarDispatcher, @@ -58,6 +60,7 @@ class PreferencesRootPresenter @Inject constructor( } val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() + val hasAnalyticsProviders = remember { analyticsService.getAvailableAnalyticsProviders().isNotEmpty() } // Session verification status (unknown, not verified, verified) val sessionVerifiedStatus by sessionVerificationService.sessionVerifiedStatus.collectAsState() @@ -72,6 +75,7 @@ class PreferencesRootPresenter @Inject constructor( myUser = matrixUser.value, version = versionFormatter.get(), showCompleteVerification = sessionIsNotVerified, + showAnalyticsSettings = hasAnalyticsProviders, showDeveloperSettings = showDeveloperSettings, snackbarMessage = snackbarMessage, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt index 2b0963c53c..540c470815 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt @@ -25,6 +25,7 @@ data class PreferencesRootState( val myUser: MatrixUser?, val version: String, val showCompleteVerification: Boolean, + val showAnalyticsSettings: Boolean, val showDeveloperSettings: Boolean, val snackbarMessage: SnackbarMessage?, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt index 9dbd54ffff..e8c148267f 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt @@ -25,6 +25,7 @@ fun aPreferencesRootState() = PreferencesRootState( myUser = null, version = "Version 1.1 (1)", showCompleteVerification = true, + showAnalyticsSettings = true, showDeveloperSettings = true, snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete), ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt index 01d790f8b9..90eade31ab 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt @@ -82,11 +82,13 @@ fun PreferencesRootView( ) Divider() } - PreferenceText( - title = stringResource(id = CommonStrings.common_analytics), - icon = Icons.Outlined.InsertChart, - onClick = onOpenAnalytics, - ) + if (state.showAnalyticsSettings) { + PreferenceText( + title = stringResource(id = CommonStrings.common_analytics), + icon = Icons.Outlined.InsertChart, + onClick = onOpenAnalytics, + ) + } PreferenceText( title = stringResource(id = CommonStrings.action_report_bug), icon = Icons.Outlined.BugReport, diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt index 91bb0c12f3..580426fcfa 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt @@ -20,6 +20,7 @@ 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.analytics.test.FakeAnalyticsService import io.element.android.features.logout.impl.DefaultLogoutPreferencePresenter import io.element.android.libraries.architecture.Async import io.element.android.libraries.core.meta.BuildType @@ -41,6 +42,7 @@ class PreferencesRootPresenterTest { logoutPresenter, matrixClient, FakeSessionVerificationService(), + FakeAnalyticsService(), BuildType.DEBUG, FakeVersionFormatter(), SnackbarDispatcher(), @@ -61,6 +63,7 @@ class PreferencesRootPresenterTest { ) ) assertThat(loadedState.showDeveloperSettings).isEqualTo(true) + assertThat(loadedState.showAnalyticsSettings).isEqualTo(false) } } } diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt index 88f499b993..cc6526a3e8 100644 --- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt +++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt @@ -103,6 +103,8 @@ fun DependencyHandlerScope.allLibrariesImpl() { } fun DependencyHandlerScope.allServicesImpl() { + // For analytics configuration, either use noop, or use the impl, with at least one analyticsproviders implementation + // implementation(project(":services:analytics:noop")) implementation(project(":services:analytics:impl")) implementation(project(":services:analyticsproviders:posthog")) implementation(project(":services:apperror:impl")) From a774d96585f954865404170766979667480b2fa9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 19:19:59 +0200 Subject: [PATCH 087/251] Include Sentry module. --- plugins/src/main/kotlin/extension/DependencyHandleScope.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt index cc6526a3e8..fb082e27a7 100644 --- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt +++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt @@ -107,6 +107,8 @@ fun DependencyHandlerScope.allServicesImpl() { // implementation(project(":services:analytics:noop")) implementation(project(":services:analytics:impl")) implementation(project(":services:analyticsproviders:posthog")) + implementation(project(":services:analyticsproviders:sentry")) + implementation(project(":services:apperror:impl")) implementation(project(":services:appnavstate:impl")) implementation(project(":services:toolbox:impl")) From 3e7234f3af2c6d5ab0b2642d1cd400cc2a1a455a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 19:28:52 +0200 Subject: [PATCH 088/251] Disable Sentry auto-init --- .../sentry/src/main/AndroidManifest.xml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 services/analyticsproviders/sentry/src/main/AndroidManifest.xml diff --git a/services/analyticsproviders/sentry/src/main/AndroidManifest.xml b/services/analyticsproviders/sentry/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..079912fc00 --- /dev/null +++ b/services/analyticsproviders/sentry/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + From 6c9a36f56f33cd871a4cdd1f6444d840a1d5ddfd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 20:55:04 +0200 Subject: [PATCH 089/251] Add a way to crash this app on demand. Useful to test Crash detection, and analytics report, etc. --- .../preferences/impl/developer/DeveloperSettingsView.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt index 1ead19154d..82b477fcc9 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt @@ -56,6 +56,12 @@ fun DeveloperSettingsView( RageshakePreferencesView( state = state.rageshakeState, ) + PreferenceCategory(title = "Crash", showDivider = false) { + PreferenceText( + title = "Crash the app 💥!,", + onClick = { error("This crash is a test.") } + ) + } val cache = state.cacheSize PreferenceCategory(title = "Cache", showDivider = false) { PreferenceText( From a0dbba58b346172db8fc675cb166e265c5a8b29c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Jul 2023 21:40:08 +0200 Subject: [PATCH 090/251] Fix typo --- .../preferences/impl/developer/DeveloperSettingsView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt index 82b477fcc9..7c5b5d91c8 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt @@ -58,7 +58,7 @@ fun DeveloperSettingsView( ) PreferenceCategory(title = "Crash", showDivider = false) { PreferenceText( - title = "Crash the app 💥!,", + title = "Crash the app 💥", onClick = { error("This crash is a test.") } ) } From 8fd9e332985ecabc28bd8a045e6820b595afc7b2 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 21 Jul 2023 19:53:55 +0000 Subject: [PATCH 091/251] Update screenshots --- ...eloperSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...eloperSettingsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...loperSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...loperSettingsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 4fb8f691f4..597ebdcb61 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb77df9e072715ed947537e4474d504b5b5d533bfe9cd888362a421fea5b53b5 -size 44068 +oid sha256:6c0a16524e1017274eeb0f07a50a47c6351af9a901355b54cccbc9ec799e79f4 +size 45174 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 4fb8f691f4..597ebdcb61 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb77df9e072715ed947537e4474d504b5b5d533bfe9cd888362a421fea5b53b5 -size 44068 +oid sha256:6c0a16524e1017274eeb0f07a50a47c6351af9a901355b54cccbc9ec799e79f4 +size 45174 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index ff0fc9708e..e1ba6b44d4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:004a25de04aac1ca9cbbad77581e0b52a6f8fba30aa2e64c03a791c54b297d5a -size 48875 +oid sha256:abb7854a1e47764a907434cdf32e138b95c947bab682da485eea86f9726cf3b9 +size 49946 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index ff0fc9708e..e1ba6b44d4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:004a25de04aac1ca9cbbad77581e0b52a6f8fba30aa2e64c03a791c54b297d5a -size 48875 +oid sha256:abb7854a1e47764a907434cdf32e138b95c947bab682da485eea86f9726cf3b9 +size 49946 From e196d951f830a5dfdf6eafe069302c10c354f4ba Mon Sep 17 00:00:00 2001 From: bmarty Date: Mon, 24 Jul 2023 00:10:09 +0000 Subject: [PATCH 092/251] Sync Strings from Localazy --- .../impl/src/main/res/values-de/translations.xml | 9 +++++++++ .../impl/src/main/res/values-fr/translations.xml | 8 ++++++++ .../impl/src/main/res/values-sk/translations.xml | 9 +++++++++ features/ftue/impl/src/main/res/values/localazy.xml | 2 +- .../impl/src/main/res/values-sk/translations.xml | 4 ++-- .../impl/src/main/res/values-fr/translations.xml | 2 +- .../impl/src/main/res/values-sk/translations.xml | 13 ++++++++++++- .../impl/src/main/res/values-sk/translations.xml | 2 +- .../src/main/res/values-sk/translations.xml | 1 + .../src/main/res/values-sk/translations.xml | 2 ++ 10 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 features/ftue/impl/src/main/res/values-de/translations.xml create mode 100644 features/ftue/impl/src/main/res/values-fr/translations.xml create mode 100644 features/ftue/impl/src/main/res/values-sk/translations.xml diff --git a/features/ftue/impl/src/main/res/values-de/translations.xml b/features/ftue/impl/src/main/res/values-de/translations.xml new file mode 100644 index 0000000000..0f2efc1c57 --- /dev/null +++ b/features/ftue/impl/src/main/res/values-de/translations.xml @@ -0,0 +1,9 @@ + + + "Anrufe, Standortfreigabe, Suche und mehr werden später in diesem Jahr hinzugefügt." + "Der Nachrichtenverlauf für verschlüsselte Räume wird in diesem Update nicht verfügbar sein." + "Wir würden uns freuen, wenn du uns über die Einstellungsseite deine Meinung mitteilst." + "Los geht\'s!" + "Folgendes musst du wissen:" + "Willkommen bei %1$s!" + diff --git a/features/ftue/impl/src/main/res/values-fr/translations.xml b/features/ftue/impl/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..313a6a533f --- /dev/null +++ b/features/ftue/impl/src/main/res/values-fr/translations.xml @@ -0,0 +1,8 @@ + + + "L’historique des messages pour les salons chiffrés ne sera pas disponible dans cette mise à jour." + "Nous serions ravis d’avoir votre avis, n’hésitez pas à nous le partager via la page des paramètres." + "C’est parti !" + "Voici ce qu’il faut savoir :" + "Bienvenue sur %1$s !" + diff --git a/features/ftue/impl/src/main/res/values-sk/translations.xml b/features/ftue/impl/src/main/res/values-sk/translations.xml new file mode 100644 index 0000000000..1c22039a26 --- /dev/null +++ b/features/ftue/impl/src/main/res/values-sk/translations.xml @@ -0,0 +1,9 @@ + + + "Hovory, zdieľanie polohy, vyhľadávanie a ďalšie funkcie pribudnú neskôr v tomto roku." + "História správ pre zašifrované miestnosti nebude v tejto aktualizácii k dispozícii." + "Radi by sme od vás počuli, dajte nám vedieť, čo si myslíte, prostredníctvom stránky nastavení." + "Poďme na to!" + "Tu je to, čo potrebujete vedieť:" + "Vitajte v %1$s!" + diff --git a/features/ftue/impl/src/main/res/values/localazy.xml b/features/ftue/impl/src/main/res/values/localazy.xml index 17999e7158..05cc72034e 100644 --- a/features/ftue/impl/src/main/res/values/localazy.xml +++ b/features/ftue/impl/src/main/res/values/localazy.xml @@ -1,6 +1,6 @@ - "Calls, location sharing, search and more will be added later this year." + "Calls, polls, search and more will be added later this year." "Message history for encrypted rooms won’t be available in this update." "We’d love to hear from you, let us know what you think via the settings page." "Let\'s go!" diff --git a/features/login/impl/src/main/res/values-sk/translations.xml b/features/login/impl/src/main/res/values-sk/translations.xml index 2cdaf596e1..2969e4ecb0 100644 --- a/features/login/impl/src/main/res/values-sk/translations.xml +++ b/features/login/impl/src/main/res/values-sk/translations.xml @@ -6,9 +6,9 @@ "Zadajte hľadaný výraz alebo adresu domény." "Vyhľadať spoločnosť, komunitu alebo súkromný server." "Nájsť poskytovateľa účtu" - "Tu budú žiť vaše konverzácie - podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov." + "Tu budú žiť vaše konverzácie — podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov." "Chystáte sa prihlásiť do %s" - "Tu budú žiť vaše konverzácie - podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov." + "Tu budú žiť vaše konverzácie — podobne ako používate poskytovateľa e-mailových služieb na uchovávanie e-mailov." "Chystáte sa vytvoriť účet na %s" "Matrix.org je otvorená sieť pre bezpečnú a decentralizovanú komunikáciu." "Iný" diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml index 03dcaffac7..276fc30c94 100644 --- a/features/messages/impl/src/main/res/values-fr/translations.xml +++ b/features/messages/impl/src/main/res/values-fr/translations.xml @@ -5,7 +5,7 @@ "%1$d changements dans la conversation" - + "1 de plus" "%1$d de plus" "Appareil photo" diff --git a/features/messages/impl/src/main/res/values-sk/translations.xml b/features/messages/impl/src/main/res/values-sk/translations.xml index 39b7e974be..07e62e9a4d 100644 --- a/features/messages/impl/src/main/res/values-sk/translations.xml +++ b/features/messages/impl/src/main/res/values-sk/translations.xml @@ -6,7 +6,7 @@ "%1$d zmien miestnosti" - + "%1$d ďalší" "%1$d ďalšie" "%1$d ďalších" @@ -22,6 +22,17 @@ "V tomto rozhovore ste sami" "Správa skopírovaná" "Nemáte povolenie uverejňovať príspevky v tejto miestnosti" + "Povoliť vlastné nastavenie" + "Zapnutím tohto nastavenia sa prepíše vaše predvolené nastavenie" + "Upozorniť ma v tejto konverzácii na" + "Môžete to zmeniť vo svojich %1$s." + "všeobecných nastaveniach" + "Predvolené nastavenie" + "Pri načítavaní nastavení oznámení došlo k chybe." + "Nepodarilo sa obnoviť predvolený režim, skúste to prosím znova." + "Nepodarilo sa nastaviť režim, skúste to prosím znova." + "Všetky správy" + "Iba zmienky a kľúčové slová" "Zobraziť menej" "Zobraziť viac" "Odoslať znova" diff --git a/features/rageshake/impl/src/main/res/values-sk/translations.xml b/features/rageshake/impl/src/main/res/values-sk/translations.xml index cb530d1712..51222367a6 100644 --- a/features/rageshake/impl/src/main/res/values-sk/translations.xml +++ b/features/rageshake/impl/src/main/res/values-sk/translations.xml @@ -1,7 +1,7 @@ "Priložiť snímku obrazovky" - "V prípade ďalších otázok ma môžete kontaktovať" + "V prípade ďalších otázok ma môžete kontaktovať." "Kontaktujte ma" "Upraviť snímku obrazovky" "Popíšte prosím chybu. Čo ste urobili? Čo ste očakávali, že sa stane? Čo sa skutočne stalo. Prosím, uveďte čo najviac podrobností." diff --git a/libraries/textcomposer/src/main/res/values-sk/translations.xml b/libraries/textcomposer/src/main/res/values-sk/translations.xml index a5f42a60f8..26ac1df436 100644 --- a/libraries/textcomposer/src/main/res/values-sk/translations.xml +++ b/libraries/textcomposer/src/main/res/values-sk/translations.xml @@ -1,5 +1,6 @@ + "Pridať prílohu" "Prepnúť zoznam odrážok" "Prepnúť blok kódu" "Správa…" diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index 654f3d7cbe..726d1b1505 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -143,6 +143,8 @@ "%1$s nedokázal načítať mapu. Skúste to prosím neskôr." "Načítanie správ zlyhalo" "%1$s nemohol získať prístup k vašej polohe. Skúste to prosím neskôr." + "Ak chcete odoslať polohu, povoľte %1$s prístup k vašej polohe z obrazovky nastavení." + "Ak chcete odoslať polohu, povoľte %1$s prístup k vašej polohe v nasledujúcom dialógovom okne." "Niektoré správy neboli odoslané" "Prepáčte, vyskytla sa chyba" "🔐️ Pripojte sa ku mne na %1$s" From 78fb53645b8be8872f0eaaf93e3a1ce7435c2b7a Mon Sep 17 00:00:00 2001 From: ElementBot Date: Mon, 24 Jul 2023 08:18:21 +0000 Subject: [PATCH 093/251] Update screenshots --- ...ultGroup_WelcomeViewPreview-D-0_1_null,NEXUS_5,1.0,en].png | 4 ++-- ...ultGroup_WelcomeViewPreview-N-0_2_null,NEXUS_5,1.0,en].png | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-D-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-D-0_1_null,NEXUS_5,1.0,en].png index f300f92921..a72f564891 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-D-0_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-D-0_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16de62092834bf803c8165e974f45e14ccfc0128a3e74295a58eef965abc10c5 -size 301336 +oid sha256:1387a1337da70f8e87474aef106110f0dfb55e59f340e0906390e509da1dd0b4 +size 299376 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-N-0_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-N-0_2_null,NEXUS_5,1.0,en].png index 7465768560..a12f3bceee 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-N-0_2_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-N-0_2_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6838e81cc5f2755ff76de7254e2c8bb445b76662d7ba9b4c83443b2c2ed03029 -size 406044 +oid sha256:cdf0215f1ba1f6a89a6204e19b3df7dec1e64d7fd71bdf8c706e1969e11e702d +size 404366 From c0fdb01470087c6256459e78e82d326116bb49f8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Jul 2023 11:02:02 +0200 Subject: [PATCH 094/251] Use dns for element-x-android project. --- .../android/services/analyticsproviders/sentry/SentryConfig.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryConfig.kt b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryConfig.kt index 9d85290bf6..f2048b59f0 100644 --- a/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryConfig.kt +++ b/services/analyticsproviders/sentry/src/main/kotlin/io/element/android/services/analyticsproviders/sentry/SentryConfig.kt @@ -18,7 +18,7 @@ package io.element.android.services.analyticsproviders.sentry object SentryConfig { const val name = "Sentry" - const val dns = "https://f6acc9cfc2024641b28c87ad95e73e66@sentry.tools.element.io/49" + const val dns = "https://32f7ff6a6e724f90838b7654042b2e81@sentry.tools.element.io/59" const val envDebug = "DEBUG" const val envRelease = "RELEASE" } From ac1d355f65322912d16a3a79b93ac3fe6f0944a8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Jul 2023 12:41:02 +0200 Subject: [PATCH 095/251] Cleanup: there are no androidTest on those modules. --- features/analytics/impl/build.gradle.kts | 2 -- features/createroom/impl/build.gradle.kts | 2 -- features/login/impl/build.gradle.kts | 2 -- features/logout/impl/build.gradle.kts | 2 -- features/messages/impl/build.gradle.kts | 1 - features/onboarding/impl/build.gradle.kts | 2 -- features/preferences/impl/build.gradle.kts | 2 -- features/rageshake/impl/build.gradle.kts | 2 -- features/roomlist/impl/build.gradle.kts | 2 -- libraries/permissions/impl/build.gradle.kts | 2 -- tests/uitests/build.gradle.kts | 1 - 11 files changed, 20 deletions(-) diff --git a/features/analytics/impl/build.gradle.kts b/features/analytics/impl/build.gradle.kts index 3bf58ab636..8356bb38bb 100644 --- a/features/analytics/impl/build.gradle.kts +++ b/features/analytics/impl/build.gradle.kts @@ -52,6 +52,4 @@ dependencies { testImplementation(projects.libraries.matrix.test) testImplementation(projects.features.analytics.test) testImplementation(projects.features.analytics.impl) - - androidTestImplementation(libs.test.junitext) } diff --git a/features/createroom/impl/build.gradle.kts b/features/createroom/impl/build.gradle.kts index 8bb343c10a..fe2970dd80 100644 --- a/features/createroom/impl/build.gradle.kts +++ b/features/createroom/impl/build.gradle.kts @@ -66,7 +66,5 @@ dependencies { testImplementation(projects.libraries.mediaupload.test) testImplementation(projects.libraries.usersearch.test) - androidTestImplementation(libs.test.junitext) - ksp(libs.showkase.processor) } diff --git a/features/login/impl/build.gradle.kts b/features/login/impl/build.gradle.kts index f9bbb390e6..4a4c6756aa 100644 --- a/features/login/impl/build.gradle.kts +++ b/features/login/impl/build.gradle.kts @@ -61,6 +61,4 @@ dependencies { testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) testImplementation(projects.tests.testutils) - - androidTestImplementation(libs.test.junitext) } diff --git a/features/logout/impl/build.gradle.kts b/features/logout/impl/build.gradle.kts index 88d8282875..464695e169 100644 --- a/features/logout/impl/build.gradle.kts +++ b/features/logout/impl/build.gradle.kts @@ -48,6 +48,4 @@ dependencies { testImplementation(libs.test.truth) testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) - - androidTestImplementation(libs.test.junitext) } diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index 7488820f7d..b5edaec78e 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -78,6 +78,5 @@ dependencies { testImplementation(projects.libraries.mediapickers.test) testImplementation(libs.test.mockk) - androidTestImplementation(libs.test.junitext) ksp(libs.showkase.processor) } diff --git a/features/onboarding/impl/build.gradle.kts b/features/onboarding/impl/build.gradle.kts index 0952835d46..0f97a0ffeb 100644 --- a/features/onboarding/impl/build.gradle.kts +++ b/features/onboarding/impl/build.gradle.kts @@ -48,6 +48,4 @@ dependencies { testImplementation(libs.test.truth) testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) - - androidTestImplementation(libs.test.junitext) } diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts index f183f7f1fa..60b928e0bb 100644 --- a/features/preferences/impl/build.gradle.kts +++ b/features/preferences/impl/build.gradle.kts @@ -68,6 +68,4 @@ dependencies { testImplementation(projects.features.analytics.test) testImplementation(projects.features.analytics.impl) testImplementation(projects.tests.testutils) - - androidTestImplementation(libs.test.junitext) } diff --git a/features/rageshake/impl/build.gradle.kts b/features/rageshake/impl/build.gradle.kts index 137a3bd070..3283e3f37a 100644 --- a/features/rageshake/impl/build.gradle.kts +++ b/features/rageshake/impl/build.gradle.kts @@ -56,6 +56,4 @@ dependencies { testImplementation(libs.test.mockk) testImplementation(projects.libraries.matrix.test) testImplementation(projects.features.rageshake.test) - - androidTestImplementation(libs.test.junitext) } diff --git a/features/roomlist/impl/build.gradle.kts b/features/roomlist/impl/build.gradle.kts index 302e54fe09..f5a08ba860 100644 --- a/features/roomlist/impl/build.gradle.kts +++ b/features/roomlist/impl/build.gradle.kts @@ -71,6 +71,4 @@ dependencies { testImplementation(projects.features.networkmonitor.test) testImplementation(projects.tests.testutils) testImplementation(projects.features.leaveroom.fake) - - androidTestImplementation(libs.test.junitext) } diff --git a/libraries/permissions/impl/build.gradle.kts b/libraries/permissions/impl/build.gradle.kts index 31a4e4bbb1..9808986f8f 100644 --- a/libraries/permissions/impl/build.gradle.kts +++ b/libraries/permissions/impl/build.gradle.kts @@ -57,7 +57,5 @@ dependencies { testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) - androidTestImplementation(libs.test.junitext) - ksp(libs.showkase.processor) } diff --git a/tests/uitests/build.gradle.kts b/tests/uitests/build.gradle.kts index 729899c4f8..1881822691 100644 --- a/tests/uitests/build.gradle.kts +++ b/tests/uitests/build.gradle.kts @@ -42,7 +42,6 @@ dependencies { testImplementation(libs.test.junit) testImplementation(libs.test.parameter.injector) testImplementation(projects.libraries.designsystem) - androidTestImplementation(libs.test.junitext) ksp(libs.showkase.processor) kspTest(libs.showkase.processor) From 3e4ff90e99afc319a65166b600cec63b9f7bec3d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Jul 2023 12:44:50 +0200 Subject: [PATCH 096/251] Ignore this file. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 70599626ce..0b61f0aaaf 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ captures/ # IntelliJ *.iml .idea/.name +.idea/androidTestResultsUserPreferences.xml .idea/assetWizardSettings.xml .idea/compiler.xml .idea/deploymentTargetDropDown.xml From 3457a764466e96554a4cea9c3040399b54477458 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Jul 2023 15:01:48 +0200 Subject: [PATCH 097/251] getOrPut is not thread safe, so ensure that no multiple instance will be created per data store (#950) --- .../pushstore/impl/DefaultUserPushStoreFactory.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactory.kt b/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactory.kt index a84fe2ea69..16100fd858 100644 --- a/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactory.kt +++ b/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactory.kt @@ -41,11 +41,13 @@ class DefaultUserPushStoreFactory @Inject constructor( // We can have only one class accessing a single data store, so keep a cache of them. private val cache = mutableMapOf() override fun create(userId: SessionId): UserPushStore { - return cache.getOrPut(userId) { - UserPushStoreDataStore( - context = context, - userId = userId - ) + return synchronized(cache) { + cache.getOrPut(userId) { + UserPushStoreDataStore( + context = context, + userId = userId + ) + } } } From 754b4647eea40e2b51657cdbab200c5d2485e2d7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Jul 2023 15:28:36 +0200 Subject: [PATCH 098/251] Add a test to cover fix of #950 --- libraries/pushstore/impl/build.gradle.kts | 14 +++++ .../impl/DefaultUserPushStoreFactoryTest.kt | 56 +++++++++++++++++++ .../session-storage/test/build.gradle.kts | 26 +++++++++ .../test/observer/NoOpSessionObserver.kt | 25 +++++++++ 4 files changed, 121 insertions(+) create mode 100644 libraries/pushstore/impl/src/androidTest/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactoryTest.kt create mode 100644 libraries/session-storage/test/build.gradle.kts create mode 100644 libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/NoOpSessionObserver.kt diff --git a/libraries/pushstore/impl/build.gradle.kts b/libraries/pushstore/impl/build.gradle.kts index dca5c82a4d..5946e77694 100644 --- a/libraries/pushstore/impl/build.gradle.kts +++ b/libraries/pushstore/impl/build.gradle.kts @@ -20,6 +20,11 @@ plugins { android { namespace = "io.element.android.libraries.push.pushstore.impl" + + defaultConfig { + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + testInstrumentationRunnerArguments["clearPackageData"] = "true" + } } anvil { @@ -43,4 +48,13 @@ dependencies { testImplementation(libs.coroutines.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.services.appnavstate.test) + + androidTestImplementation(libs.coroutines.test) + androidTestImplementation(libs.test.core) + androidTestImplementation(libs.test.junit) + androidTestImplementation(libs.test.truth) + androidTestImplementation(libs.test.runner) + androidTestImplementation(projects.libraries.sessionStorage.test) + + coreLibraryDesugaring(libs.android.desugar) } diff --git a/libraries/pushstore/impl/src/androidTest/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactoryTest.kt b/libraries/pushstore/impl/src/androidTest/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactoryTest.kt new file mode 100644 index 0000000000..c87c772ddf --- /dev/null +++ b/libraries/pushstore/impl/src/androidTest/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactoryTest.kt @@ -0,0 +1,56 @@ +/* + * 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.libraries.pushstore.impl + +import androidx.test.platform.app.InstrumentationRegistry +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.pushstore.api.UserPushStore +import io.element.android.libraries.sessionstorage.test.observer.NoOpSessionObserver +import kotlinx.coroutines.runBlocking +import org.junit.Test +import kotlin.concurrent.thread + +/** + * Note: to clear the emulator, invoke: + * adb uninstall io.element.android.libraries.push.pushstore.impl.test + */ +class DefaultUserPushStoreFactoryTest { + + /** + * Ensure that creating UserPushStore is thread safe. + */ + @Test + fun testParallelCreation() { + val context = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext + val sessionId = SessionId("@alice:server.org") + val userPushStoreFactory = DefaultUserPushStoreFactory(context, NoOpSessionObserver()) + var userPushStore1: UserPushStore? = null + val thread1 = thread { + userPushStore1 = userPushStoreFactory.create(sessionId) + } + var userPushStore2: UserPushStore? = null + val thread2 = thread { + userPushStore2 = userPushStoreFactory.create(sessionId) + } + thread1.join() + thread2.join() + runBlocking { + userPushStore1!!.areNotificationEnabledForDevice() + userPushStore2!!.areNotificationEnabledForDevice() + } + } +} diff --git a/libraries/session-storage/test/build.gradle.kts b/libraries/session-storage/test/build.gradle.kts new file mode 100644 index 0000000000..0c8de84669 --- /dev/null +++ b/libraries/session-storage/test/build.gradle.kts @@ -0,0 +1,26 @@ +/* + * 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. + */ +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.libraries.sessionstorage.test" +} + +dependencies { + implementation(projects.libraries.sessionStorage.api) +} diff --git a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/NoOpSessionObserver.kt b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/NoOpSessionObserver.kt new file mode 100644 index 0000000000..03487a3701 --- /dev/null +++ b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/observer/NoOpSessionObserver.kt @@ -0,0 +1,25 @@ +/* + * 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.libraries.sessionstorage.test.observer + +import io.element.android.libraries.sessionstorage.api.observer.SessionListener +import io.element.android.libraries.sessionstorage.api.observer.SessionObserver + +class NoOpSessionObserver : SessionObserver { + override fun addListener(listener: SessionListener) = Unit + override fun removeListener(listener: SessionListener) = Unit +} From a3f28a442efb5b2f184af4bc986e25518994768f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Jul 2023 15:36:55 +0200 Subject: [PATCH 099/251] Fix regression from 061ac9bce3e4562a49d1d5998dbc8964b259fe70, composition log was not correct. --- .../android/libraries/designsystem/utils/LogCompositions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt index f6edd1a8fb..dcbef866fe 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt @@ -30,7 +30,7 @@ fun LogCompositions(tag: String, msg: String) { if (BuildConfig.DEBUG) { val ref = remember { Ref(0) } SideEffect { ref.value++ } - Timber.d(tag, "Compositions: $msg ${ref.value}") + Timber.tag(tag).d("Compositions: $msg ${ref.value}") } } From 727ebb5dc043d50e12cf591fddd295044dc9bb86 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Jul 2023 17:15:04 +0200 Subject: [PATCH 100/251] alternateText can't be an empty string. #955 --- .../timeline/components/html/HtmlDocument.kt | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt index 82d73cc1a0..ecb81a4367 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/html/HtmlDocument.kt @@ -544,17 +544,27 @@ private fun AnnotatedString.Builder.appendLink(link: Element) { pop() } is PermalinkData.RoomEmailInviteLink -> { - appendInlineContent(CHIP_ID, link.ownText()) + safeAppendInlineContent(CHIP_ID, link.ownText()) } is PermalinkData.RoomLink -> { - appendInlineContent(CHIP_ID, link.ownText()) + safeAppendInlineContent(CHIP_ID, link.ownText()) } is PermalinkData.UserLink -> { - appendInlineContent(CHIP_ID, link.ownText()) + safeAppendInlineContent(CHIP_ID, link.ownText()) } } } +fun AnnotatedString.Builder.safeAppendInlineContent(chipId: String, ownText: String) { + if (ownText.isEmpty()) { + // alternateText cannot be empty and default parameter value is private, + // so just omit the second param here. + appendInlineContent(chipId) + } else { + appendInlineContent(chipId, ownText) + } +} + @Composable private fun HtmlText( text: AnnotatedString, From 0ef49ca6cab2a4bc969bad2b83f16200a91de1a6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Jul 2023 18:04:21 +0200 Subject: [PATCH 101/251] Use an AtomicBoolean instead of a MutableStateFlow to atomically init the RustMatrixRoom. Should improve #951. --- .../matrix/impl/room/RustMatrixRoom.kt | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 16434aa3c8..6635e7ecea 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -65,6 +65,7 @@ import org.matrix.rustcomponents.sdk.genTransactionId import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown import timber.log.Timber import java.io.File +import java.util.concurrent.atomic.AtomicBoolean @OptIn(ExperimentalCoroutinesApi::class) class RustMatrixRoom( @@ -88,7 +89,7 @@ class RustMatrixRoom( private val roomCoroutineScope = sessionCoroutineScope.childScope(coroutineDispatchers.main, "RoomScope-$roomId") private val _membersStateFlow = MutableStateFlow(MatrixRoomMembersState.Unknown) - private val isInit = MutableStateFlow(false) + private val isInit = AtomicBoolean(false) private val _syncUpdateFlow = MutableStateFlow(0L) private val _timeline by lazy { RustMatrixTimeline( @@ -107,41 +108,42 @@ class RustMatrixRoom( override val timeline: MatrixTimeline = _timeline override fun open(): Result { - if (isInit.value) return Result.failure(IllegalStateException("Listener already registered")) - val settings = RoomSubscription( - requiredState = listOf( - RequiredState(key = EventType.STATE_ROOM_CANONICAL_ALIAS, value = ""), - RequiredState(key = EventType.STATE_ROOM_TOPIC, value = ""), - RequiredState(key = EventType.STATE_ROOM_JOIN_RULES, value = ""), - RequiredState(key = EventType.STATE_ROOM_POWER_LEVELS, value = ""), - ), - timelineLimit = null - ) - roomListItem.subscribe(settings) - roomCoroutineScope.launch(roomDispatcher) { - innerRoom.timelineDiffFlow { initialList -> - _timeline.postItems(initialList) - }.onEach { diff -> - if (diff.eventOrigin() == EventItemOrigin.SYNC) { - _syncUpdateFlow.value = systemClock.epochMillis() - } - _timeline.postDiff(diff) - }.launchIn(this) - - innerRoom.backPaginationStatusFlow() - .onEach { - _timeline.postPaginationStatus(it) + return if (isInit.getAndSet(true)) { + Result.failure(IllegalStateException("Listener already registered")) + } else { + val settings = RoomSubscription( + requiredState = listOf( + RequiredState(key = EventType.STATE_ROOM_CANONICAL_ALIAS, value = ""), + RequiredState(key = EventType.STATE_ROOM_TOPIC, value = ""), + RequiredState(key = EventType.STATE_ROOM_JOIN_RULES, value = ""), + RequiredState(key = EventType.STATE_ROOM_POWER_LEVELS, value = ""), + ), + timelineLimit = null + ) + roomListItem.subscribe(settings) + roomCoroutineScope.launch(roomDispatcher) { + innerRoom.timelineDiffFlow { initialList -> + _timeline.postItems(initialList) + }.onEach { diff -> + if (diff.eventOrigin() == EventItemOrigin.SYNC) { + _syncUpdateFlow.value = systemClock.epochMillis() + } + _timeline.postDiff(diff) }.launchIn(this) - fetchMembers() + innerRoom.backPaginationStatusFlow() + .onEach { + _timeline.postPaginationStatus(it) + }.launchIn(this) + + fetchMembers() + } + Result.success(Unit) } - isInit.value = true - return Result.success(Unit) } override fun close() { - if (isInit.value) { - isInit.value = false + if (isInit.getAndSet(false)) { roomCoroutineScope.cancel() roomListItem.unsubscribe() innerRoom.destroy() From da9f52129fc8e9036c23edf861f3659c739e2aa7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Jul 2023 21:53:19 +0200 Subject: [PATCH 102/251] Use ConcurrentHashMap to manage synchronization. --- .../pushstore/impl/DefaultUserPushStoreFactory.kt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactory.kt b/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactory.kt index 16100fd858..8c85dca80c 100644 --- a/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactory.kt +++ b/libraries/pushstore/impl/src/main/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactory.kt @@ -26,6 +26,7 @@ import io.element.android.libraries.pushstore.api.UserPushStore import io.element.android.libraries.pushstore.api.UserPushStoreFactory import io.element.android.libraries.sessionstorage.api.observer.SessionListener import io.element.android.libraries.sessionstorage.api.observer.SessionObserver +import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject @SingleIn(AppScope::class) @@ -39,15 +40,13 @@ class DefaultUserPushStoreFactory @Inject constructor( } // We can have only one class accessing a single data store, so keep a cache of them. - private val cache = mutableMapOf() + private val cache = ConcurrentHashMap() override fun create(userId: SessionId): UserPushStore { - return synchronized(cache) { - cache.getOrPut(userId) { - UserPushStoreDataStore( - context = context, - userId = userId - ) - } + return cache.getOrPut(userId) { + UserPushStoreDataStore( + context = context, + userId = userId + ) } } From 015b98d818eab1bce1f92d4e76d3e8989961e9c9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 25 Jul 2023 08:07:48 +0200 Subject: [PATCH 103/251] Update gradle/gradle-build-action action to v2.7.0 (#958) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- .github/workflows/nightlyReports.yml | 2 +- .github/workflows/quality.yml | 2 +- .github/workflows/recordScreenshots.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/tests.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 06684c5eb4..657dc7c772 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,7 +34,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.6.1 + uses: gradle/gradle-build-action@v2.7.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Assemble debug APK diff --git a/.github/workflows/nightlyReports.yml b/.github/workflows/nightlyReports.yml index ce7b763ef1..5c70a3d385 100644 --- a/.github/workflows/nightlyReports.yml +++ b/.github/workflows/nightlyReports.yml @@ -62,7 +62,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.6.1 + uses: gradle/gradle-build-action@v2.7.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Dependency analysis diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 94b8b7ff4e..ecc40760be 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -39,7 +39,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.6.1 + uses: gradle/gradle-build-action@v2.7.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Run code quality check suite diff --git a/.github/workflows/recordScreenshots.yml b/.github/workflows/recordScreenshots.yml index d088b3ad94..54e35bbaff 100644 --- a/.github/workflows/recordScreenshots.yml +++ b/.github/workflows/recordScreenshots.yml @@ -24,7 +24,7 @@ jobs: java-version: '17' # Add gradle cache, this should speed up the process - name: Configure gradle - uses: gradle/gradle-build-action@v2.6.1 + uses: gradle/gradle-build-action@v2.7.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Record screenshots diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ed2d8d4fbd..8ab294bc72 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.6.1 + uses: gradle/gradle-build-action@v2.7.0 - name: Create app bundle env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 04fd393e25..e4f2300b4c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,7 +33,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '17' - name: Configure gradle - uses: gradle/gradle-build-action@v2.6.1 + uses: gradle/gradle-build-action@v2.7.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} From daf23c554114e83df08b90dc85dfd7e3258d0af1 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 25 Jul 2023 12:06:36 +0200 Subject: [PATCH 104/251] Rework some MatrixRoom api and fix rust 'destroyed' crash --- .../android/appnav/room/RoomLoadedFlowNode.kt | 10 ++- .../libraries/matrix/api/room/MatrixRoom.kt | 8 +- .../matrix/impl/room/RustMatrixRoom.kt | 75 +++++-------------- .../impl/timeline/RustMatrixTimeline.kt | 42 +++++++++-- .../matrix/test/room/FakeMatrixRoom.kt | 11 ++- .../android/samples/minimal/RoomListScreen.kt | 1 - 6 files changed, 76 insertions(+), 71 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt index 73a8579b07..24ec9795f7 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt @@ -20,6 +20,7 @@ import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.ui.Modifier +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.lifecycle.subscribe @@ -161,13 +162,16 @@ class RoomLoadedFlowNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { - // Rely on the View Lifecycle instead of the Node Lifecycle, + // Rely on the View Lifecycle in addition to the Node Lifecycle, // because this node enters 'onDestroy' before his children, so it can leads to // using the room in a child node where it's already closed. DisposableEffect(Unit) { - inputs.room.open() + inputs.room.subscribeToSync() onDispose { - inputs.room.close() + inputs.room.unsubscribeFromSync() + if (lifecycle.currentState == Lifecycle.State.DESTROYED) { + inputs.room.destroy() + } } } Children( diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt index be0ff447b3..1445f85228 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt @@ -63,7 +63,11 @@ interface MatrixRoom : Closeable { val timeline: MatrixTimeline - fun open(): Result + fun destroy() + + fun subscribeToSync() + + fun unsubscribeFromSync() suspend fun userDisplayName(userId: UserId): Result @@ -133,6 +137,8 @@ interface MatrixRoom : Closeable { zoomLevel: Int? = null, assetType: AssetType? = null, ): Result + + override fun close() = destroy() } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 6635e7ecea..89e83b58c6 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -40,9 +40,6 @@ import io.element.android.libraries.matrix.impl.core.toProgressWatcher import io.element.android.libraries.matrix.impl.media.map import io.element.android.libraries.matrix.impl.room.location.toInner import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline -import io.element.android.libraries.matrix.impl.timeline.backPaginationStatusFlow -import io.element.android.libraries.matrix.impl.timeline.eventOrigin -import io.element.android.libraries.matrix.impl.timeline.timelineDiffFlow import io.element.android.libraries.sessionstorage.api.SessionData import io.element.android.services.toolbox.api.systemclock.SystemClock import kotlinx.coroutines.CoroutineScope @@ -51,11 +48,7 @@ import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.matrix.rustcomponents.sdk.EventItemOrigin import org.matrix.rustcomponents.sdk.RequiredState import org.matrix.rustcomponents.sdk.Room import org.matrix.rustcomponents.sdk.RoomListItem @@ -65,7 +58,6 @@ import org.matrix.rustcomponents.sdk.genTransactionId import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown import timber.log.Timber import java.io.File -import java.util.concurrent.atomic.AtomicBoolean @OptIn(ExperimentalCoroutinesApi::class) class RustMatrixRoom( @@ -89,7 +81,6 @@ class RustMatrixRoom( private val roomCoroutineScope = sessionCoroutineScope.childScope(coroutineDispatchers.main, "RoomScope-$roomId") private val _membersStateFlow = MutableStateFlow(MatrixRoomMembersState.Unknown) - private val isInit = AtomicBoolean(false) private val _syncUpdateFlow = MutableStateFlow(0L) private val _timeline by lazy { RustMatrixTimeline( @@ -98,6 +89,7 @@ class RustMatrixRoom( roomCoroutineScope = roomCoroutineScope, dispatcher = roomDispatcher, lastLoginTimestamp = sessionData.loginTimestamp, + onNewSyncedEvent = { _syncUpdateFlow.value = systemClock.epochMillis() } ) } @@ -107,48 +99,27 @@ class RustMatrixRoom( override val timeline: MatrixTimeline = _timeline - override fun open(): Result { - return if (isInit.getAndSet(true)) { - Result.failure(IllegalStateException("Listener already registered")) - } else { - val settings = RoomSubscription( - requiredState = listOf( - RequiredState(key = EventType.STATE_ROOM_CANONICAL_ALIAS, value = ""), - RequiredState(key = EventType.STATE_ROOM_TOPIC, value = ""), - RequiredState(key = EventType.STATE_ROOM_JOIN_RULES, value = ""), - RequiredState(key = EventType.STATE_ROOM_POWER_LEVELS, value = ""), - ), - timelineLimit = null - ) - roomListItem.subscribe(settings) - roomCoroutineScope.launch(roomDispatcher) { - innerRoom.timelineDiffFlow { initialList -> - _timeline.postItems(initialList) - }.onEach { diff -> - if (diff.eventOrigin() == EventItemOrigin.SYNC) { - _syncUpdateFlow.value = systemClock.epochMillis() - } - _timeline.postDiff(diff) - }.launchIn(this) - - innerRoom.backPaginationStatusFlow() - .onEach { - _timeline.postPaginationStatus(it) - }.launchIn(this) - - fetchMembers() - } - Result.success(Unit) - } + override fun subscribeToSync() { + val settings = RoomSubscription( + requiredState = listOf( + RequiredState(key = EventType.STATE_ROOM_CANONICAL_ALIAS, value = ""), + RequiredState(key = EventType.STATE_ROOM_TOPIC, value = ""), + RequiredState(key = EventType.STATE_ROOM_JOIN_RULES, value = ""), + RequiredState(key = EventType.STATE_ROOM_POWER_LEVELS, value = ""), + ), + timelineLimit = null + ) + roomListItem.subscribe(settings) } - override fun close() { - if (isInit.getAndSet(false)) { - roomCoroutineScope.cancel() - roomListItem.unsubscribe() - innerRoom.destroy() - roomListItem.destroy() - } + override fun unsubscribeFromSync() { + roomListItem.unsubscribe() + } + + override fun destroy() { + roomCoroutineScope.cancel() + innerRoom.destroy() + roomListItem.destroy() } override val name: String? @@ -365,12 +336,6 @@ class RustMatrixRoom( } } - private suspend fun fetchMembers() = withContext(roomDispatcher) { - runCatching { - innerRoom.fetchMembers() - } - } - override suspend fun reportContent(eventId: EventId, reason: String, blockUserId: UserId?): Result = withContext(roomDispatcher) { runCatching { innerRoom.reportContent(eventId = eventId.value, score = null, reason = reason) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt index e213fb623c..d9b6604170 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt @@ -26,8 +26,8 @@ import io.element.android.libraries.matrix.impl.timeline.item.event.EventMessage import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper -import kotlinx.coroutines.CompletableDeferred import io.element.android.libraries.matrix.impl.timeline.postprocessor.TimelineEncryptedHistoryPostProcessor +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -37,17 +37,21 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.getAndUpdate +import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.sample +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.BackPaginationStatus +import org.matrix.rustcomponents.sdk.EventItemOrigin import org.matrix.rustcomponents.sdk.PaginationOptions import org.matrix.rustcomponents.sdk.Room import org.matrix.rustcomponents.sdk.TimelineDiff import org.matrix.rustcomponents.sdk.TimelineItem import timber.log.Timber -import java.util.concurrent.atomic.AtomicBoolean import java.util.Date +import java.util.concurrent.atomic.AtomicBoolean private const val INITIAL_MAX_SIZE = 50 @@ -57,6 +61,7 @@ class RustMatrixTimeline( private val innerRoom: Room, private val dispatcher: CoroutineDispatcher, private val lastLoginTimestamp: Date?, + private val onNewSyncedEvent: () -> Unit, ) : MatrixTimeline { private val initLatch = CompletableDeferred() @@ -93,13 +98,40 @@ class RustMatrixTimeline( override val paginationState: StateFlow = _paginationState.asStateFlow() + init { + Timber.d("Initialize timeline for room ${matrixRoom.roomId}") + roomCoroutineScope.launch(dispatcher) { + innerRoom.timelineDiffFlow { initialList -> + postItems(initialList) + }.onEach { diff -> + if (diff.eventOrigin() == EventItemOrigin.SYNC) { + onNewSyncedEvent() + } + postDiff(diff) + }.launchIn(this) + + innerRoom.backPaginationStatusFlow() + .onEach { + postPaginationStatus(it) + }.launchIn(this) + + fetchMembers() + } + } + + private suspend fun fetchMembers() = withContext(dispatcher) { + runCatching { + innerRoom.fetchMembers() + } + } + @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) override val timelineItems: Flow> = _timelineItems.sample(50) .mapLatest { items -> encryptedHistoryPostProcessor.process(items) } - internal suspend fun postItems(items: List) { + private suspend fun postItems(items: List) { // Split the initial items in multiple list as there is no pagination in the cached data, so we can post timelineItems asap. items.chunked(INITIAL_MAX_SIZE).reversed().forEach { timelineDiffProcessor.postItems(it) @@ -108,12 +140,12 @@ class RustMatrixTimeline( initLatch.complete(Unit) } - internal suspend fun postDiff(timelineDiff: TimelineDiff) { + private suspend fun postDiff(timelineDiff: TimelineDiff) { initLatch.await() timelineDiffProcessor.postDiff(timelineDiff) } - internal fun postPaginationStatus(status: BackPaginationStatus) { + private fun postPaginationStatus(status: BackPaginationStatus) { _paginationState.getAndUpdate { currentPaginationState -> if (hasEncryptionHistoryBanner()) { return@getAndUpdate currentPaginationState.copy( diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index 7fe7de5b9f..59f6ed57bd 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -100,7 +100,6 @@ class FakeMatrixRoom( private val _sentLocations = mutableListOf() val sentLocations: List = _sentLocations - var invitedUserId: UserId? = null private set @@ -128,9 +127,11 @@ class FakeMatrixRoom( override val timeline: MatrixTimeline = matrixTimeline - override fun open(): Result { - return Result.success(Unit) - } + override fun subscribeToSync() = Unit + + override fun unsubscribeFromSync() = Unit + + override fun destroy() = Unit override suspend fun userDisplayName(userId: UserId): Result = simulateLongTask { userDisplayNameResult @@ -283,8 +284,6 @@ class FakeMatrixRoom( return sendLocationResult } - override fun close() = Unit - fun givenLeaveRoomError(throwable: Throwable?) { this.leaveRoomError = throwable } diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt index f66de878fb..41dbc8bfe2 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt @@ -87,7 +87,6 @@ class RoomListScreen( Singleton.appScope.launch { withContext(coroutineDispatchers.io) { matrixClient.getRoom(roomId)!!.use { room -> - room.open() room.timeline.paginateBackwards(20, 50) } } From 39d4f5bff3cc3d0ec87745929896658193ddb2ca Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Jul 2023 14:24:09 +0200 Subject: [PATCH 105/251] Fix gradle warning: "Project accessors enabled, but root project name not explicitly set for 'plugins'. Checking out the project in different folders will impact the generated code and implicitly the buildscript classpath, breaking caching." --- plugins/settings.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/settings.gradle.kts b/plugins/settings.gradle.kts index defcb6f17b..7e2ce1ea50 100644 --- a/plugins/settings.gradle.kts +++ b/plugins/settings.gradle.kts @@ -14,6 +14,8 @@ * limitations under the License. */ +rootProject.name = "ElementX_plugins" + dependencyResolutionManagement { repositories { mavenCentral() From 9cf74eff63c267433069efc512fdd5f84c050877 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 25 Jul 2023 16:02:33 +0200 Subject: [PATCH 106/251] Clean PR --- .../messages/impl/MessagesStateProvider.kt | 8 ++++---- .../IconTitlePlaceholdersRowMolecule.kt | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index 582cd2e7ab..ce50cc138b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -34,14 +34,14 @@ open class MessagesStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aMessagesState(), - aMessagesState().copy( - roomName = Async.Uninitialized, - roomAvatar = Async.Uninitialized, - ), aMessagesState().copy(hasNetworkConnection = false), aMessagesState().copy(composerState = aMessageComposerState().copy(showAttachmentSourcePicker = true)), aMessagesState().copy(userHasPermissionToSendMessage = false), aMessagesState().copy(showReinvitePrompt = true), + aMessagesState().copy( + roomName = Async.Uninitialized, + roomAvatar = Async.Uninitialized, + ), ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt index ea98a0283d..ec8636c759 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt @@ -1,3 +1,19 @@ +/* + * 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.libraries.designsystem.atomic.molecules import androidx.compose.foundation.background From fa963dcc1190592347083b719efafd49b4bb47ca Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 25 Jul 2023 14:20:05 +0000 Subject: [PATCH 107/251] Update screenshots --- ...tGroup_MessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 3 +++ ...Group_MessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f5df0bc94e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d5f6af4e49ea68ef43a7342910d5f6c72981044ad776ea484adc60fb5578a179 +size 49863 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b7a9808bc0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:26aef777d4601de18948ace8c85d5935eb8e008d07b4f3a09f40a416164bbc44 +size 51602 From b96b0b10f525ab35667660e106bb5625556633c1 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 25 Jul 2023 18:37:32 +0200 Subject: [PATCH 108/251] Turbine: introduce consumeItemsUntilTimeout --- tests/testutils/build.gradle.kts | 1 + .../android/tests/testutils/ReceiveTurbine.kt | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt diff --git a/tests/testutils/build.gradle.kts b/tests/testutils/build.gradle.kts index d7c17c7895..184bbc418a 100644 --- a/tests/testutils/build.gradle.kts +++ b/tests/testutils/build.gradle.kts @@ -30,4 +30,5 @@ dependencies { implementation(libs.test.junit) implementation(libs.coroutines.test) implementation(projects.libraries.core) + implementation(libs.test.turbine) } diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt new file mode 100644 index 0000000000..e3f7b7139c --- /dev/null +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt @@ -0,0 +1,44 @@ +/* + * 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.tests.testutils + +import app.cash.turbine.Event +import app.cash.turbine.ReceiveTurbine +import app.cash.turbine.withTurbineTimeout +import io.element.android.libraries.core.data.tryOrNull +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds + +/** + * Consume all items until timeout is reached waiting for an event. + * The timeout is applied for each event. + * @return the list of consumed items. + */ +suspend fun ReceiveTurbine.consumeItemsUntilTimeout(timeout: Duration = 100.milliseconds): List { + val items = ArrayList() + tryOrNull { + while (true) { + when (val event = withTurbineTimeout(timeout) { awaitEvent() }) { + is Event.Item -> { + items.add(event.value) + } + else -> break + } + } + } + return items +} From bbbee5a6d9fda7e9a6c5daa970679337baa23c63 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 25 Jul 2023 18:37:54 +0200 Subject: [PATCH 109/251] Fix tests --- .../features/messages/impl/MessagesView.kt | 42 +++++++++---------- .../messages/MessagesPresenterTest.kt | 26 ++++-------- 2 files changed, 29 insertions(+), 39 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index d8a8446c8c..c1187fdf7b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -295,27 +295,6 @@ fun MessagesViewTopBar( onRoomDetailsClicked: () -> Unit = {}, onBackPressed: () -> Unit = {}, ) { - @Composable - fun RoomAvatarAndNameRow( - roomName: String, - roomAvatar: AvatarData, - modifier: Modifier = Modifier - ) { - Row( - modifier = modifier, - verticalAlignment = Alignment.CenterVertically - ) { - Avatar(roomAvatar) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = roomName, - style = ElementTheme.typography.fontBodyLgMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - } - } - TopAppBar( modifier = modifier, navigationIcon = { @@ -340,6 +319,27 @@ fun MessagesViewTopBar( ) } +@Composable +fun RoomAvatarAndNameRow( + roomName: String, + roomAvatar: AvatarData, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically + ) { + Avatar(roomAvatar) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = roomName, + style = ElementTheme.typography.fontBodyLgMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } +} + @Composable fun CantSendMessageBanner( modifier: Modifier = Modifier, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index 2ff244621d..aeca219b68 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -65,6 +65,7 @@ import io.element.android.libraries.mediapickers.test.FakePickerProvider import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor import io.element.android.libraries.textcomposer.MessageComposerMode +import io.element.android.tests.testutils.consumeItemsUntilTimeout import io.element.android.tests.testutils.testCoroutineDispatchers import io.mockk.mockk import kotlinx.coroutines.test.TestScope @@ -132,7 +133,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Forward, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -177,7 +177,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent(eventId = null))) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -314,7 +313,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.ReportContent, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -328,10 +326,10 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.Dismiss) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) + } } @@ -342,7 +340,6 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) val initialState = awaitItem() initialState.eventSink.invoke(MessagesEvents.HandleAction(TimelineItemAction.Developer, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -419,9 +416,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() - skipItems(1) + val initialState = consumeItemsUntilTimeout().last() initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) skipItems(1) val loadingState = awaitItem() @@ -448,9 +443,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() - skipItems(1) + val initialState = consumeItemsUntilTimeout().last() initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) skipItems(1) val loadingState = awaitItem() @@ -469,9 +462,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() - skipItems(1) + val initialState = consumeItemsUntilTimeout().last() initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) skipItems(1) val loadingState = awaitItem() @@ -497,9 +488,7 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() - skipItems(1) + val initialState = consumeItemsUntilTimeout().last() initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) skipItems(1) val loadingState = awaitItem() @@ -532,8 +521,9 @@ class MessagesPresenterTest { }.test { // Default value assertThat(awaitItem().userHasPermissionToSendMessage).isTrue() - skipItems(2) + skipItems(1) assertThat(awaitItem().userHasPermissionToSendMessage).isFalse() + cancelAndIgnoreRemainingEvents() } } From ba12fbc9e3275dd1b0ac616cc4166c36c91f2c8a Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 25 Jul 2023 18:41:43 +0200 Subject: [PATCH 110/251] Small change after PR review --- .../android/libraries/matrix/impl/RustMatrixClient.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index c0bdcbb77e..6bfe37045f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -158,10 +158,7 @@ class RustMatrixClient constructor( roomSummaryDataSource.awaitAllRoomsAreLoaded() cachedPairOfRoom = pairOfRoom(roomId) } - return@withContext if (cachedPairOfRoom == null) { - null - } else { - val (roomListItem, fullRoom) = cachedPairOfRoom + cachedPairOfRoom?.let { (roomListItem, fullRoom) -> RustMatrixRoom( sessionId = sessionId, roomListItem = roomListItem, From 4e506667fb3e58d04bbe10e195026e8d87d5bed9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Jul 2023 07:02:07 +0200 Subject: [PATCH 111/251] Update danger/danger-js action to v11.2.7 (#961) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/danger.yml | 2 +- .github/workflows/quality.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml index 223a273b68..3fd17939e0 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -11,7 +11,7 @@ jobs: - run: | npm install --save-dev @babel/plugin-transform-flow-strip-types - name: Danger - uses: danger/danger-js@11.2.6 + uses: danger/danger-js@11.2.7 with: args: "--dangerfile ./tools/danger/dangerfile.js" env: diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index ecc40760be..6c21ddc55a 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -65,7 +65,7 @@ jobs: yarn add danger-plugin-lint-report --dev - name: Danger lint if: always() - uses: danger/danger-js@11.2.6 + uses: danger/danger-js@11.2.7 with: args: "--dangerfile ./tools/danger/dangerfile-lint.js" env: From 17685e8759cedb7ca515374eada6a2bdd2a68302 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 09:45:40 +0200 Subject: [PATCH 112/251] Fix warning: "'setter for config: ConfigurableFileCollection' is deprecated. Setter will be removed in a future release. Use `from` or `setFrom` instead." --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 9272514899..a50b7673e4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -56,7 +56,7 @@ allprojects { // activate all available (even unstable) rules. allRules = true // point to your custom config defining rules to run, overwriting default behavior - config = files("$rootDir/tools/detekt/detekt.yml") + config.from(files("$rootDir/tools/detekt/detekt.yml")) } dependencies { detektPlugins("io.nlopez.compose.rules:detekt:0.1.12") From 996574f2c6b4b22c8f6fd1dcc98e9be97f6675c4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 10:03:02 +0200 Subject: [PATCH 113/251] Enable detekt rule `PreviewPublic` and fix existing issues. --- .../main/kotlin/io/element/android/x/icon/IconPreview.kt | 4 ++-- .../io/element/android/appnav/loggedin/LoggedInView.kt | 2 +- .../io/element/android/appnav/loggedin/SyncStateView.kt | 2 +- .../io/element/android/appnav/room/LoadingRoomNodeView.kt | 4 ++-- .../analytics/api/preferences/AnalyticsPreferencesView.kt | 4 ++-- .../android/features/analytics/impl/AnalyticsOptInView.kt | 4 ++-- .../createroom/impl/components/RoomPrivacyOption.kt | 4 ++-- .../createroom/impl/configureroom/ConfigureRoomView.kt | 4 ++-- .../features/createroom/impl/root/CreateRoomRootView.kt | 4 ++-- .../android/features/location/api/StaticMapView.kt | 2 +- .../location/api/internal/StaticMapPlaceholder.kt | 2 +- .../features/location/impl/send/SendLocationView.kt | 2 +- .../login/impl/accountprovider/AccountProviderView.kt | 4 ++-- .../features/login/impl/changeserver/ChangeServerView.kt | 4 ++-- .../android/features/login/impl/oidc/webview/OidcView.kt | 4 ++-- .../changeaccountprovider/ChangeAccountProviderView.kt | 4 ++-- .../confirmaccountprovider/ConfirmAccountProviderView.kt | 4 ++-- .../searchaccountprovider/SearchAccountProviderView.kt | 4 ++-- .../features/messages/impl/actionlist/ActionListView.kt | 2 +- .../impl/attachments/preview/AttachmentsPreviewView.kt | 2 +- .../features/messages/impl/forward/ForwardMessagesView.kt | 4 ++-- .../messages/impl/media/viewer/MediaViewerView.kt | 2 +- .../features/messages/impl/report/ReportMessageView.kt | 4 ++-- .../features/messages/impl/timeline/TimelineView.kt | 2 +- .../impl/timeline/components/TimelineItemReactionsView.kt | 8 ++++---- .../impl/timeline/components/group/GroupHeaderView.kt | 4 ++-- .../android/features/preferences/impl/about/AboutView.kt | 4 ++-- .../preferences/impl/analytics/AnalyticsSettingsView.kt | 4 ++-- .../preferences/impl/developer/DeveloperSettingsView.kt | 4 ++-- .../features/preferences/impl/root/PreferencesRootView.kt | 4 ++-- .../rageshake/api/preferences/RageshakePreferencesView.kt | 4 ++-- .../features/rageshake/impl/bugreport/BugReportView.kt | 4 ++-- .../android/features/roomdetails/impl/RoomDetailsView.kt | 4 ++-- .../features/roomdetails/impl/edit/RoomDetailsEditView.kt | 4 ++-- .../roomdetails/impl/invite/RoomInviteMembersView.kt | 4 ++-- .../roomdetails/impl/members/RoomMemberListView.kt | 4 ++-- .../impl/members/details/RoomMemberDetailsView.kt | 4 ++-- .../features/verifysession/impl/VerifySelfSessionView.kt | 4 ++-- .../designsystem/atomic/atoms/InfoListItemMolecule.kt | 2 +- .../designsystem/components/LabelledTextField.kt | 4 ++-- .../android/libraries/designsystem/components/PinIcon.kt | 2 +- .../libraries/designsystem/components/avatar/Avatar.kt | 2 +- .../matrix/ui/components/AvatarActionBottomSheet.kt | 4 ++-- .../libraries/matrix/ui/components/MatrixUserHeader.kt | 4 ++-- .../matrix/ui/components/MatrixUserHeaderPlaceholder.kt | 4 ++-- .../libraries/matrix/ui/components/UnsavedAvatar.kt | 4 ++-- .../android/libraries/permissions/api/PermissionsView.kt | 4 ++-- .../android/libraries/textcomposer/TextComposer.kt | 6 +++--- .../android/libraries/theme/MaterialThemeColors.kt | 4 ++-- tools/detekt/detekt.yml | 2 +- 50 files changed, 90 insertions(+), 90 deletions(-) diff --git a/app/src/main/kotlin/io/element/android/x/icon/IconPreview.kt b/app/src/main/kotlin/io/element/android/x/icon/IconPreview.kt index 49c2cc5782..52e3af1aab 100644 --- a/app/src/main/kotlin/io/element/android/x/icon/IconPreview.kt +++ b/app/src/main/kotlin/io/element/android/x/icon/IconPreview.kt @@ -28,7 +28,7 @@ import io.element.android.x.R @Preview @Composable -fun IconPreview( +internal fun IconPreview( modifier: Modifier = Modifier, ) { Box(modifier = modifier) { @@ -39,7 +39,7 @@ fun IconPreview( @Preview @Composable -fun RoundIconPreview( +internal fun RoundIconPreview( modifier: Modifier = Modifier, ) { Box(modifier = modifier.clip(shape = CircleShape)) { diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt index 60784ea4ed..964cb447aa 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt @@ -58,7 +58,7 @@ fun LoggedInView( @DayNightPreviews @Composable -fun LoggedInViewPreview(@PreviewParameter(LoggedInStateProvider::class) state: LoggedInState) = ElementPreview { +internal fun LoggedInViewPreview(@PreviewParameter(LoggedInStateProvider::class) state: LoggedInState) = ElementPreview { LoggedInView( state = state ) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt index 5108bb8716..bdec2b528f 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt @@ -91,7 +91,7 @@ private fun SyncState.mustBeVisible() = when (this) { @DayNightPreviews @Composable -fun SyncStateViewPreview() = ElementPreview { +internal fun SyncStateViewPreview() = ElementPreview { // Add a box to see the shadow Box(modifier = Modifier.padding(24.dp)) { SyncStateView( diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt index ae5ae4f8db..6adc4a026b 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/LoadingRoomNodeView.kt @@ -123,12 +123,12 @@ private fun LoadingRoomTopBar( @Preview @Composable -fun LoadingRoomNodeViewLightPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) = +internal fun LoadingRoomNodeViewLightPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun LoadingRoomNodeViewDarkPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) = +internal fun LoadingRoomNodeViewDarkPreview(@PreviewParameter(LoadingRoomStateProvider::class) state: LoadingRoomState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt index f6d77226b9..5aa9287d86 100644 --- a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt +++ b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt @@ -81,12 +81,12 @@ fun buildAnnotatedStringWithColoredPart( @Preview @Composable -fun AnalyticsPreferencesViewLightPreview(@PreviewParameter(AnalyticsPreferencesStateProvider::class) state: AnalyticsPreferencesState) = +internal fun AnalyticsPreferencesViewLightPreview(@PreviewParameter(AnalyticsPreferencesStateProvider::class) state: AnalyticsPreferencesState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun AnalyticsPreferencesViewDarkPreview(@PreviewParameter(AnalyticsPreferencesStateProvider::class) state: AnalyticsPreferencesState) = +internal fun AnalyticsPreferencesViewDarkPreview(@PreviewParameter(AnalyticsPreferencesStateProvider::class) state: AnalyticsPreferencesState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt index a27e6e7399..5453daa0f9 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt @@ -204,13 +204,13 @@ private fun AnalyticsOptInFooter( @Preview @Composable -fun AnalyticsOptInViewLightPreview(@PreviewParameter(AnalyticsOptInStateProvider::class) state: AnalyticsOptInState) = ElementPreviewLight { +internal fun AnalyticsOptInViewLightPreview(@PreviewParameter(AnalyticsOptInStateProvider::class) state: AnalyticsOptInState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun AnalyticsOptInViewDarkPreview(@PreviewParameter(AnalyticsOptInStateProvider::class) state: AnalyticsOptInState) = ElementPreviewDark { +internal fun AnalyticsOptInViewDarkPreview(@PreviewParameter(AnalyticsOptInStateProvider::class) state: AnalyticsOptInState) = ElementPreviewDark { ContentToPreview(state) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt index 1d900cea8c..302f72d514 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/RoomPrivacyOption.kt @@ -93,11 +93,11 @@ fun RoomPrivacyOption( @Preview @Composable -fun RoomPrivacyOptionLightPreview() = ElementPreviewLight { ContentToPreview() } +internal fun RoomPrivacyOptionLightPreview() = ElementPreviewLight { ContentToPreview() } @Preview @Composable -fun RoomPrivacyOptionDarkPreview() = ElementPreviewDark { ContentToPreview() } +internal fun RoomPrivacyOptionDarkPreview() = ElementPreviewDark { ContentToPreview() } @Composable private fun ContentToPreview() { diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index 9ac8f0fbde..7f533510bd 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -277,12 +277,12 @@ private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier = @Preview @Composable -fun ConfigureRoomViewLightPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = +internal fun ConfigureRoomViewLightPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun ConfigureRoomViewDarkPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = +internal fun ConfigureRoomViewDarkPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt index ac8a05f448..7411dfdae8 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt @@ -205,12 +205,12 @@ fun CreateRoomActionButton( @Preview @Composable -fun CreateRoomRootViewLightPreview(@PreviewParameter(CreateRoomRootStateProvider::class) state: CreateRoomRootState) = +internal fun CreateRoomRootViewLightPreview(@PreviewParameter(CreateRoomRootStateProvider::class) state: CreateRoomRootState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun CreateRoomRootViewDarkPreview(@PreviewParameter(CreateRoomRootStateProvider::class) state: CreateRoomRootState) = +internal fun CreateRoomRootViewDarkPreview(@PreviewParameter(CreateRoomRootStateProvider::class) state: CreateRoomRootState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt index 39b3e8a9a1..50d400092a 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt @@ -120,7 +120,7 @@ fun StaticMapView( @DayNightPreviews @Composable -fun StaticMapViewPreview() = ElementPreview { +internal fun StaticMapViewPreview() = ElementPreview { StaticMapView( lat = 0.0, lon = 0.0, diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapPlaceholder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapPlaceholder.kt index d36ead5b28..84349d97c9 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapPlaceholder.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapPlaceholder.kt @@ -79,7 +79,7 @@ internal fun StaticMapPlaceholder( @DayNightPreviews @Composable -fun StaticMapPlaceholderPreview( +internal fun StaticMapPlaceholderPreview( @PreviewParameter(BooleanParameterProvider::class) values: Boolean ) = ElementPreview { StaticMapPlaceholder( diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt index cdaca94b04..cfb30a5523 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt @@ -224,7 +224,7 @@ fun SendLocationView( @DayNightPreviews @Composable -fun SendLocationViewPreview( +internal fun SendLocationViewPreview( @PreviewParameter(SendLocationStateProvider::class) state: SendLocationState ) = ElementPreview { SendLocationView( diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt index 378859225e..ecc2f996cb 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt @@ -114,12 +114,12 @@ fun AccountProviderView( @Preview @Composable -fun AccountProviderViewLightPreview(@PreviewParameter(AccountProviderProvider::class) item: AccountProvider) = +internal fun AccountProviderViewLightPreview(@PreviewParameter(AccountProviderProvider::class) item: AccountProvider) = ElementPreviewLight { ContentToPreview(item) } @Preview @Composable -fun AccountProviderViewDarkPreview(@PreviewParameter(AccountProviderProvider::class) item: AccountProvider) = +internal fun AccountProviderViewDarkPreview(@PreviewParameter(AccountProviderProvider::class) item: AccountProvider) = ElementPreviewDark { ContentToPreview(item) } @Composable diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt index f9e9624503..ffa337fba7 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt @@ -71,12 +71,12 @@ fun ChangeServerView( @Preview @Composable -fun ChangeServerViewLightPreview(@PreviewParameter(ChangeServerStateProvider::class) state: ChangeServerState) = +internal fun ChangeServerViewLightPreview(@PreviewParameter(ChangeServerStateProvider::class) state: ChangeServerState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun ChangeServerViewDarkPreview(@PreviewParameter(ChangeServerStateProvider::class) state: ChangeServerState) = +internal fun ChangeServerViewDarkPreview(@PreviewParameter(ChangeServerStateProvider::class) state: ChangeServerState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt index c1235b76c5..1b7486e814 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt @@ -100,12 +100,12 @@ fun OidcView( @Preview @Composable -fun OidcViewLightPreview(@PreviewParameter(OidcStateProvider::class) state: OidcState) = +internal fun OidcViewLightPreview(@PreviewParameter(OidcStateProvider::class) state: OidcState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun OidcViewDarkPreview(@PreviewParameter(OidcStateProvider::class) state: OidcState) = +internal fun OidcViewDarkPreview(@PreviewParameter(OidcStateProvider::class) state: OidcState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt index 0f444350c9..0e35f9eda5 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderView.kt @@ -127,12 +127,12 @@ fun ChangeAccountProviderView( @Preview @Composable -fun ChangeAccountProviderViewLightPreview(@PreviewParameter(ChangeAccountProviderStateProvider::class) state: ChangeAccountProviderState) = +internal fun ChangeAccountProviderViewLightPreview(@PreviewParameter(ChangeAccountProviderStateProvider::class) state: ChangeAccountProviderState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun ChangeAccountProviderViewDarkPreview(@PreviewParameter(ChangeAccountProviderStateProvider::class) state: ChangeAccountProviderState) = +internal fun ChangeAccountProviderViewDarkPreview(@PreviewParameter(ChangeAccountProviderStateProvider::class) state: ChangeAccountProviderState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt index 2bc002b3fc..faaabf38cd 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt @@ -143,12 +143,12 @@ fun ConfirmAccountProviderView( @Preview @Composable -fun ConfirmAccountProviderViewLightPreview(@PreviewParameter(ConfirmAccountProviderStateProvider::class) state: ConfirmAccountProviderState) = +internal fun ConfirmAccountProviderViewLightPreview(@PreviewParameter(ConfirmAccountProviderStateProvider::class) state: ConfirmAccountProviderState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun ConfirmAccountProviderViewDarkPreview(@PreviewParameter(ConfirmAccountProviderStateProvider::class) state: ConfirmAccountProviderState) = +internal fun ConfirmAccountProviderViewDarkPreview(@PreviewParameter(ConfirmAccountProviderStateProvider::class) state: ConfirmAccountProviderState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt index 84c5bf4fac..8cfaae2a9e 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt @@ -211,12 +211,12 @@ private fun HomeserverData.toAccountProvider(): AccountProvider { @Preview @Composable -fun SearchAccountProviderViewLightPreview(@PreviewParameter(SearchAccountProviderStateProvider::class) state: SearchAccountProviderState) = +internal fun SearchAccountProviderViewLightPreview(@PreviewParameter(SearchAccountProviderStateProvider::class) state: SearchAccountProviderState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun SearchAccountProviderViewDarkPreview(@PreviewParameter(SearchAccountProviderStateProvider::class) state: SearchAccountProviderState) = +internal fun SearchAccountProviderViewDarkPreview(@PreviewParameter(SearchAccountProviderStateProvider::class) state: SearchAccountProviderState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt index fd2ad94345..613caba586 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt @@ -405,7 +405,7 @@ private fun EmojiButton( @DayNightPreviews @Composable -fun SheetContentPreview( +internal fun SheetContentPreview( @PreviewParameter(ActionListStateProvider::class) state: ActionListState ) = ElementPreview { SheetContent( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt index 6f33ef5d0b..ecdbc183b1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt @@ -166,7 +166,7 @@ private fun AttachmentsPreviewBottomActions( @Preview @Composable -fun AttachmentsPreviewViewDarkPreview(@PreviewParameter(AttachmentsPreviewStateProvider::class) state: AttachmentsPreviewState) = +internal fun AttachmentsPreviewViewDarkPreview(@PreviewParameter(AttachmentsPreviewStateProvider::class) state: AttachmentsPreviewState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt index 467a963088..1b9a3c5c99 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt @@ -283,12 +283,12 @@ private fun ForwardingErrorDialog(onDismiss: () -> Unit, modifier: Modifier = Mo @Preview @Composable -fun ForwardMessagesViewLightPreview(@PreviewParameter(ForwardMessagesStateProvider::class) state: ForwardMessagesState) = +internal fun ForwardMessagesViewLightPreview(@PreviewParameter(ForwardMessagesStateProvider::class) state: ForwardMessagesState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun ForwardMessagesViewDarkPreview(@PreviewParameter(ForwardMessagesStateProvider::class) state: ForwardMessagesState) = +internal fun ForwardMessagesViewDarkPreview(@PreviewParameter(ForwardMessagesStateProvider::class) state: ForwardMessagesState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt index 5fb14a6ca0..ff2e4d49f4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt @@ -255,7 +255,7 @@ private fun ErrorView( @Preview @Composable -fun MediaViewerViewDarkPreview(@PreviewParameter(MediaViewerStateProvider::class) state: MediaViewerState) = +internal fun MediaViewerViewDarkPreview(@PreviewParameter(MediaViewerStateProvider::class) state: MediaViewerState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt index d9851c82a9..7924f07620 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt @@ -168,12 +168,12 @@ fun ReportMessageView( @Preview @Composable -fun ReportMessageViewLightPreview(@PreviewParameter(ReportMessageStateProvider::class) state: ReportMessageState) = +internal fun ReportMessageViewLightPreview(@PreviewParameter(ReportMessageStateProvider::class) state: ReportMessageState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun ReportMessageViewDarkPreview(@PreviewParameter(ReportMessageStateProvider::class) state: ReportMessageState) = +internal fun ReportMessageViewDarkPreview(@PreviewParameter(ReportMessageStateProvider::class) state: ReportMessageState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index d7c259eba2..cecc2d7a63 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -310,7 +310,7 @@ private fun JumpToBottomButton( @DayNightPreviews @Composable -fun TimelineViewPreview( +internal fun TimelineViewPreview( @PreviewParameter(TimelineItemEventContentProvider::class) content: TimelineItemEventContent ) = ElementPreview { val timelineItems = aTimelineItemList(content) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt index 962d3a2b2b..6682a302d2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt @@ -106,7 +106,7 @@ private fun TimelineItemReactionsView( @DayNightPreviews @Composable -fun TimelineItemReactionsViewPreview() = ElementPreview { +internal fun TimelineItemReactionsViewPreview() = ElementPreview { ContentToPreview( reactions = aTimelineItemReactions(count = 1).reactions ) @@ -114,7 +114,7 @@ fun TimelineItemReactionsViewPreview() = ElementPreview { @DayNightPreviews @Composable -fun TimelineItemReactionsViewFewPreview() = ElementPreview { +internal fun TimelineItemReactionsViewFewPreview() = ElementPreview { ContentToPreview( reactions = aTimelineItemReactions(count = 3).reactions ) @@ -122,7 +122,7 @@ fun TimelineItemReactionsViewFewPreview() = ElementPreview { @DayNightPreviews @Composable -fun TimelineItemReactionsViewIncomingPreview() = ElementPreview { +internal fun TimelineItemReactionsViewIncomingPreview() = ElementPreview { ContentToPreview( reactions = aTimelineItemReactions(count = 18).reactions ) @@ -130,7 +130,7 @@ fun TimelineItemReactionsViewIncomingPreview() = ElementPreview { @DayNightPreviews @Composable -fun TimelineItemReactionsViewOutgoingPreview() = ElementPreview { +internal fun TimelineItemReactionsViewOutgoingPreview() = ElementPreview { ContentToPreview( reactions = aTimelineItemReactions(count = 18).reactions, isOutgoing = true diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt index a631e34266..ce853b84b5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/group/GroupHeaderView.kt @@ -91,12 +91,12 @@ fun GroupHeaderView( @Preview @Composable -fun GroupHeaderViewLightPreview() = +internal fun GroupHeaderViewLightPreview() = ElementPreviewLight { ContentToPreview() } @Preview @Composable -fun GroupHeaderViewDarkPreview() = +internal fun GroupHeaderViewDarkPreview() = ElementPreviewDark { ContentToPreview() } @Composable diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt index b7c2663b72..81075969c6 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutView.kt @@ -50,12 +50,12 @@ fun AboutView( @Preview @Composable -fun AboutViewLightPreview(@PreviewParameter(AboutStateProvider::class) state: AboutState) = +internal fun AboutViewLightPreview(@PreviewParameter(AboutStateProvider::class) state: AboutState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun AboutViewDarkPreview(@PreviewParameter(AboutStateProvider::class) state: AboutState) = +internal fun AboutViewDarkPreview(@PreviewParameter(AboutStateProvider::class) state: AboutState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt index 165406c6f5..3ee7365122 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/analytics/AnalyticsSettingsView.kt @@ -46,12 +46,12 @@ fun AnalyticsSettingsView( @Preview @Composable -fun AnalyticsSettingsViewLightPreview(@PreviewParameter(AnalyticsSettingsStateProvider::class) state: AnalyticsSettingsState) = +internal fun AnalyticsSettingsViewLightPreview(@PreviewParameter(AnalyticsSettingsStateProvider::class) state: AnalyticsSettingsState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun AnalyticsSettingsViewDarkPreview(@PreviewParameter(AnalyticsSettingsStateProvider::class) state: AnalyticsSettingsState) = +internal fun AnalyticsSettingsViewDarkPreview(@PreviewParameter(AnalyticsSettingsStateProvider::class) state: AnalyticsSettingsState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt index 7c5b5d91c8..23ea4faf86 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt @@ -96,12 +96,12 @@ fun FeatureListContent( @Preview @Composable -fun DeveloperSettingsViewLightPreview(@PreviewParameter(DeveloperSettingsStateProvider::class) state: DeveloperSettingsState) = +internal fun DeveloperSettingsViewLightPreview(@PreviewParameter(DeveloperSettingsStateProvider::class) state: DeveloperSettingsState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun DeveloperSettingsViewDarkPreview(@PreviewParameter(DeveloperSettingsStateProvider::class) state: DeveloperSettingsState) = +internal fun DeveloperSettingsViewDarkPreview(@PreviewParameter(DeveloperSettingsStateProvider::class) state: DeveloperSettingsState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt index 90eade31ab..ba8dc05f35 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt @@ -129,12 +129,12 @@ fun DeveloperPreferencesView(onOpenDeveloperSettings: () -> Unit) { @LargeHeightPreview @Composable -fun PreferencesRootViewLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = +internal fun PreferencesRootViewLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreviewLight { ContentToPreview(matrixUser) } @LargeHeightPreview @Composable -fun PreferencesRootViewDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = +internal fun PreferencesRootViewDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreviewDark { ContentToPreview(matrixUser) } @Composable diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt index 73e04fb5d4..3cc2a8211a 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt @@ -68,12 +68,12 @@ fun RageshakePreferencesView( @Preview @Composable -fun RageshakePreferencesViewLightPreview(@PreviewParameter(RageshakePreferencesStateProvider::class) state: RageshakePreferencesState) = +internal fun RageshakePreferencesViewLightPreview(@PreviewParameter(RageshakePreferencesStateProvider::class) state: RageshakePreferencesState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun RageshakePreferencesViewDarkPreview(@PreviewParameter(RageshakePreferencesStateProvider::class) state: RageshakePreferencesState) = +internal fun RageshakePreferencesViewDarkPreview(@PreviewParameter(RageshakePreferencesStateProvider::class) state: RageshakePreferencesState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt index 74f3a13d13..3748f2931b 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt @@ -176,11 +176,11 @@ fun BugReportView( @Preview @Composable -fun BugReportViewLightPreview(@PreviewParameter(BugReportStateProvider::class) state: BugReportState) = ElementPreviewLight { ContentToPreview(state) } +internal fun BugReportViewLightPreview(@PreviewParameter(BugReportStateProvider::class) state: BugReportState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun BugReportViewDarkPreview(@PreviewParameter(BugReportStateProvider::class) state: BugReportState) = ElementPreviewDark { ContentToPreview(state) } +internal fun BugReportViewDarkPreview(@PreviewParameter(BugReportStateProvider::class) state: BugReportState) = ElementPreviewDark { ContentToPreview(state) } @Composable private fun ContentToPreview(state: BugReportState) { diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index 63868c8227..b3db986bf9 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -321,12 +321,12 @@ internal fun OtherActionsSection(onLeaveRoom: () -> Unit, modifier: Modifier = M @LargeHeightPreview @Composable -fun RoomDetailsLightPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) = +internal fun RoomDetailsLightPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) = ElementPreviewLight { ContentToPreview(state) } @LargeHeightPreview @Composable -fun RoomDetailsDarkPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) = +internal fun RoomDetailsDarkPreview(@PreviewParameter(RoomDetailsStateProvider::class) state: RoomDetailsState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt index 029f5ac5df..ba470d631e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt @@ -287,12 +287,12 @@ private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier = @Preview @Composable -fun RoomDetailsEditViewLightPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) = +internal fun RoomDetailsEditViewLightPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun RoomDetailsEditViewDarkPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) = +internal fun RoomDetailsEditViewDarkPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt index 555f04af20..b53800236e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt @@ -220,12 +220,12 @@ private fun RoomInviteMembersSearchBar( @Preview @Composable -fun RoomInviteMembersLightPreview(@PreviewParameter(RoomInviteMembersStateProvider::class) state: RoomInviteMembersState) = +internal fun RoomInviteMembersLightPreview(@PreviewParameter(RoomInviteMembersStateProvider::class) state: RoomInviteMembersState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun RoomInviteMembersDarkPreview(@PreviewParameter(RoomInviteMembersStateProvider::class) state: RoomInviteMembersState) = +internal fun RoomInviteMembersDarkPreview(@PreviewParameter(RoomInviteMembersStateProvider::class) state: RoomInviteMembersState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt index 3bfde66c06..bad1dc3591 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt @@ -257,12 +257,12 @@ private fun RoomMemberSearchBar( @Preview @Composable -fun RoomMemberListLightPreview(@PreviewParameter(RoomMemberListStateProvider::class) state: RoomMemberListState) = +internal fun RoomMemberListLightPreview(@PreviewParameter(RoomMemberListStateProvider::class) state: RoomMemberListState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun RoomMemberListDarkPreview(@PreviewParameter(RoomMemberListStateProvider::class) state: RoomMemberListState) = +internal fun RoomMemberListDarkPreview(@PreviewParameter(RoomMemberListStateProvider::class) state: RoomMemberListState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt index 72b9d4c20c..dcd9232d60 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt @@ -146,12 +146,12 @@ internal fun SendMessageSection(onSendMessage: () -> Unit, modifier: Modifier = @LargeHeightPreview @Composable -fun RoomMemberDetailsViewLightPreview(@PreviewParameter(RoomMemberDetailsStateProvider::class) state: RoomMemberDetailsState) = +internal fun RoomMemberDetailsViewLightPreview(@PreviewParameter(RoomMemberDetailsStateProvider::class) state: RoomMemberDetailsState) = ElementPreviewLight { ContentToPreview(state) } @LargeHeightPreview @Composable -fun RoomMemberDetailsViewDarkPreview(@PreviewParameter(RoomMemberDetailsStateProvider::class) state: RoomMemberDetailsState) = +internal fun RoomMemberDetailsViewDarkPreview(@PreviewParameter(RoomMemberDetailsStateProvider::class) state: RoomMemberDetailsState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt index 77984a3bb2..a116300dc6 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt @@ -242,12 +242,12 @@ internal fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit) @Preview @Composable -fun VerifySelfSessionViewLightPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) = +internal fun VerifySelfSessionViewLightPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun VerifySelfSessionViewDarkPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) = +internal fun VerifySelfSessionViewDarkPreview(@PreviewParameter(VerifySelfSessionStateProvider::class) state: VerifySelfSessionState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/InfoListItemMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/InfoListItemMolecule.kt index 6b20c96880..2af9e77b99 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/InfoListItemMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/InfoListItemMolecule.kt @@ -70,7 +70,7 @@ fun InfoListItemMolecule( @DayNightPreviews @Composable -fun InfoListItemMoleculePreview() { +internal fun InfoListItemMoleculePreview() { ElementPreview { val color = if (isSystemInDarkTheme()) Color.DarkGray else Color.LightGray Column( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt index cc1d92ccd8..4c790a48e2 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt @@ -65,11 +65,11 @@ fun LabelledTextField( @Preview @Composable -fun LabelledTextFieldLightPreview() = ElementPreviewLight { ContentToPreview() } +internal fun LabelledTextFieldLightPreview() = ElementPreviewLight { ContentToPreview() } @Preview @Composable -fun LabelledTextFieldDarkPreview() = ElementPreviewDark { ContentToPreview() } +internal fun LabelledTextFieldDarkPreview() = ElementPreviewDark { ContentToPreview() } @Composable private fun ContentToPreview() { diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt index c6af3e1cdf..93a0c5d436 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/PinIcon.kt @@ -51,6 +51,6 @@ fun PinIcon( @DayNightPreviews @Composable -fun PinIconPreview() = ElementPreview { +internal fun PinIconPreview() = ElementPreview { PinIcon() } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt index b28e52a5ff..1c763a81c3 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/Avatar.kt @@ -102,7 +102,7 @@ private fun InitialsAvatar( @Preview(group = PreviewGroup.Avatars) @Composable -fun AvatarPreview(@PreviewParameter(AvatarDataProvider::class) avatarData: AvatarData) = +internal fun AvatarPreview(@PreviewParameter(AvatarDataProvider::class) avatarData: AvatarData) = ElementThemedPreview { Row( verticalAlignment = Alignment.CenterVertically, diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt index b12f577c36..d541b534b1 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt @@ -111,12 +111,12 @@ private fun AvatarActionBottomSheetContent( @Preview @Composable -fun AvatarActionBottomSheetLightPreview() = +internal fun AvatarActionBottomSheetLightPreview() = ElementPreviewLight { ContentToPreview() } @Preview @Composable -fun AvatarActionBottomSheetDarkPreview() = +internal fun AvatarActionBottomSheetDarkPreview() = ElementPreviewDark { ContentToPreview() } @Composable diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt index 6054aa53af..a42d44b642 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeader.kt @@ -103,12 +103,12 @@ private fun MatrixUserHeaderContent( @Preview @Composable -fun MatrixUserHeaderLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = +internal fun MatrixUserHeaderLightPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreviewLight { ContentToPreview(matrixUser) } @Preview @Composable -fun MatrixUserHeaderDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = +internal fun MatrixUserHeaderDarkPreview(@PreviewParameter(MatrixUserProvider::class) matrixUser: MatrixUser) = ElementPreviewDark { ContentToPreview(matrixUser) } @Composable diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt index 57901edc03..faa80f82e6 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserHeaderPlaceholder.kt @@ -68,12 +68,12 @@ fun MatrixUserHeaderPlaceholder( @Preview @Composable -fun MatrixUserHeaderPlaceholderLightPreview() = +internal fun MatrixUserHeaderPlaceholderLightPreview() = ElementPreviewLight { ContentToPreview() } @Preview @Composable -fun MatrixUserHeaderPlaceholderDarkPreview() = +internal fun MatrixUserHeaderPlaceholderDarkPreview() = ElementPreviewDark { ContentToPreview() } @Composable diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt index 2b5d2f6800..13fcb40792 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt @@ -85,11 +85,11 @@ fun UnsavedAvatar( @Preview @Composable -fun UnsavedAvatarLightPreview() = ElementPreviewLight { ContentToPreview() } +internal fun UnsavedAvatarLightPreview() = ElementPreviewLight { ContentToPreview() } @Preview @Composable -fun UnsavedAvatarDarkPreview() = ElementPreviewDark { ContentToPreview() } +internal fun UnsavedAvatarDarkPreview() = ElementPreviewDark { ContentToPreview() } @Composable private fun ContentToPreview() { diff --git a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt index 30f19aa31c..d9dba2a0d3 100644 --- a/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt +++ b/libraries/permissions/api/src/main/kotlin/io/element/android/libraries/permissions/api/PermissionsView.kt @@ -83,12 +83,12 @@ fun PermissionsView( @Preview @Composable -fun PermissionsViewLightPreview(@PreviewParameter(PermissionsViewStateProvider::class) state: PermissionsState) = +internal fun PermissionsViewLightPreview(@PreviewParameter(PermissionsViewStateProvider::class) state: PermissionsState) = ElementPreviewLight { ContentToPreview(state) } @Preview @Composable -fun PermissionsViewDarkPreview(@PreviewParameter(PermissionsViewStateProvider::class) state: PermissionsState) = +internal fun PermissionsViewDarkPreview(@PreviewParameter(PermissionsViewStateProvider::class) state: PermissionsState) = ElementPreviewDark { ContentToPreview(state) } @Composable diff --git a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index 8c4c361707..09f493adc6 100644 --- a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -415,7 +415,7 @@ private fun BoxScope.SendButton( @DayNightPreviews @Composable -fun TextComposerSimplePreview() = ElementPreview { +internal fun TextComposerSimplePreview() = ElementPreview { Column { TextComposer( onSendMessage = {}, @@ -446,7 +446,7 @@ fun TextComposerSimplePreview() = ElementPreview { @DayNightPreviews @Composable -fun TextComposerEditPreview() = ElementPreview { +internal fun TextComposerEditPreview() = ElementPreview { TextComposer( onSendMessage = {}, onComposerTextChange = {}, @@ -459,7 +459,7 @@ fun TextComposerEditPreview() = ElementPreview { @DayNightPreviews @Composable -fun TextComposerReplyPreview() = ElementPreview { +internal fun TextComposerReplyPreview() = ElementPreview { Column { TextComposer( onSendMessage = {}, diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt index d211869f71..3d359594e1 100644 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt +++ b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/MaterialThemeColors.kt @@ -91,7 +91,7 @@ internal val materialColorSchemeDark = darkColorScheme( @Preview @Composable -fun ColorsSchemePreviewLight() = ColorsSchemePreview( +internal fun ColorsSchemePreviewLight() = ColorsSchemePreview( Color.Black, Color.White, materialColorSchemeLight, @@ -99,7 +99,7 @@ fun ColorsSchemePreviewLight() = ColorsSchemePreview( @Preview @Composable -fun ColorsSchemePreviewDark() = ColorsSchemePreview( +internal fun ColorsSchemePreviewDark() = ColorsSchemePreview( Color.White, Color.Black, materialColorSchemeDark, diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 0123535b48..cf497b944e 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -149,7 +149,7 @@ Compose: PreviewNaming: active: true PreviewPublic: - active: false + active: true # You can optionally disable that only previews with @PreviewParameter are flagged previewPublicOnlyIfParams: false RememberMissing: From f3974dd5701397bd09aa8093ca0363b1908f2252 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 10:11:00 +0200 Subject: [PATCH 114/251] Enable detekt rule `ImplicitDefaultLocale` and fix existing issue. --- .../android/libraries/androidutils/system/SystemUtils.kt | 2 +- tools/detekt/detekt.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt index 8347990303..a9a17dcceb 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt @@ -183,7 +183,7 @@ fun Context.startInstallFromSourceIntent( noActivityFoundMessage: String = getString(R.string.error_no_compatible_app_found), ) { val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES) - .setData(Uri.parse(String.format("package:%s", packageName))) + .setData(Uri.parse("package:$packageName")) try { activityResultLauncher.launch(intent) } catch (activityNotFoundException: ActivityNotFoundException) { diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index cf497b944e..0ed13accdb 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -48,7 +48,7 @@ empty-blocks: potential-bugs: ImplicitDefaultLocale: - active: false + active: true exceptions: TooGenericExceptionCaught: From 9e3679cbd67b96ebf1807e0aed293c6328cd723b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 10:18:28 +0200 Subject: [PATCH 115/251] Enable detekt rule `UseCheckOrError` and fix existing issues (and a potential crash on MapboxMap?). --- .../confirmaccountprovider/ConfirmAccountProviderPresenter.kt | 2 +- .../io/element/android/libraries/maplibre/compose/MapboxMap.kt | 2 +- .../matrix/impl/auth/RustMatrixAuthenticationService.kt | 2 +- tools/detekt/detekt.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt index 123d3013c7..1a021ad605 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderPresenter.kt @@ -92,7 +92,7 @@ class ConfirmAccountProviderPresenter @AssistedInject constructor( } else if (matrixHomeServerDetails.supportsPasswordLogin) { LoginFlow.PasswordLogin } else { - throw IllegalStateException("Unsupported login flow") + error("Unsupported login flow") } }.getOrThrow() }.runCatchingUpdatingState(loginFlowAction, errorTransform = ChangeServerError::from) diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt index c67be52c1c..82b5e1c237 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt @@ -236,7 +236,7 @@ private fun MapView.lifecycleObserver(previousState: MutableState { //handled in onDispose } - else -> throw IllegalStateException() + Lifecycle.Event.ON_ANY -> Unit } previousState.value = event } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt index 0bc299d020..71e6e730e5 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt @@ -93,7 +93,7 @@ class RustMatrixAuthenticationService @Inject constructor( client.restoreSession(sessionData.toSession()) createMatrixClient(client) } else { - throw IllegalStateException("No session to restore with id $sessionId") + error("No session to restore with id $sessionId") } }.mapFailure { failure -> failure.mapClientException() diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 0ed13accdb..79d8d5fe51 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -38,7 +38,7 @@ style: ProtectedMemberInFinalClass: active: false UseCheckOrError: - active: false + active: true empty-blocks: EmptyFunctionBlock: From 9894e2fed05c17c93612e2fea1f662b3f2d420a3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 10:19:25 +0200 Subject: [PATCH 116/251] Enable detekt rule `ProtectedMemberInFinalClass`. --- tools/detekt/detekt.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 79d8d5fe51..57754a88bc 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -36,7 +36,7 @@ style: SerialVersionUIDInSerializableClass: active: false ProtectedMemberInFinalClass: - active: false + active: true UseCheckOrError: active: true From d82b47beebf8342ae99467364541581cb74787bf Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 10:23:11 +0200 Subject: [PATCH 117/251] Enable detekt rule `UnnecessaryAbstractClass`, `LoopWithTooManyJumpStatements`, `EmptySecondaryConstructor`. --- tools/detekt/detekt.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 57754a88bc..919a9f74f8 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -9,7 +9,7 @@ style: ReturnCount: active: false UnnecessaryAbstractClass: - active: false + active: true FunctionOnlyReturningConstant: active: false UnusedPrivateMember: @@ -32,7 +32,7 @@ style: ThrowsCount: active: false LoopWithTooManyJumpStatements: - active: false + active: true SerialVersionUIDInSerializableClass: active: false ProtectedMemberInFinalClass: @@ -44,7 +44,7 @@ empty-blocks: EmptyFunctionBlock: active: false EmptySecondaryConstructor: - active: false + active: true potential-bugs: ImplicitDefaultLocale: From f055a25457ccc0bdd4cb417fa976a7bbec02a693 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 10:33:51 +0200 Subject: [PATCH 118/251] Enable detekt rule `ThrowingExceptionsWithoutMessageOrCause` and fix existing issues. --- .../createroom/impl/root/CreateRoomRootStateProvider.kt | 2 +- .../impl/screens/waitlistscreen/WaitListStateProvider.kt | 2 +- .../attachments/preview/AttachmentsPreviewStateProvider.kt | 2 +- .../messages/impl/media/viewer/MediaViewerStateProvider.kt | 2 +- .../messages/impl/report/ReportMessageStateProvider.kt | 2 +- .../libraries/matrix/api/permalink/PermalinkParser.kt | 6 +++--- tools/detekt/detekt.yml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt index d1484b7a4f..7a3bf6ef03 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootStateProvider.kt @@ -41,7 +41,7 @@ open class CreateRoomRootStateProvider : PreviewParameterProvider { get() = sequenceOf( aWaitListState(loginAction = Async.Uninitialized), aWaitListState(loginAction = Async.Loading()), - aWaitListState(loginAction = Async.Failure(Throwable())), + aWaitListState(loginAction = Async.Failure(Throwable("error"))), aWaitListState(loginAction = Async.Failure(Throwable(message = "IO_ELEMENT_X_WAIT_LIST"))), aWaitListState(loginAction = Async.Success(SessionId("@alice:element.io"))), // Add other state here diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt index ee41ace4b0..de7f5cd47b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt @@ -30,7 +30,7 @@ open class AttachmentsPreviewStateProvider : PreviewParameterProvider get() = sequenceOf( aMediaViewerState(), aMediaViewerState(Async.Loading()), - aMediaViewerState(Async.Failure(IllegalStateException())), + aMediaViewerState(Async.Failure(IllegalStateException("error"))), aMediaViewerState( Async.Success( LocalMedia(Uri.EMPTY, anImageInfo()) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageStateProvider.kt index 89e6d7a220..de5e787a3d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageStateProvider.kt @@ -26,7 +26,7 @@ open class ReportMessageStateProvider : PreviewParameterProvider Date: Wed, 26 Jul 2023 10:34:39 +0200 Subject: [PATCH 119/251] Enable detekt rule `ComplexCondition`, `LargeClass`. --- tools/detekt/detekt.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 37b77ff957..da3735a47e 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -74,9 +74,9 @@ complexity: NestedBlockDepth: active: false ComplexCondition: - active: false + active: true LargeClass: - active: false + active: true naming: VariableNaming: From 5d0bf5dbe0ed595310e5b1a29f3eaf648226e550 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 10:37:44 +0200 Subject: [PATCH 120/251] Enable detekt rule `InstanceOfCheckForException`. --- tools/detekt/detekt.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index da3735a47e..b8c1428c9e 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -60,7 +60,7 @@ exceptions: TooGenericExceptionThrown: active: false InstanceOfCheckForException: - active: false + active: true complexity: TooManyFunctions: From 140271069c641e7b9a6e7201461792c1ff052289 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 10:38:44 +0200 Subject: [PATCH 121/251] Enable detekt rule `TooGenericExceptionThrown` and fix existing issue. --- .../io/element/android/libraries/architecture/NodeInputs.kt | 2 +- tools/detekt/detekt.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeInputs.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeInputs.kt index b96d9e166b..534c9d741b 100644 --- a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeInputs.kt +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeInputs.kt @@ -23,5 +23,5 @@ import com.bumble.appyx.core.plugin.plugins interface NodeInputs : Plugin inline fun Node.inputs(): I { - return plugins().firstOrNull() ?: throw RuntimeException("Make sure to actually pass NodeInputs plugin to your node") + return requireNotNull(plugins().firstOrNull()) { "Make sure to actually pass NodeInputs plugin to your node" } } diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index b8c1428c9e..d3ee4dc679 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -58,7 +58,7 @@ exceptions: ThrowingExceptionsWithoutMessageOrCause: active: true TooGenericExceptionThrown: - active: false + active: true InstanceOfCheckForException: active: true From cfa63caebb757d0133733d4bb7b6d04cc465f52d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Jul 2023 08:42:55 +0000 Subject: [PATCH 122/251] Update dependency io.sentry:sentry-android to v6.27.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fe9a5aa182..499aeb29c8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -163,7 +163,7 @@ maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:2.0.0" # Analytics posthog = "com.posthog.android:posthog:2.0.3" -sentry = "io.sentry:sentry-android:6.26.0" +sentry = "io.sentry:sentry-android:6.27.0" matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:42b2faa417c1e95f430bf8f6e379adba25ad5ef8" # Di From 03cefb2c791b65d47b01668cb0640dbb5dd88c57 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 10:48:38 +0200 Subject: [PATCH 123/251] Enable detekt rules `SuspendFunSwallowedCancellation` and `SuspendFunWithCoroutineScopeReceiver`. --- tools/detekt/detekt.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index d3ee4dc679..756d145821 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -40,6 +40,15 @@ style: UseCheckOrError: active: true +coroutines: + GlobalCoroutineUsage: + # Keep false for now. + active: false + SuspendFunSwallowedCancellation: + active: true + SuspendFunWithCoroutineScopeReceiver: + active: true + empty-blocks: EmptyFunctionBlock: active: false From 1d3aa2cd0547cac25f3d9c961e51290fcf41172b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 11:07:11 +0200 Subject: [PATCH 124/251] Enable detekt rules `CascadingCallWrapping` and fix existing issues. --- .../io/element/android/appnav/LoggedInEventProcessor.kt | 3 ++- .../kotlin/io/element/android/appnav/LoggedInFlowNode.kt | 7 ++++--- .../io/element/android/appnav/room/RoomFlowNode.kt | 3 ++- .../io/element/android/appnav/room/RoomLoadedFlowNode.kt | 3 ++- .../messages/impl/media/viewer/MediaViewerPresenter.kt | 9 ++++++--- .../impl/messagecomposer/MessageComposerPresenter.kt | 3 ++- .../roomlist/impl/datasource/RoomListDataSource.kt | 3 ++- .../libraries/matrix/api/permalink/PermalinkParser.kt | 3 ++- .../android/libraries/matrix/impl/RustMatrixClient.kt | 6 ++++-- .../matrix/impl/room/RustRoomSummaryDataSource.kt | 3 ++- .../libraries/matrix/impl/timeline/RustMatrixTimeline.kt | 3 ++- tools/detekt/detekt.yml | 3 +++ 12 files changed, 33 insertions(+), 16 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt index 64c9ec7c4f..e55f059d14 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt @@ -58,7 +58,8 @@ class LoggedInEventProcessor @Inject constructor( .filter { it } .onEach { displayMessage(CommonStrings.common_verification_complete) - }.launchIn(this) + } + .launchIn(this) } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 4130e5da23..03ac045158 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -44,14 +44,14 @@ import io.element.android.appnav.loggedin.LoggedInNode import io.element.android.appnav.room.RoomFlowNode import io.element.android.appnav.room.RoomLoadedFlowNode import io.element.android.features.createroom.api.CreateRoomEntryPoint +import io.element.android.features.ftue.api.FtueEntryPoint +import io.element.android.features.ftue.api.state.FtueState import io.element.android.features.invitelist.api.InviteListEntryPoint import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.features.preferences.api.PreferencesEntryPoint import io.element.android.features.roomlist.api.RoomListEntryPoint import io.element.android.features.verifysession.api.VerifySessionEntryPoint -import io.element.android.features.ftue.api.FtueEntryPoint -import io.element.android.features.ftue.api.state.FtueState import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler @@ -305,7 +305,8 @@ class LoggedInFlowNode @AssistedInject constructor( override fun onFtueFlowFinished() { backstack.pop() } - }).build() + }) + .build() } } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt index 20ec9f48b4..661d3c5433 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt @@ -96,7 +96,8 @@ class RoomFlowNode @AssistedInject constructor( } else { backstack.newRoot(NavTarget.Loading) } - }.launchIn(lifecycleScope) + } + .launchIn(lifecycleScope) } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt index 24ec9795f7..b3156bfc84 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt @@ -115,7 +115,8 @@ class RoomLoadedFlowNode @AssistedInject constructor( room.updateMembers() .onFailure { Timber.e(it, "Fail to fetch members for room ${room.roomId}") - }.onSuccess { + } + .onSuccess { Timber.v("Success fetching members for room ${room.roomId}") } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt index 7cc73ef32e..13a9ff3bee 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerPresenter.kt @@ -103,14 +103,17 @@ class MediaViewerPresenter @AssistedInject constructor( ) .onSuccess { mediaFile.value = it - }.mapCatching { mediaFile -> + } + .mapCatching { mediaFile -> localMediaFactory.createFromMediaFile( mediaFile = mediaFile, mediaInfo = inputs.mediaInfo ) - }.onSuccess { + } + .onSuccess { localMedia.value = Async.Success(it) - }.onFailure { + } + .onFailure { localMedia.value = Async.Failure(it) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index ec16d3342d..934a67f2e4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -268,7 +268,8 @@ class MessageComposerPresenter @Inject constructor( mediaSender.sendMedia(uri, mimeType, compressIfPossible = false, progressCallback) .onSuccess { attachmentState.value = AttachmentsState.None - }.onFailure { + } + .onFailure { val snackbarMessage = SnackbarMessage(sendAttachmentError(it)) snackbarDispatcher.post(snackbarMessage) attachmentState.value = AttachmentsState.None diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt index 8602f15910..4eb7bc9d72 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt @@ -73,7 +73,8 @@ class RoomListDataSource @Inject constructor( } .onEach { _filteredRooms.value = it - }.launchIn(coroutineScope) + } + .launchIn(coroutineScope) } fun updateFilter(filterValue: String) { diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt index 2db607768b..ba9cdc1e80 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt @@ -137,7 +137,8 @@ object PermalinkParser { .parameterList .filter { it.mParameter == "via" - }.map { + } + .map { URLDecoder.decode(it.mValue, "UTF-8") } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 640e0772a9..8c418cf3e4 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -147,7 +147,8 @@ class RustMatrixClient constructor( if (syncState == SyncState.Running) { onSlidingSyncUpdate() } - }.launchIn(sessionCoroutineScope) + } + .launchIn(sessionCoroutineScope) } override suspend fun getRoom(roomId: RoomId): MatrixRoom? { @@ -227,7 +228,8 @@ class RustMatrixClient constructor( roomSummaryDataSource.allRooms() .filter { roomSummaries -> roomSummaries.map { it.identifier() }.contains(roomId.value) - }.first() + } + .first() } roomId } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt index efdbcf34ad..83d35a4183 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt @@ -64,7 +64,8 @@ internal class RustRoomSummaryDataSource( .map { it.toRoomSummaryDataSourceLoadingState() } .onEach { allRoomsLoadingState.value = it - }.launchIn(this) + } + .launchIn(this) launch { // Wait until running, as invites is only available after that diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt index d9b6604170..d55585879f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt @@ -113,7 +113,8 @@ class RustMatrixTimeline( innerRoom.backPaginationStatusFlow() .onEach { postPaginationStatus(it) - }.launchIn(this) + } + .launchIn(this) fetchMembers() } diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 756d145821..3c9f9b5a25 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -1,6 +1,9 @@ # Default rules: https://github.com/detekt/detekt/blob/main/detekt-core/src/main/resources/default-detekt-config.yml style: + CascadingCallWrapping: + active: true + includeElvis: true MaxLineLength: # Default is 120 maxLineLength: 160 From d433c3cbaacfc0872f6a367fc0f252c11748347b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 11:10:30 +0200 Subject: [PATCH 125/251] Enable detekt rules `AlsoCouldBeApply` and fix existing issues. --- .../login/impl/oidc/customtab/CustomTabHandler.kt | 3 +-- .../impl/DefaultLastMessageTimestampFormatterTest.kt | 2 +- .../components/avatar/AvatarDataProvider.kt | 11 ++++------- tools/detekt/detekt.yml | 2 ++ 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt index 48c674e0a0..b83c0eb1ac 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt @@ -41,8 +41,7 @@ class CustomTabHandler @Inject constructor( if (packageName != null) { customTabsServiceConnection = object : CustomTabsServiceConnection() { override fun onCustomTabsServiceConnected(name: ComponentName, client: CustomTabsClient) { - customTabsClient = client - .also { it.warmup(0L) } + customTabsClient = client.apply { warmup(0L) } prefetchUrl(url) } diff --git a/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageTimestampFormatterTest.kt b/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageTimestampFormatterTest.kt index 483e27af71..5aefcdcd7b 100644 --- a/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageTimestampFormatterTest.kt +++ b/libraries/dateformatter/impl/src/test/kotlin/io/element/android/libraries/dateformatter/impl/DefaultLastMessageTimestampFormatterTest.kt @@ -101,7 +101,7 @@ class DefaultLastMessageTimestampFormatterTest { * Create DefaultLastMessageFormatter and set current time to the provided date. */ private fun createFormatter(@Suppress("SameParameterValue") currentDate: String): LastMessageTimestampFormatter { - val clock = FakeClock().also { it.givenInstant(Instant.parse(currentDate)) } + val clock = FakeClock().apply { givenInstant(Instant.parse(currentDate)) } val localDateTimeProvider = LocalDateTimeProvider(clock, TimeZone.UTC) val dateFormatters = DateFormatters(Locale.US, clock, TimeZone.UTC) return DefaultLastMessageTimestampFormatter(localDateTimeProvider, dateFormatters) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt index 1727fffd1c..14c7ad3eff 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarDataProvider.kt @@ -20,19 +20,16 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider open class AvatarDataProvider : PreviewParameterProvider { override val values: Sequence - get() { - AvatarSize.values() - .also { it.sortBy { item -> item.name } } - .asSequence() - return AvatarSize.values().asSequence().map { + get() = AvatarSize.values() + .asSequence() + .map { sequenceOf( anAvatarData(size = it), anAvatarData(size = it).copy(name = null), anAvatarData(size = it).copy(url = "aUrl"), ) } - .flatten() - } + .flatten() } fun anAvatarData( diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 3c9f9b5a25..c7a35f61d3 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -1,6 +1,8 @@ # Default rules: https://github.com/detekt/detekt/blob/main/detekt-core/src/main/resources/default-detekt-config.yml style: + AlsoCouldBeApply: + active: true CascadingCallWrapping: active: true includeElvis: true From 8458a9e937489862b53ad6d29fcb83253cc8325b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 11:15:52 +0200 Subject: [PATCH 126/251] Enable detekt rules `DataClassShouldBeImmutable` and fix existing issues. --- .../impl/notifications/RoomEventGroupInfo.kt | 22 +++++++++---------- .../notifications/RoomGroupMessageCreator.kt | 11 +++++----- .../model/SimpleNotifiableEvent.kt | 2 +- .../firebase/PushDataFirebase.kt | 2 +- .../unifiedpush/PushDataUnifiedPush.kt | 2 +- tools/detekt/detekt.yml | 2 ++ 6 files changed, 20 insertions(+), 21 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomEventGroupInfo.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomEventGroupInfo.kt index 734c34b051..96a8b90f06 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomEventGroupInfo.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomEventGroupInfo.kt @@ -23,17 +23,15 @@ import io.element.android.libraries.matrix.api.core.SessionId * Data class to hold information about a group of notifications for a room. */ data class RoomEventGroupInfo( - val sessionId: SessionId, - val roomId: RoomId, - val roomDisplayName: String, - val isDirect: Boolean = false -) { + val sessionId: SessionId, + val roomId: RoomId, + val roomDisplayName: String, + val isDirect: Boolean = false, // An event in the list has not yet been display - var hasNewEvent: Boolean = false - + val hasNewEvent: Boolean = false, // true if at least one on the not yet displayed event is noisy - var shouldBing: Boolean = false - var customSound: String? = null - var hasSmartReplyError: Boolean = false - var isUpdated: Boolean = false -} + val shouldBing: Boolean = false, + val customSound: String? = null, + val hasSmartReplyError: Boolean = false, + val isUpdated: Boolean = false, +) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt index 989ba2ad09..0f2b548a52 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt @@ -85,12 +85,11 @@ class RoomGroupMessageCreator @Inject constructor( roomId = roomId, roomDisplayName = roomName, isDirect = !roomIsGroup, - ).also { - it.hasSmartReplyError = smartReplyErrors.isNotEmpty() - it.shouldBing = meta.shouldBing - it.customSound = events.last().soundName - it.isUpdated = events.last().isUpdated - }, + hasSmartReplyError = smartReplyErrors.isNotEmpty(), + shouldBing = meta.shouldBing, + customSound = events.last().soundName, + isUpdated = events.last().isUpdated, + ), threadId = lastKnownRoomEvent.threadId, largeIcon = largeBitmap, lastMessageTimestamp, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/SimpleNotifiableEvent.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/SimpleNotifiableEvent.kt index f252765530..4b262983d4 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/SimpleNotifiableEvent.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/SimpleNotifiableEvent.kt @@ -30,7 +30,7 @@ data class SimpleNotifiableEvent( val type: String?, val timestamp: Long, val soundName: String?, - override var canBeReplaced: Boolean, + override val canBeReplaced: Boolean, override val isRedacted: Boolean = false, override val isUpdated: Boolean = false ) : NotifiableEvent diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/PushDataFirebase.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/PushDataFirebase.kt index 9dedf9648f..795c8bb1e8 100644 --- a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/PushDataFirebase.kt +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/PushDataFirebase.kt @@ -36,7 +36,7 @@ import io.element.android.libraries.pushproviders.api.PushData data class PushDataFirebase( val eventId: String?, val roomId: String?, - var unread: Int?, + val unread: Int?, val clientSecret: String? ) diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/PushDataUnifiedPush.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/PushDataUnifiedPush.kt index f092d0167c..4485cb2c7f 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/PushDataUnifiedPush.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/PushDataUnifiedPush.kt @@ -47,7 +47,7 @@ data class PushDataUnifiedPush( data class PushDataUnifiedPushNotification( @SerialName("event_id") val eventId: String? = null, @SerialName("room_id") val roomId: String? = null, - @SerialName("counts") var counts: PushDataUnifiedPushCounts? = null, + @SerialName("counts") val counts: PushDataUnifiedPushCounts? = null, ) @Serializable diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index c7a35f61d3..e65f3a3864 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -6,6 +6,8 @@ style: CascadingCallWrapping: active: true includeElvis: true + DataClassShouldBeImmutable: + active: true MaxLineLength: # Default is 120 maxLineLength: 160 From 4ba4bd1f56815d75c590fb96bf688e4bf8653ed8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 11:23:05 +0200 Subject: [PATCH 127/251] Enable more detekt rules and remove unused extension. --- .../core/extensions/BasicExtensions.kt | 4 -- tools/detekt/detekt.yml | 48 +++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt index db07432df0..343f5ce351 100644 --- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt @@ -72,7 +72,3 @@ fun String.ellipsize(length: Int): String { return "${this.take(length)}…" } - -inline fun Any?.takeAs(): R? { - return takeIf { it is R } as R? -} diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index e65f3a3864..c9e8aa395b 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -8,6 +8,14 @@ style: includeElvis: true DataClassShouldBeImmutable: active: true + EqualsNullCall: + active: true + EqualsOnSignatureLine: + active: true + ExplicitCollectionElementAccessMethod: + active: true + ExplicitItLambdaParameter: + active: true MaxLineLength: # Default is 120 maxLineLength: 160 @@ -65,6 +73,34 @@ empty-blocks: potential-bugs: ImplicitDefaultLocale: active: true + CastNullableToNonNullableType: + active: true + CastToNullableType: + active: true + Deprecation: + active: true + DontDowncastCollectionTypes: + active: true + ElseCaseInsteadOfExhaustiveWhen: + active: true + ExitOutsideMain: + active: true + ImplicitUnitReturnType: + active: true + allowExplicitReturnType: false + MissingPackageDeclaration: + active: true + excludes: ['**/*.kts'] + NullCheckOnMutableProperty: + active: true + NullableToStringCall: + active: true + PropertyUsedBeforeDeclaration: + active: true + UnconditionalJumpStatementInLoop: + active: true + UnnecessaryNotNullCheck: + active: true exceptions: TooGenericExceptionCaught: @@ -77,6 +113,8 @@ exceptions: active: true InstanceOfCheckForException: active: true + ObjectExtendsThrowable: + active: true complexity: TooManyFunctions: @@ -102,10 +140,20 @@ naming: FunctionNaming: active: true ignoreAnnotated: ['Composable'] + LambdaParameterNaming: + active: true + NonBooleanPropertyPrefixedWithIs: + active: true + VariableMaxLength: + active: true performance: SpreadOperator: active: false + CouldBeSequence: + active: true + UnnecessaryPartOfBinaryExpression: + active: true # Note: all rules for `comments` are disabled by default, but I put them here to be aware of their existence comments: From 6fff373613b50b67836e5be46c2e15f05973bf38 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 11:43:26 +0200 Subject: [PATCH 128/251] Enable detekt rules `OptionalUnit` and fix existing issues. --- .../kotlin/io/element/android/appnav/LoggedInFlowNode.kt | 6 +++--- .../io/element/android/appnav/room/RoomLoadedFlowNode.kt | 4 ++-- .../android/features/invitelist/impl/InviteListPresenter.kt | 3 +-- .../element/android/features/messages/impl/MessagesView.kt | 3 ++- tools/detekt/detekt.yml | 2 ++ 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 03ac045158..a32b860006 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -100,13 +100,13 @@ class LoggedInFlowNode @AssistedInject constructor( ) { interface Callback : Plugin { - fun onOpenBugReport() = Unit + fun onOpenBugReport() } interface LifecycleCallback : NodeLifecycleCallback { - fun onFlowCreated(identifier: String, client: MatrixClient) = Unit + fun onFlowCreated(identifier: String, client: MatrixClient) - fun onFlowReleased(identifier: String, client: MatrixClient) = Unit + fun onFlowReleased(identifier: String, client: MatrixClient) } data class Inputs( diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt index b3156bfc84..d00c4791f7 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomLoadedFlowNode.kt @@ -75,8 +75,8 @@ class RoomLoadedFlowNode @AssistedInject constructor( } interface LifecycleCallback : NodeLifecycleCallback { - fun onFlowCreated(identifier: String, room: MatrixRoom) = Unit - fun onFlowReleased(identifier: String, room: MatrixRoom) = Unit + fun onFlowCreated(identifier: String, room: MatrixRoom) + fun onFlowReleased(identifier: String, room: MatrixRoom) } data class Inputs( diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt index 21a57b48a7..bba497e2fd 100644 --- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt +++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt @@ -152,8 +152,7 @@ class InviteListPresenter @Inject constructor( client.getRoom(roomId)?.use { it.leave().getOrThrow() notificationDrawerManager.clearMembershipNotificationForRoom(client.sessionId, roomId) - } - Unit + }.let { } }.runCatchingUpdatingState(declinedAction) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index b68b0eea7a..6b67573b4f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -125,8 +125,9 @@ fun MessagesView( state.eventSink(MessagesEvents.ToggleReaction(emoji, event.eventId)) } - fun onMoreReactionsClicked(event: TimelineItem.Event): Unit = + fun onMoreReactionsClicked(event: TimelineItem.Event) { state.customReactionState.eventSink(CustomReactionEvents.UpdateSelectedEvent(event.eventId)) + } Scaffold( modifier = modifier, diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index c9e8aa395b..388076639f 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -54,6 +54,8 @@ style: active: true UseCheckOrError: active: true + OptionalUnit: + active: true coroutines: GlobalCoroutineUsage: From 15ac81d32a9f7f7c9246acfd47db7fe6447ca9f9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 11:47:50 +0200 Subject: [PATCH 129/251] Enable detekt rules `UnderscoresInNumericLiterals` and fix existing issues. --- .../rageshake/impl/crash/VectorUncaughtExceptionHandler.kt | 6 +++--- .../theme/components/previews/DatePickerPreview.kt | 2 +- .../TimelineEncryptedHistoryPostProcessorTest.kt | 2 +- .../push/impl/notifications/NotificationIdProviderTest.kt | 2 +- tools/detekt/detekt.yml | 4 ++++ 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/VectorUncaughtExceptionHandler.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/VectorUncaughtExceptionHandler.kt index a5e7edf405..b71c8af372 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/VectorUncaughtExceptionHandler.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/crash/VectorUncaughtExceptionHandler.kt @@ -62,9 +62,9 @@ class VectorUncaughtExceptionHandler( totalSize = info.totalMemory() usedSize = totalSize - freeSize } - append("usedSize " + usedSize / 1048576L + " MB\n") - append("freeSize " + freeSize / 1048576L + " MB\n") - append("totalSize " + totalSize / 1048576L + " MB\n") + append("usedSize " + usedSize / 1_048_576L + " MB\n") + append("freeSize " + freeSize / 1_048_576L + " MB\n") + append("totalSize " + totalSize / 1_048_576L + " MB\n") append("Thread: ") append(thread.name) append(", Exception: ") diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt index a422c5e729..685fe40302 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt @@ -44,7 +44,7 @@ internal fun DatePickerPreviewDark() { @Composable private fun ContentToPreview() { val state = rememberDatePickerState( - initialSelectedDateMillis = 1672578000000L, + initialSelectedDateMillis = 1_672_578_000_000L, ) AlertDialogContent( buttons = { /*TODO*/ }, diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt index 91f0bc1883..3270f6048f 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt @@ -27,7 +27,7 @@ import java.util.Date class TimelineEncryptedHistoryPostProcessorTest { - private val defaultLastLoginTimestamp = Date(1689061264L) + private val defaultLastLoginTimestamp = Date(1_689_061_264L) @Test fun `given an unencrypted room, nothing is done`() { diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationIdProviderTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationIdProviderTest.kt index 57f28e72db..b9664ef577 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationIdProviderTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationIdProviderTest.kt @@ -25,7 +25,7 @@ class NotificationIdProviderTest { @Test fun `test notification id provider`() { val sut = NotificationIdProvider() - val offsetForASessionId = 305410 + val offsetForASessionId = 305_410 assertThat(sut.getSummaryNotificationId(A_SESSION_ID)).isEqualTo(offsetForASessionId + 0) assertThat(sut.getRoomMessagesNotificationId(A_SESSION_ID)).isEqualTo(offsetForASessionId + 1) assertThat(sut.getRoomEventNotificationId(A_SESSION_ID)).isEqualTo(offsetForASessionId + 2) diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 388076639f..0675583bd8 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -56,6 +56,10 @@ style: active: true OptionalUnit: active: true + UnderscoresInNumericLiterals: + active: true + acceptableLength: 4 + allowNonStandardGrouping: false coroutines: GlobalCoroutineUsage: From 613cd4f863189101767fc4102f6a140fd0b35ba6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 11:50:58 +0200 Subject: [PATCH 130/251] Enable detekt rules `UseDataClass` and fix existing issues. --- .../android/libraries/designsystem/utils/LogCompositions.kt | 6 ++++-- tools/detekt/detekt.yml | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt index dcbef866fe..2618a5de82 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/LogCompositions.kt @@ -28,10 +28,12 @@ import timber.log.Timber @Composable fun LogCompositions(tag: String, msg: String) { if (BuildConfig.DEBUG) { - val ref = remember { Ref(0) } + val ref = remember { Ref() } SideEffect { ref.value++ } Timber.tag(tag).d("Compositions: $msg ${ref.value}") } } -class Ref(var value: Int) +private class Ref { + var value: Int = 0 +} diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 0675583bd8..fbecf65073 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -60,6 +60,9 @@ style: active: true acceptableLength: 4 allowNonStandardGrouping: false + UseDataClass: + active: true + allowVars: false coroutines: GlobalCoroutineUsage: From b69e01a5f343917900fb73b3ffc28a941ef3f821 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 12:11:52 +0200 Subject: [PATCH 131/251] Enable detekt rules `UseLet` and fix existing issues. --- .../matrix/ui/room/MatrixRoomMembers.kt | 11 +++-------- .../push/impl/push/DefaultPushHandler.kt | 18 +++++++++--------- tools/detekt/detekt.yml | 2 ++ 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt index 7995672e92..668b963bf1 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt @@ -57,14 +57,9 @@ fun MatrixRoom.getDirectRoomMember(roomMembersState: MatrixRoomMembersState): St val roomMembers = roomMembersState.roomMembers() return remember(roomMembersState) { derivedStateOf { - if (roomMembers == null) { - null - } else if (roomMembers.size == 2 && isDirect && isEncrypted) { - roomMembers.find { it.userId != this.sessionId } - } else { - null - } + roomMembers + ?.takeIf { it.size == 2 && isDirect && isEncrypted } + ?.find { it.userId != sessionId } } } } - diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt index aa1d0032e0..c3d68e52ac 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/DefaultPushHandler.kt @@ -100,15 +100,15 @@ class DefaultPushHandler @Inject constructor( } val clientSecret = pushData.clientSecret - val userId = if (clientSecret == null) { - // Should not happen. In this case, restore default session - null - } else { - // Get userId from client secret - pushClientSecret.getUserIdFromSecret(clientSecret) - } ?: run { - matrixAuthenticationService.getLatestSessionId() - } + // clientSecret should not be null. If this happens, restore default session + val userId = clientSecret + ?.let { + // Get userId from client secret + pushClientSecret.getUserIdFromSecret(clientSecret) + } + ?: run { + matrixAuthenticationService.getLatestSessionId() + } if (userId == null) { Timber.w("Unable to get a session") diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index fbecf65073..c7dc36ad74 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -63,6 +63,8 @@ style: UseDataClass: active: true allowVars: false + UseLet: + active: true coroutines: GlobalCoroutineUsage: From d22b005aa3e03f79b49c27dd340f8c5387d1faac Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 12:12:37 +0200 Subject: [PATCH 132/251] Enable more detekt rules. --- tools/detekt/detekt.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index c7dc36ad74..14193a1c70 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -56,15 +56,38 @@ style: active: true OptionalUnit: active: true + PreferToOverPairSyntax: + active: true + RedundantExplicitType: + active: true + TrailingWhitespace: + active: true + TrimMultilineRawString: + active: true + trimmingMethods: + - 'trimIndent' + - 'trimMargin' UnderscoresInNumericLiterals: active: true acceptableLength: 4 allowNonStandardGrouping: false + UnnecessaryAnnotationUseSiteTarget: + active: true + UnnecessaryBackticks: + active: true + UnnecessaryBracesAroundTrailingLambda: + active: true UseDataClass: active: true allowVars: false + UseEmptyCounterpart: + active: true + UseIfEmptyOrIfBlank: + active: true UseLet: active: true + UseSumOfInsteadOfFlatMapSize: + active: true coroutines: GlobalCoroutineUsage: From 2c2c23b3a176ba74d63d14bdb2403cda3f14e483 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 26 Jul 2023 12:22:41 +0200 Subject: [PATCH 133/251] Push to understand test failure in CI --- .../android/features/messages/MessagesPresenterTest.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index aeca219b68..a24e5f9ff8 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -490,11 +490,11 @@ class MessagesPresenterTest { }.test { val initialState = consumeItemsUntilTimeout().last() initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) - skipItems(1) - val loadingState = awaitItem() - assertThat(loadingState.inviteProgress.isLoading()).isTrue() - val newState = awaitItem() - assertThat(newState.inviteProgress.isFailure()).isTrue() + val remainingStates = consumeItemsUntilTimeout() + assertThat(remainingStates.size).isEqualTo(3) + assertThat(remainingStates[0].inviteProgress.isLoading()).isFalse() + assertThat(remainingStates[1].inviteProgress.isLoading()).isTrue() + assertThat(remainingStates[2].inviteProgress.isFailure()).isTrue() } } From c6e023b053910a4eb9960f8f2d281b2e45241d72 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 26 Jul 2023 13:07:11 +0200 Subject: [PATCH 134/251] Add consumeItemsUntilPredicate to check how it goes... --- .../features/messages/MessagesPresenterTest.kt | 14 +++++++++----- .../android/tests/testutils/ReceiveTurbine.kt | 17 ++++++++++++++++- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index a24e5f9ff8..b47ac55d35 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -65,6 +65,7 @@ import io.element.android.libraries.mediapickers.test.FakePickerProvider import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor import io.element.android.libraries.textcomposer.MessageComposerMode +import io.element.android.tests.testutils.consumeItemsUntilPredicate import io.element.android.tests.testutils.consumeItemsUntilTimeout import io.element.android.tests.testutils.testCoroutineDispatchers import io.mockk.mockk @@ -490,11 +491,14 @@ class MessagesPresenterTest { }.test { val initialState = consumeItemsUntilTimeout().last() initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) - val remainingStates = consumeItemsUntilTimeout() - assertThat(remainingStates.size).isEqualTo(3) - assertThat(remainingStates[0].inviteProgress.isLoading()).isFalse() - assertThat(remainingStates[1].inviteProgress.isLoading()).isTrue() - assertThat(remainingStates[2].inviteProgress.isFailure()).isTrue() + val loadingState = consumeItemsUntilPredicate { state -> + state.inviteProgress.isLoading() + }.last() + assertThat(loadingState.inviteProgress.isLoading()).isTrue() + val failureState = consumeItemsUntilPredicate { state -> + state.inviteProgress.isFailure() + }.last() + assertThat(failureState.inviteProgress.isFailure()).isTrue() } } diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt index e3f7b7139c..57aaa7dbfc 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt @@ -24,17 +24,32 @@ import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds /** - * Consume all items until timeout is reached waiting for an event. + * Consume all items until timeout is reached waiting for an event or we receive terminal event. * The timeout is applied for each event. * @return the list of consumed items. */ suspend fun ReceiveTurbine.consumeItemsUntilTimeout(timeout: Duration = 100.milliseconds): List { + return consumeItemsUntilPredicate(timeout) { false } +} + +/** + * Consume items until predicate is true, or timeout is reached waiting for an event, or we receive terminal event. + * The timeout is applied for each event. + * @return the list of consumed items. + */ +suspend fun ReceiveTurbine.consumeItemsUntilPredicate( + timeout: Duration = 100.milliseconds, + predicate: (T) -> Boolean, +): List { val items = ArrayList() tryOrNull { while (true) { when (val event = withTurbineTimeout(timeout) { awaitEvent() }) { is Event.Item -> { items.add(event.value) + if (predicate(event.value)) { + break + } } else -> break } From 9c1f9f47f2326258748da3694f4b6ad3a3fe01c2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 12:41:45 +0200 Subject: [PATCH 135/251] Make some composable private. --- .../android/features/messages/impl/MessagesView.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index c1187fdf7b..f909ac7f1a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -203,7 +203,7 @@ fun MessagesView( } @Composable -fun ReinviteDialog(state: MessagesState) { +private fun ReinviteDialog(state: MessagesState) { if (state.showReinvitePrompt) { ConfirmationDialog( title = stringResource(id = R.string.screen_room_invite_again_alert_title), @@ -240,7 +240,7 @@ private fun AttachmentStateView( } @Composable -fun MessagesViewContent( +private fun MessagesViewContent( state: MessagesState, onMessageClicked: (TimelineItem.Event) -> Unit, onUserDataClicked: (UserId) -> Unit, @@ -288,7 +288,7 @@ fun MessagesViewContent( @OptIn(ExperimentalMaterial3Api::class) @Composable -fun MessagesViewTopBar( +private fun MessagesViewTopBar( roomName: String?, roomAvatar: AvatarData?, modifier: Modifier = Modifier, @@ -320,7 +320,7 @@ fun MessagesViewTopBar( } @Composable -fun RoomAvatarAndNameRow( +private fun RoomAvatarAndNameRow( roomName: String, roomAvatar: AvatarData, modifier: Modifier = Modifier @@ -341,7 +341,7 @@ fun RoomAvatarAndNameRow( } @Composable -fun CantSendMessageBanner( +private fun CantSendMessageBanner( modifier: Modifier = Modifier, ) { Row( From e7f673c5bcc22d24b5b733e1651d394c009d5bd8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 13:58:04 +0200 Subject: [PATCH 136/251] Add missing preview. --- .../molecules/IconTitlePlaceholdersRowMolecule.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt index ec8636c759..1a10aa2886 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/IconTitlePlaceholdersRowMolecule.kt @@ -30,6 +30,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.atomic.atoms.PlaceholderAtom +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.preview.DayNightPreviews +import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.placeholderBackground import io.element.android.libraries.theme.ElementTheme @@ -57,3 +60,11 @@ fun IconTitlePlaceholdersRowMolecule( PlaceholderAtom(width = 45.dp, height = 7.dp) } } + +@DayNightPreviews +@Composable +internal fun IconTitlePlaceholdersRowMoleculePreview() = ElementPreview { + IconTitlePlaceholdersRowMolecule( + iconSize = AvatarSize.TimelineRoom.dp, + ) +} From c7d5baa3561d586217db1846760b897438e836fc Mon Sep 17 00:00:00 2001 From: ElementBot Date: Wed, 26 Jul 2023 12:12:39 +0000 Subject: [PATCH 137/251] Update screenshots --- ...PlaceholdersRowMoleculePreview-D_0_null,NEXUS_5,1.0,en].png | 3 +++ ...PlaceholdersRowMoleculePreview-N_1_null,NEXUS_5,1.0,en].png | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_IconTitlePlaceholdersRowMoleculePreview-D_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_IconTitlePlaceholdersRowMoleculePreview-N_1_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_IconTitlePlaceholdersRowMoleculePreview-D_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_IconTitlePlaceholdersRowMoleculePreview-D_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..08f5a24c49 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_IconTitlePlaceholdersRowMoleculePreview-D_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ebb2d33b0f8aa473b9deb44e57df4edec1346f164098eec87163ebd5c8db9ebd +size 5314 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_IconTitlePlaceholdersRowMoleculePreview-N_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_IconTitlePlaceholdersRowMoleculePreview-N_1_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..52309e0c28 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_IconTitlePlaceholdersRowMoleculePreview-N_1_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0b71348d0a316569c389682f5904c2d5b67180bd6c4ae13112d81afdb6cdada6 +size 5273 From 2c1521d8a4d0352babce7f392d9fbf03a117a1b6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 14:37:16 +0200 Subject: [PATCH 138/251] [README] Rename "ElementX" to "Element X" --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e31acf87b8..52acc405ba 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ # element-x-android -ElementX Android is a [Matrix](https://matrix.org/) Android Client provided by [Element](https://element.io/). This app is currently in a pre-alpha release stage with only basic functionality. +Element X Android is a [Matrix](https://matrix.org/) Android Client provided by [Element](https://element.io/). This app is currently in a pre-alpha release stage with only basic functionality. The application is a total rewrite of [Element-Android](https://github.com/vector-im/element-android) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running Android 6+. The UI layer is written using Jetpack compose. @@ -33,7 +33,7 @@ Here are some early screenshots of the application: ## Rust SDK -ElementX leverages the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) through an FFI layer that the final client can directly import and use. +Element X leverages the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) through an FFI layer that the final client can directly import and use. We're doing this as a way to share code between platforms and while we've seen promising results it's still in the experimental stage and bound to change. @@ -54,7 +54,7 @@ Makes sure to select the `app` configuration when building (as we also have samp ## Support -When you are experiencing an issue on ElementX Android, please first search in [GitHub issues](https://github.com/vector-im/element-x-android/issues) +When you are experiencing an issue on Element X Android, please first search in [GitHub issues](https://github.com/vector-im/element-x-android/issues) and then in [#element-android:matrix.org](https://matrix.to/#/#element-android:matrix.org). If after your research you still have a question, ask at [#element-android:matrix.org](https://matrix.to/#/#element-android:matrix.org). Otherwise feel free to create a GitHub issue if you encounter a bug or a crash, by explaining clearly in detail what happened. You can also perform bug reporting (Rageshake) from the Element application by shaking your phone or going to the application settings. This is especially recommended when you encounter a crash. From 6239c6dabc5d6abf9b2c1bb4279d808ec3098a1d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 14:44:29 +0200 Subject: [PATCH 139/251] [README] Link to the #element-x-android Matrix room --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 52acc405ba..f9c89fa859 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=vector-im_element-x-android&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=vector-im_element-x-android&metric=bugs)](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android) [![codecov](https://codecov.io/github/vector-im/element-x-android/branch/develop/graph/badge.svg?token=ecwvia7amV)](https://codecov.io/github/vector-im/element-x-android) -[![Element Android Matrix room #element-android:matrix.org](https://img.shields.io/matrix/element-android:matrix.org.svg?label=%23element-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-android:matrix.org) +[![Element X_Android Matrix room #element-x-android:matrix.org](https://img.shields.io/matrix/element-x-android:matrix.org.svg?label=%23element-x-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-x-android:matrix.org) [![Weblate](https://translate.element.io/widgets/element-android/-/svg-badge.svg)](https://translate.element.io/engage/element-android/?utm_source=widget) # element-x-android @@ -45,7 +45,7 @@ This project is in work in progress. The app does not cover yet all functionalit Please see our [contribution guide](CONTRIBUTING.md). -Come chat with the community in the dedicated Matrix [room](https://matrix.to/#/#element-android:matrix.org). +Come chat with the community in the dedicated Matrix [room](https://matrix.to/#/#element-x-android:matrix.org). ## Build instructions @@ -55,8 +55,8 @@ Makes sure to select the `app` configuration when building (as we also have samp ## Support When you are experiencing an issue on Element X Android, please first search in [GitHub issues](https://github.com/vector-im/element-x-android/issues) -and then in [#element-android:matrix.org](https://matrix.to/#/#element-android:matrix.org). -If after your research you still have a question, ask at [#element-android:matrix.org](https://matrix.to/#/#element-android:matrix.org). Otherwise feel free to create a GitHub issue if you encounter a bug or a crash, by explaining clearly in detail what happened. You can also perform bug reporting (Rageshake) from the Element application by shaking your phone or going to the application settings. This is especially recommended when you encounter a crash. +and then in [#element-x-android:matrix.org](https://matrix.to/#/#element-x-android:matrix.org). +If after your research you still have a question, ask at [#element-x-android:matrix.org](https://matrix.to/#/#element-x-android:matrix.org). Otherwise feel free to create a GitHub issue if you encounter a bug or a crash, by explaining clearly in detail what happened. You can also perform bug reporting (Rageshake) from the Element application by shaking your phone or going to the application settings. This is especially recommended when you encounter a crash. ## Copyright & License From f2bde1b948049e1aca261c190d8c048c8d661319 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 14:45:44 +0200 Subject: [PATCH 140/251] [README] Rageshake is disabled by default now. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f9c89fa859..02574a1c47 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Makes sure to select the `app` configuration when building (as we also have samp When you are experiencing an issue on Element X Android, please first search in [GitHub issues](https://github.com/vector-im/element-x-android/issues) and then in [#element-x-android:matrix.org](https://matrix.to/#/#element-x-android:matrix.org). -If after your research you still have a question, ask at [#element-x-android:matrix.org](https://matrix.to/#/#element-x-android:matrix.org). Otherwise feel free to create a GitHub issue if you encounter a bug or a crash, by explaining clearly in detail what happened. You can also perform bug reporting (Rageshake) from the Element application by shaking your phone or going to the application settings. This is especially recommended when you encounter a crash. +If after your research you still have a question, ask at [#element-x-android:matrix.org](https://matrix.to/#/#element-x-android:matrix.org). Otherwise feel free to create a GitHub issue if you encounter a bug or a crash, by explaining clearly in detail what happened. You can also perform bug reporting from the application settings. This is especially recommended when you encounter a crash. ## Copyright & License From 8450a23e892e51eedc0fab4b948739e9661f383b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 15:10:20 +0200 Subject: [PATCH 141/251] [README] Change translation shield from Weblate to Localazy. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 02574a1c47..d02006b944 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=vector-im_element-x-android&metric=bugs)](https://sonarcloud.io/summary/new_code?id=vector-im_element-x-android) [![codecov](https://codecov.io/github/vector-im/element-x-android/branch/develop/graph/badge.svg?token=ecwvia7amV)](https://codecov.io/github/vector-im/element-x-android) [![Element X_Android Matrix room #element-x-android:matrix.org](https://img.shields.io/matrix/element-x-android:matrix.org.svg?label=%23element-x-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-x-android:matrix.org) -[![Weblate](https://translate.element.io/widgets/element-android/-/svg-badge.svg)](https://translate.element.io/engage/element-android/?utm_source=widget) +[![Localazy](https://img.shields.io/endpoint?url=https%3A%2F%2Fconnect.localazy.com%2Fstatus%2Felement%2Fdata%3Fcontent%3Dall%26title%3Dlocalazy%26logo%3Dtrue)](https://localazy.com/p/element) # element-x-android From eac0d767822e3a44e540acd87694018febaccbca Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 15:12:58 +0200 Subject: [PATCH 142/251] Add Localazy badge to the Localazy readme. --- tools/localazy/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/localazy/README.md b/tools/localazy/README.md index c1ad1d0ee0..b0b5c7e980 100644 --- a/tools/localazy/README.md +++ b/tools/localazy/README.md @@ -16,6 +16,8 @@ Localazy is used to host the source strings and their translations. ## Localazy project +[![Localazy](https://img.shields.io/endpoint?url=https%3A%2F%2Fconnect.localazy.com%2Fstatus%2Felement%2Fdata%3Fcontent%3Dall%26title%3Dlocalazy%26logo%3Dtrue)](https://localazy.com/p/element) + To add new strings, or to translate existing strings, go the the Localazy project: [https://localazy.com/p/element](https://localazy.com/p/element). Please follow the key naming rules (see below). Never edit manually the files `localazy.xml` or `translations.xml`!. From 91186f5bd8e6dc40dc1f0764a961ea06902e857b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 15:13:55 +0200 Subject: [PATCH 143/251] [README] Fix small typo. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d02006b944..3d0c68fe22 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ # element-x-android -Element X Android is a [Matrix](https://matrix.org/) Android Client provided by [Element](https://element.io/). This app is currently in a pre-alpha release stage with only basic functionality. +Element X Android is a [Matrix](https://matrix.org/) Android Client provided by [element.io](https://element.io/). This app is currently in a pre-alpha release stage with only basic functionalities. The application is a total rewrite of [Element-Android](https://github.com/vector-im/element-android) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running Android 6+. The UI layer is written using Jetpack compose. From 2886b0f4461cc39e29ff407fd6d9bb469928c944 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 15:20:41 +0200 Subject: [PATCH 144/251] [README] Update Contributing section. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3d0c68fe22..aa8961a2b9 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,11 @@ This project is in work in progress. The app does not cover yet all functionalit ## Contributing -Please see our [contribution guide](CONTRIBUTING.md). +Want to get actively involved in the project? You're more than welcome! A good way to start is to check the issues that are labelled with the [good first issue](https://github.com/vector-im/element-x-android/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) label. Let us know by commenting the issue that you're starting working on it. -Come chat with the community in the dedicated Matrix [room](https://matrix.to/#/#element-x-android:matrix.org). +But first make sure to read our [contribution guide](CONTRIBUTING.md) first. + +You can also come chat with the community in the Matrix [room](https://matrix.to/#/#element-x-android:matrix.org) dedicated to the project. ## Build instructions From 7880fdbed9f4410d1f47876bcc1e84707a928841 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 15:24:54 +0200 Subject: [PATCH 145/251] Update doc about Push --- docs/_developer_onboarding.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_developer_onboarding.md b/docs/_developer_onboarding.md index 9198137577..d55840cc7c 100644 --- a/docs/_developer_onboarding.md +++ b/docs/_developer_onboarding.md @@ -313,7 +313,7 @@ suffix `Presenter`,states MUST have a suffix `State`, etc. Also we want to have ### Push -**Note** Firebase Push is not yet implemented on the project. +**Note** Firebase is implemented, but Unified Push is not yet fully implemented on the project, so this is not possible to choose this push provider in the app at the moment. Please see the dedicated [documentation](notifications.md) for more details. From f98db82e155ac163b88a40969ef95843ab74dde1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 15:25:19 +0200 Subject: [PATCH 146/251] Fix item indentation. --- docs/_developer_onboarding.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/_developer_onboarding.md b/docs/_developer_onboarding.md index d55840cc7c..ad5882abae 100644 --- a/docs/_developer_onboarding.md +++ b/docs/_developer_onboarding.md @@ -342,8 +342,7 @@ We have 3 tests frameworks in place, and this should be sufficient to guarantee file [TemplateView.kt](../features/template/src/main/kotlin/io/element/android/features/template/TemplateView.kt). We create PreviewProvider to provide different states. See for instance the file [TemplateStateProvider.kt](../features/template/src/main/kotlin/io/element/android/features/template/TemplateStateProvider.kt) - - Tests on presenter with [Molecule](https://github.com/cashapp/molecule) and [Turbine](https://github.com/cashapp/turbine). See in the template the - class [TemplatePresenterTests](../features/template/src/test/kotlin/io/element/android/features/template/TemplatePresenterTests.kt). +- Tests on presenter with [Molecule](https://github.com/cashapp/molecule) and [Turbine](https://github.com/cashapp/turbine). See in the template the class [TemplatePresenterTests](../features/template/src/test/kotlin/io/element/android/features/template/TemplatePresenterTests.kt). **Note** For now we want to avoid using class mocking (with library such as *mockk*), because this should be not necessary. We prefer to create Fake implementation of our interfaces. Mocking can be used to mock Android framework classes though, such as `Bitmap` for instance. From 5433e8eca1c491a9b646eb533b336a6ab2fe04ec Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 15:26:00 +0200 Subject: [PATCH 147/251] Rename "ElementX" to "Element X" --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f50c8f2a89..1a77970f9b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,7 +50,7 @@ Note: please make sure that the configuration is `app` and not `samples.minimal` ## Strings -The strings of the project are managed externally using [https://localazy.com](https://localazy.com) and shared with ElementX iOS. +The strings of the project are managed externally using [https://localazy.com](https://localazy.com) and shared with Element X iOS. ### I want to add new strings to the project @@ -64,7 +64,7 @@ Please note that the Localazy project is not open yet for external contributions To help translating, please go to [https://localazy.com/p/element](https://localazy.com/p/element). -- If you want to fix an issue with an English string, please open an issue on the github project of ElementX (Android or iOS). Only the core team can modify or add English strings. +- If you want to fix an issue with an English string, please open an issue on the github project of Element X (Android or iOS). Only the core team can modify or add English strings. - If you want to fix an issue in other languages, or add a missing translation, or even add a new language, please go to [https://localazy.com/p/element](https://localazy.com/p/element). More informations can be found [in this README.md](./tools/localazy/README.md). From c094f1ec9495e1beb733fe130c1a2464884eed00 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 15:26:45 +0200 Subject: [PATCH 148/251] Localazy project is now open for external contributions. --- CONTRIBUTING.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1a77970f9b..d9cec159e4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -60,14 +60,12 @@ Please follow the naming rules for the key. More details in [the dedicated secti ### I want to help translating Element -Please note that the Localazy project is not open yet for external contributions. - To help translating, please go to [https://localazy.com/p/element](https://localazy.com/p/element). - If you want to fix an issue with an English string, please open an issue on the github project of Element X (Android or iOS). Only the core team can modify or add English strings. - If you want to fix an issue in other languages, or add a missing translation, or even add a new language, please go to [https://localazy.com/p/element](https://localazy.com/p/element). -More informations can be found [in this README.md](./tools/localazy/README.md). +More information can be found [in this README.md](./tools/localazy/README.md). ## I want to submit a PR to fix an issue From 8188fad1e4b3f37d3f19a5a57cd10679283e3047 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 15:27:49 +0200 Subject: [PATCH 149/251] Update command to run (to also detect warnings) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d9cec159e4..3ca92c2d8a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -99,7 +99,7 @@ See https://github.com/twisted/towncrier#news-fragments if you need more details Make sure the following commands execute without any error:
-./gradlew check
+./tools/quality/check.sh
 
Some separate commands can also be run, see below. From ef7e3597187ea3376da514c89efa4ee4010d9199 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 26 Jul 2023 15:30:00 +0200 Subject: [PATCH 150/251] Add a paragraph about detekt. --- CONTRIBUTING.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3ca92c2d8a..7c3ca2740b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,6 +13,7 @@ * [Kotlin](#kotlin) * [Changelog](#changelog) * [Code quality](#code-quality) + * [detekt](#detekt) * [ktlint](#ktlint) * [knit](#knit) * [lint](#lint) @@ -104,6 +105,12 @@ Make sure the following commands execute without any error: Some separate commands can also be run, see below. +#### detekt + +
+./gradlew detekt
+
+ #### ktlint

From da79802c130353442bbe4332a940b2ec3b536587 Mon Sep 17 00:00:00 2001
From: Benoit Marty 
Date: Wed, 26 Jul 2023 15:36:37 +0200
Subject: [PATCH 151/251] Update section about Jetpack Compose

---
 CONTRIBUTING.md | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7c3ca2740b..86b22b88a0 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -171,7 +171,18 @@ For instance, when updating the image `src` of an ImageView, please also conside
 
 ### Jetpack Compose
 
-When adding or editing `@Composable`, make sure that you create a `@Preview` function, with suffix `Preview`. This will also create a UI test automatically.
+When adding or editing `@Composable`, make sure that you create an internal function annotated with `@DayNightPreviews`, with a name suffixed by `Preview`, and having `ElementPreview` as the root composable.
+
+Example:
+```kotlin
+@DayNightPreviews
+@Composable
+internal fun PinIconPreview() = ElementPreview {
+    PinIcon()
+}
+```
+
+This will allow to preview the composable in both light and dark mode in Android Studio. This will also automatically add UI tests. The GitHub action [Record screenshots](https://github.com/vector-im/element-x-android/actions/workflows/recordScreenshots.yml) has to be run to record the new screenshots. The PR reviewer can trigger this for you if you're not part of the core team. 
 
 ### Authors
 

From 1b9a71cc11092bf252bec1a92c12c785034d8010 Mon Sep 17 00:00:00 2001
From: Benoit Marty 
Date: Wed, 26 Jul 2023 15:38:20 +0200
Subject: [PATCH 152/251] Add a title for the table of content.

---
 README.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/README.md b/README.md
index aa8961a2b9..106425452b 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,8 @@ Element X Android is a [Matrix](https://matrix.org/) Android Client provided by
 
 The application is a total rewrite of [Element-Android](https://github.com/vector-im/element-android) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running Android 6+. The UI layer is written using Jetpack compose.
 
+## Table of contents
+
 
 
 * [Screenshots](#screenshots)

From 0c27f0ce4d1e16e298f87af7493d697bb2b0212c Mon Sep 17 00:00:00 2001
From: Benoit Marty 
Date: Wed, 26 Jul 2023 15:38:50 +0200
Subject: [PATCH 153/251] Update main title of the Readme.

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 106425452b..e4110fe963 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
 [![Element X_Android Matrix room #element-x-android:matrix.org](https://img.shields.io/matrix/element-x-android:matrix.org.svg?label=%23element-x-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-x-android:matrix.org)
 [![Localazy](https://img.shields.io/endpoint?url=https%3A%2F%2Fconnect.localazy.com%2Fstatus%2Felement%2Fdata%3Fcontent%3Dall%26title%3Dlocalazy%26logo%3Dtrue)](https://localazy.com/p/element)
 
-# element-x-android
+# Element X Android
 
 Element X Android is a [Matrix](https://matrix.org/) Android Client provided by [element.io](https://element.io/). This app is currently in a pre-alpha release stage with only basic functionalities.
 

From 990a4e416bfa90e05c3f8654322ab878c2451322 Mon Sep 17 00:00:00 2001
From: Benoit Marty 
Date: Wed, 26 Jul 2023 15:39:45 +0200
Subject: [PATCH 154/251] We are now API 23+

---
 CONTRIBUTING.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 86b22b88a0..d83826f9f1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -158,7 +158,7 @@ Make sure the following commands execute without any error:
 
 ### Tests
 
-Element X is currently supported on Android Lollipop (API 21+): please test your change on an Android device (or Android emulator) running with API 21. Many issues can happen (including crashes) on older devices.
+Element X is currently supported on Android Marshmallow (API 23+): please test your change on an Android device (or Android emulator) running with API 23. Many issues can happen (including crashes) on older devices.
 Also, if possible, please test your change on a real device. Testing on Android emulator may not be sufficient.
 
 You should consider adding Unit tests with your PR, and also integration tests (AndroidTest). Please refer to [this document](./docs/integration_tests.md) to install and run the integration test environment.

From 9673e3c21c7c353719e42c0654c6b5b5bb241547 Mon Sep 17 00:00:00 2001
From: Benoit Marty 
Date: Wed, 26 Jul 2023 15:43:03 +0200
Subject: [PATCH 155/251] Small rewrite of the intro

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index e4110fe963..508e954709 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
 
 Element X Android is a [Matrix](https://matrix.org/) Android Client provided by [element.io](https://element.io/). This app is currently in a pre-alpha release stage with only basic functionalities.
 
-The application is a total rewrite of [Element-Android](https://github.com/vector-im/element-android) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running Android 6+. The UI layer is written using Jetpack compose.
+The application is a total rewrite of [Element-Android](https://github.com/vector-im/element-android) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running Android 6+. The UI layer is written using [Jetpack Compose](https://developer.android.com/jetpack/compose), and the navigation is managed using [Appyx](https://github.com/bumble-tech/appyx).
 
 ## Table of contents
 

From f952f04e3c93bce34adc50c323aa2fb8ba12f43c Mon Sep 17 00:00:00 2001
From: Benoit Marty 
Date: Wed, 26 Jul 2023 15:45:19 +0200
Subject: [PATCH 156/251] Link to the status issue.

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 508e954709..87fdb668f2 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,7 @@ We're doing this as a way to share code between platforms and while we've seen p
 
 ## Status
 
-This project is in work in progress. The app does not cover yet all functionalities we expect.
+This project is in work in progress. The app does not cover yet all functionalities we expect. The list of supported features can be found in [this issue](https://github.com/vector-im/element-x-android/issues/911).
 
 ## Contributing
 

From a9d1a299f5ef7ad06b7c1b79cc831b0a219f181b Mon Sep 17 00:00:00 2001
From: Jorge Martin Espinosa 
Date: Wed, 26 Jul 2023 16:22:09 +0200
Subject: [PATCH 157/251] Prepare update to Rust SDK 0.1.36 (#966)

* Update to Rust SDK 0.1.36

* Cancel fetching members when the we exit the room
---
 .../components/TimelineItemEventRow.kt        | 24 ++++++----
 gradle/libs.versions.toml                     |  2 +-
 .../api/notification/NotificationData.kt      |  8 +---
 .../api/timeline/item/event/EventContent.kt   |  2 +-
 libraries/matrix/impl/build.gradle.kts        |  2 +-
 .../libraries/matrix/impl/RustMatrixClient.kt |  2 +-
 .../impl/notification/NotificationMapper.kt   | 47 +++++++++++++++----
 .../notification/RustNotificationService.kt   |  9 ++--
 ...melineEventToNotificationContentMapper.kt} | 20 ++++----
 .../impl/timeline/RustMatrixTimeline.kt       |  7 ++-
 .../timeline/item/event/EventMessageMapper.kt |  4 +-
 .../notifications/NotifiableEventResolver.kt  | 15 +++---
 .../notifications/RoomGroupMessageCreator.kt  |  2 +-
 .../model/NotifiableMessageEvent.kt           |  3 +-
 .../fixtures/NotifiableEventFixture.kt        |  3 +-
 settings.gradle.kts                           |  4 ++
 16 files changed, 99 insertions(+), 55 deletions(-)
 rename libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/{TimelineEventMapper.kt => TimelineEventToNotificationContentMapper.kt} (87%)

diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt
index a74b37efc5..4fb6e65797 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt
@@ -517,42 +517,46 @@ private fun ReplyToContent(
     }
 }
 
-private fun attachmentThumbnailInfoForInReplyTo(inReplyTo: InReplyTo.Ready) =
-    when (val type = inReplyTo.content.type) {
+private fun attachmentThumbnailInfoForInReplyTo(inReplyTo: InReplyTo.Ready): AttachmentThumbnailInfo? {
+    val messageContent = inReplyTo.content as? MessageContent ?: return null
+    return when (val type = messageContent.type) {
         is ImageMessageType -> AttachmentThumbnailInfo(
             thumbnailSource = type.info?.thumbnailSource,
-            textContent = inReplyTo.content.body,
+            textContent = messageContent.body,
             type = AttachmentThumbnailType.Image,
             blurHash = type.info?.blurhash,
         )
         is VideoMessageType -> AttachmentThumbnailInfo(
             thumbnailSource = type.info?.thumbnailSource,
-            textContent = inReplyTo.content.body,
+            textContent = messageContent.body,
             type = AttachmentThumbnailType.Video,
             blurHash = type.info?.blurhash,
         )
         is FileMessageType -> AttachmentThumbnailInfo(
             thumbnailSource = type.info?.thumbnailSource,
-            textContent = inReplyTo.content.body,
+            textContent = messageContent.body,
             type = AttachmentThumbnailType.File,
         )
         is LocationMessageType -> AttachmentThumbnailInfo(
-            textContent = inReplyTo.content.body,
+            textContent = messageContent.body,
             type = AttachmentThumbnailType.Location,
         )
         is AudioMessageType -> AttachmentThumbnailInfo(
-            textContent = inReplyTo.content.body,
+            textContent = messageContent.body,
             type = AttachmentThumbnailType.Audio,
         )
         else -> null
     }
+}
 
 @Composable
-private fun textForInReplyTo(inReplyTo: InReplyTo.Ready) =
-    when (inReplyTo.content.type) {
+private fun textForInReplyTo(inReplyTo: InReplyTo.Ready): String {
+    val messageContent = inReplyTo.content as? MessageContent ?: return ""
+    return when (messageContent.type) {
         is LocationMessageType -> stringResource(CommonStrings.common_shared_location)
-        else -> inReplyTo.content.body
+        else -> messageContent.body
     }
+}
 
 @Preview
 @Composable
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 499aeb29c8..6b54842015 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -145,7 +145,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
 appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
 molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" }
 timber = "com.jakewharton.timber:timber:5.0.1"
-matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.34"
+matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.36"
 sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
 sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" }
 sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt
index 4ded947d56..639509a15a 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt
@@ -23,7 +23,6 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipState
 import io.element.android.libraries.matrix.api.timeline.item.event.MessageType
 
 data class NotificationData(
-    val senderId: UserId,
     val eventId: EventId,
     val roomId: RoomId,
     val senderAvatarUrl: String?,
@@ -33,14 +32,10 @@ data class NotificationData(
     val isDirect: Boolean,
     val isEncrypted: Boolean,
     val isNoisy: Boolean,
-    val event: NotificationEvent,
-)
-
-data class NotificationEvent(
     val timestamp: Long,
     val content: NotificationContent,
     // For images for instance
-    val contentUrl: String?
+    val contentUrl: String?,
 )
 
 sealed interface NotificationContent {
@@ -61,6 +56,7 @@ sealed interface NotificationContent {
         ) : MessageLike
         object RoomEncrypted : MessageLike
         data class RoomMessage(
+            val senderId: UserId,
             val messageType: MessageType
         ) : MessageLike
         object RoomRedaction : MessageLike
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt
index 203fb30794..5ddc26a6fc 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt
@@ -44,7 +44,7 @@ sealed interface InReplyTo {
     /** The event details are available. */
     data class Ready(
         val eventId: EventId,
-        val content: MessageContent,
+        val content: EventContent,
         val senderId: UserId,
         val senderDisplayName: String?,
         val senderAvatarUrl: String?,
diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts
index 7786a3ee3f..112cc0777c 100644
--- a/libraries/matrix/impl/build.gradle.kts
+++ b/libraries/matrix/impl/build.gradle.kts
@@ -29,7 +29,7 @@ anvil {
 }
 
 dependencies {
-    // api(projects.libraries.rustsdk)
+//    api(projects.libraries.rustsdk)
     implementation(libs.matrix.sdk)
     implementation(projects.libraries.di)
     implementation(projects.libraries.androidutils)
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
index 6bfe37045f..305bcaaba3 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
@@ -103,7 +103,7 @@ class RustMatrixClient constructor(
         builder.finish()
     }
 
-    private val notificationService = RustNotificationService(notificationClient)
+    private val notificationService = RustNotificationService(sessionId, notificationClient, clock)
 
     private val isLoggingOut = AtomicBoolean(false)
 
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt
index 07acb7fec5..a5db095035 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt
@@ -19,19 +19,29 @@ package io.element.android.libraries.matrix.impl.notification
 import io.element.android.libraries.core.bool.orFalse
 import io.element.android.libraries.matrix.api.core.EventId
 import io.element.android.libraries.matrix.api.core.RoomId
-import io.element.android.libraries.matrix.api.core.UserId
+import io.element.android.libraries.matrix.api.core.SessionId
+import io.element.android.libraries.matrix.api.notification.NotificationContent
 import io.element.android.libraries.matrix.api.notification.NotificationData
+import io.element.android.libraries.matrix.api.room.RoomMembershipState
+import io.element.android.services.toolbox.api.systemclock.SystemClock
+import org.matrix.rustcomponents.sdk.NotificationEvent
 import org.matrix.rustcomponents.sdk.NotificationItem
 import org.matrix.rustcomponents.sdk.use
 
-class NotificationMapper {
-    private val timelineEventMapper = TimelineEventMapper()
+class NotificationMapper(
+    private val sessionId: SessionId,
+    private val clock: SystemClock,
+) {
+    private val notificationContentMapper = NotificationContentMapper(sessionId)
 
-    fun map(roomId: RoomId, notificationItem: NotificationItem): NotificationData {
+    fun map(
+        eventId: EventId,
+        roomId: RoomId,
+        notificationItem: NotificationItem
+    ): NotificationData {
         return notificationItem.use { item ->
             NotificationData(
-                senderId = UserId(item.event.senderId()),
-                eventId = EventId(item.event.eventId()),
+                eventId = eventId,
                 roomId = roomId,
                 senderAvatarUrl = item.senderInfo.avatarUrl,
                 senderDisplayName = item.senderInfo.displayName,
@@ -39,9 +49,30 @@ class NotificationMapper {
                 roomDisplayName = item.roomInfo.displayName,
                 isDirect = item.roomInfo.isDirect,
                 isEncrypted = item.roomInfo.isEncrypted.orFalse(),
-                isNoisy = item.isNoisy,
-                event = item.event.use { event -> timelineEventMapper.map(event) }
+                isNoisy = item.isNoisy.orFalse(),
+                timestamp = item.timestamp() ?: clock.epochMillis(),
+                content = item.event.use(notificationContentMapper::map),
+                contentUrl = null,
             )
         }
     }
 }
+
+class NotificationContentMapper(
+    private val sessionId: SessionId,
+) {
+    private val timelineEventToNotificationContentMapper = TimelineEventToNotificationContentMapper()
+
+    fun map(notificationEvent: NotificationEvent): NotificationContent =
+        when (notificationEvent) {
+            is NotificationEvent.Timeline -> timelineEventToNotificationContentMapper.map(notificationEvent.event)
+            is NotificationEvent.Invite -> NotificationContent.StateEvent.RoomMemberContent(
+                userId = sessionId.value,
+                membershipState = RoomMembershipState.INVITE,
+            )
+        }
+}
+
+private fun NotificationItem.timestamp(): Long? {
+    return (this.event as? NotificationEvent.Timeline)?.event?.timestamp()?.toLong()
+}
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt
index 92c996049e..2c4258da11 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt
@@ -21,13 +21,16 @@ import io.element.android.libraries.matrix.api.core.RoomId
 import io.element.android.libraries.matrix.api.core.SessionId
 import io.element.android.libraries.matrix.api.notification.NotificationData
 import io.element.android.libraries.matrix.api.notification.NotificationService
+import io.element.android.services.toolbox.api.systemclock.SystemClock
 import org.matrix.rustcomponents.sdk.NotificationClient
 import org.matrix.rustcomponents.sdk.use
 
 class RustNotificationService(
+    sessionId: SessionId,
     private val notificationClient: NotificationClient,
+    clock: SystemClock,
 ) : NotificationService {
-    private val notificationMapper: NotificationMapper = NotificationMapper()
+    private val notificationMapper: NotificationMapper = NotificationMapper(sessionId, clock)
 
     override fun getNotification(
         userId: SessionId,
@@ -36,9 +39,9 @@ class RustNotificationService(
         filterByPushRules: Boolean,
     ): Result {
         return runCatching {
-            val item = notificationClient.getNotification(roomId.value, eventId.value)
+            val item = notificationClient.getNotificationWithSlidingSync(roomId.value, eventId.value)
             item?.use {
-                notificationMapper.map(roomId, it)
+                notificationMapper.map(eventId, roomId, it)
             }
         }
     }
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt
similarity index 87%
rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt
rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt
index f7d4a00188..3121c9240d 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt
@@ -16,8 +16,8 @@
 
 package io.element.android.libraries.matrix.impl.notification
 
+import io.element.android.libraries.matrix.api.core.UserId
 import io.element.android.libraries.matrix.api.notification.NotificationContent
-import io.element.android.libraries.matrix.api.notification.NotificationEvent
 import io.element.android.libraries.matrix.impl.room.RoomMemberMapper
 import io.element.android.libraries.matrix.impl.timeline.item.event.EventMessageMapper
 import org.matrix.rustcomponents.sdk.MessageLikeEventContent
@@ -27,22 +27,18 @@ import org.matrix.rustcomponents.sdk.TimelineEventType
 import org.matrix.rustcomponents.sdk.use
 import javax.inject.Inject
 
-class TimelineEventMapper @Inject constructor() {
+class TimelineEventToNotificationContentMapper @Inject constructor() {
 
-    fun map(timelineEvent: TimelineEvent): NotificationEvent {
+    fun map(timelineEvent: TimelineEvent): NotificationContent {
         return timelineEvent.use {
-            NotificationEvent(
-                timestamp = it.timestamp().toLong(),
-                content = it.eventType().toContent(),
-                contentUrl = null // TODO it.eventType().toContentUrl(),
-            )
+            it.eventType().toContent(senderId = UserId(timelineEvent.senderId()))
         }
     }
 }
 
-private fun TimelineEventType.toContent(): NotificationContent {
+private fun TimelineEventType.toContent(senderId: UserId): NotificationContent {
     return when (this) {
-        is TimelineEventType.MessageLike -> content.toContent()
+        is TimelineEventType.MessageLike -> content.toContent(senderId)
         is TimelineEventType.State -> content.toContent()
     }
 }
@@ -75,7 +71,7 @@ private fun StateEventContent.toContent(): NotificationContent.StateEvent {
     }
 }
 
-private fun MessageLikeEventContent.toContent(): NotificationContent.MessageLike {
+private fun MessageLikeEventContent.toContent(senderId: UserId): NotificationContent.MessageLike {
     return use {
         when (it) {
             MessageLikeEventContent.CallAnswer -> NotificationContent.MessageLike.CallAnswer
@@ -92,7 +88,7 @@ private fun MessageLikeEventContent.toContent(): NotificationContent.MessageLike
             is MessageLikeEventContent.ReactionContent -> NotificationContent.MessageLike.ReactionContent(it.relatedEventId)
             MessageLikeEventContent.RoomEncrypted -> NotificationContent.MessageLike.RoomEncrypted
             is MessageLikeEventContent.RoomMessage -> {
-                NotificationContent.MessageLike.RoomMessage(EventMessageMapper().mapMessageType(it.messageType))
+                NotificationContent.MessageLike.RoomMessage(senderId, EventMessageMapper().mapMessageType(it.messageType))
             }
             MessageLikeEventContent.RoomRedaction -> NotificationContent.MessageLike.RoomRedaction
             MessageLikeEventContent.Sticker -> NotificationContent.MessageLike.Sticker
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt
index d556d8aab9..3997a492ac 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt
@@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelin
 import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper
 import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper
 import io.element.android.libraries.matrix.impl.timeline.postprocessor.TimelineEncryptedHistoryPostProcessor
+import io.element.android.libraries.matrix.impl.util.TaskHandleBag
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -102,6 +103,8 @@ class RustMatrixTimeline(
 
     init {
         Timber.d("Initialize timeline for room ${matrixRoom.roomId}")
+
+        val taskHandleBag = TaskHandleBag()
         roomCoroutineScope.launch(dispatcher) {
             innerRoom.timelineDiffFlow { initialList ->
                 postItems(initialList)
@@ -117,7 +120,9 @@ class RustMatrixTimeline(
                     postPaginationStatus(it)
                 }.launchIn(this)
 
-            fetchMembers()
+            taskHandleBag += fetchMembers().getOrNull()
+        }.invokeOnCompletion {
+            taskHandleBag.dispose()
         }
     }
 
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt
index 96b61fd558..330a06da62 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt
@@ -42,6 +42,8 @@ import org.matrix.rustcomponents.sdk.MessageType as RustMessageType
 
 class EventMessageMapper {
 
+    private val timelineEventContentMapper by lazy { TimelineEventContentMapper() }
+
     fun map(message: Message): MessageContent = message.use {
         val type = it.msgtype().use(this::mapMessageType)
         val inReplyToId = it.inReplyTo()?.eventId?.let(::EventId)
@@ -51,7 +53,7 @@ class EventMessageMapper {
                     val senderProfile = details.senderProfile as? ProfileDetails.Ready
                     InReplyTo.Ready(
                         eventId = inReplyToId!!,
-                        content = map(details.message),
+                        content = timelineEventContentMapper.map(details.content),
                         senderId = UserId(details.sender),
                         senderDisplayName = senderProfile?.displayName,
                         senderAvatarUrl = senderProfile?.avatarUrl,
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt
index 29ac866347..55395101a6 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt
@@ -22,6 +22,7 @@ import io.element.android.libraries.matrix.api.core.EventId
 import io.element.android.libraries.matrix.api.core.RoomId
 import io.element.android.libraries.matrix.api.core.SessionId
 import io.element.android.libraries.matrix.api.core.ThreadId
+import io.element.android.libraries.matrix.api.core.UserId
 import io.element.android.libraries.matrix.api.notification.NotificationContent
 import io.element.android.libraries.matrix.api.notification.NotificationData
 import io.element.android.libraries.matrix.api.room.RoomMembershipState
@@ -81,18 +82,18 @@ class NotifiableEventResolver @Inject constructor(
     }
 
     private fun NotificationData.asNotifiableEvent(userId: SessionId): NotifiableEvent? {
-        return when (val content = this.event.content) {
+        return when (val content = this.content) {
             is NotificationContent.MessageLike.RoomMessage -> {
                 buildNotifiableMessageEvent(
                     sessionId = userId,
+                    senderId = content.senderId,
                     roomId = roomId,
                     eventId = eventId,
                     noisy = isNoisy,
-                    timestamp = event.timestamp,
+                    timestamp = this.timestamp,
                     senderName = senderDisplayName,
-                    senderId = senderId.value,
                     body = descriptionFromMessageContent(content),
-                    imageUriString = event.contentUrl,
+                    imageUriString = this.contentUrl,
                     roomName = roomDisplayName,
                     roomIsDirect = isDirect,
                     roomAvatarPath = roomAvatarUrl,
@@ -109,7 +110,7 @@ class NotifiableEventResolver @Inject constructor(
                         canBeReplaced = true,
                         roomName = roomDisplayName,
                         noisy = isNoisy,
-                        timestamp = event.timestamp,
+                        timestamp = this.timestamp,
                         soundName = null,
                         isRedacted = false,
                         isUpdated = false,
@@ -177,6 +178,7 @@ class NotifiableEventResolver @Inject constructor(
 @Suppress("LongParameterList")
 private fun buildNotifiableMessageEvent(
     sessionId: SessionId,
+    senderId: UserId,
     roomId: RoomId,
     eventId: EventId,
     editedEventId: EventId? = null,
@@ -184,7 +186,6 @@ private fun buildNotifiableMessageEvent(
     noisy: Boolean,
     timestamp: Long,
     senderName: String?,
-    senderId: String?,
     body: String?,
     // We cannot use Uri? type here, as that could trigger a
     // NotSerializableException when persisting this to storage
@@ -202,6 +203,7 @@ private fun buildNotifiableMessageEvent(
     isUpdated: Boolean = false
 ) = NotifiableMessageEvent(
     sessionId = sessionId,
+    senderId = senderId,
     roomId = roomId,
     eventId = eventId,
     editedEventId = editedEventId,
@@ -209,7 +211,6 @@ private fun buildNotifiableMessageEvent(
     noisy = noisy,
     timestamp = timestamp,
     senderName = senderName,
-    senderId = senderId,
     body = body,
     imageUriString = imageUriString,
     threadId = threadId,
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt
index 989ba2ad09..dba58d165c 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt
@@ -108,7 +108,7 @@ class RoomGroupMessageCreator @Inject constructor(
                 Person.Builder()
                     .setName(event.senderName?.annotateForDebug(70))
                     .setIcon(bitmapLoader.getUserIcon(event.senderAvatarPath))
-                    .setKey(event.senderId)
+                    .setKey(event.senderId.value)
                     .build()
             }
             when {
diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt
index 57a3eb45aa..1b6bb8a67a 100644
--- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt
+++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt
@@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.core.EventId
 import io.element.android.libraries.matrix.api.core.RoomId
 import io.element.android.libraries.matrix.api.core.SessionId
 import io.element.android.libraries.matrix.api.core.ThreadId
+import io.element.android.libraries.matrix.api.core.UserId
 import io.element.android.libraries.matrix.api.timeline.item.event.EventType
 import io.element.android.services.appnavstate.api.AppNavigationState
 import io.element.android.services.appnavstate.api.currentRoomId
@@ -32,10 +33,10 @@ data class NotifiableMessageEvent(
     override val eventId: EventId,
     override val editedEventId: EventId?,
     override val canBeReplaced: Boolean,
+    val senderId: UserId,
     val noisy: Boolean,
     val timestamp: Long,
     val senderName: String?,
-    val senderId: String?,
     val body: String?,
     // We cannot use Uri? type here, as that could trigger a
     // NotSerializableException when persisting this to storage
diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt
index 9a998abf43..780d2abb71 100644
--- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt
+++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt
@@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.core.EventId
 import io.element.android.libraries.matrix.api.core.RoomId
 import io.element.android.libraries.matrix.api.core.SessionId
 import io.element.android.libraries.matrix.api.core.ThreadId
+import io.element.android.libraries.matrix.api.core.UserId
 import io.element.android.libraries.matrix.test.AN_EVENT_ID
 import io.element.android.libraries.matrix.test.A_ROOM_ID
 import io.element.android.libraries.matrix.test.A_SESSION_ID
@@ -84,7 +85,7 @@ fun aNotifiableMessageEvent(
     noisy = false,
     timestamp = 0,
     senderName = "sender-name",
-    senderId = "sending-id",
+    senderId = UserId("@sending-id:domain.com"),
     body = "message-body",
     roomId = roomId,
     threadId = threadId,
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 408c9e2934..ede03543d9 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -29,6 +29,10 @@ dependencyResolutionManagement {
     repositories {
         google()
         mavenCentral()
+        // To have immediate access to Rust SDK versions
+        maven {
+            url = URI("https://s01.oss.sonatype.org/content/repositories/releases")
+        }
         maven {
             url = URI("https://www.jitpack.io")
             content {

From 9bd97ad329d03206848aa3dfd64b868a98bc160a Mon Sep 17 00:00:00 2001
From: Jorge Martin Espinosa 
Date: Wed, 26 Jul 2023 16:31:26 +0200
Subject: [PATCH 158/251] Improve fetch members performance, relax regexes for
 validating ids (#964)

* Fetched `RoomMembers` are mapped in parallel

* Add horizontal padding to room/user name in room details screen

* Relax User & Event id regex matches

---------

Co-authored-by: ElementBot 
---
 .../features/roomdetails/impl/RoomDetailsView.kt      |  9 ++++++++-
 .../impl/members/details/RoomMemberDetailsView.kt     | 11 +++++++++--
 .../libraries/matrix/api/core/MatrixPatterns.kt       |  6 ++++--
 .../libraries/matrix/impl/room/RustMatrixRoom.kt      |  3 ++-
 ...ilsViewDarkPreview--3_3_null_0,NEXUS_5,1.0,en].png |  4 ++--
 ...ilsViewDarkPreview--3_3_null_1,NEXUS_5,1.0,en].png |  4 ++--
 ...ilsViewDarkPreview--3_3_null_2,NEXUS_5,1.0,en].png |  4 ++--
 ...ilsViewDarkPreview--3_3_null_3,NEXUS_5,1.0,en].png |  4 ++--
 ...ilsViewDarkPreview--3_3_null_4,NEXUS_5,1.0,en].png |  4 ++--
 ...ilsViewDarkPreview--3_3_null_5,NEXUS_5,1.0,en].png |  4 ++--
 ...lsViewLightPreview--2_2_null_0,NEXUS_5,1.0,en].png |  4 ++--
 ...lsViewLightPreview--2_2_null_1,NEXUS_5,1.0,en].png |  4 ++--
 ...lsViewLightPreview--2_2_null_2,NEXUS_5,1.0,en].png |  4 ++--
 ...lsViewLightPreview--2_2_null_3,NEXUS_5,1.0,en].png |  4 ++--
 ...lsViewLightPreview--2_2_null_4,NEXUS_5,1.0,en].png |  4 ++--
 ...lsViewLightPreview--2_2_null_5,NEXUS_5,1.0,en].png |  4 ++--
 ...DetailsDarkPreview--1_1_null_0,NEXUS_5,1.0,en].png |  4 ++--
 ...DetailsDarkPreview--1_1_null_1,NEXUS_5,1.0,en].png |  4 ++--
 ...DetailsDarkPreview--1_1_null_2,NEXUS_5,1.0,en].png |  4 ++--
 ...DetailsDarkPreview--1_1_null_3,NEXUS_5,1.0,en].png |  4 ++--
 ...DetailsDarkPreview--1_1_null_5,NEXUS_5,1.0,en].png |  4 ++--
 ...DetailsDarkPreview--1_1_null_6,NEXUS_5,1.0,en].png |  4 ++--
 ...DetailsDarkPreview--1_1_null_7,NEXUS_5,1.0,en].png |  4 ++--
 ...DetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png |  4 ++--
 ...etailsLightPreview--0_0_null_0,NEXUS_5,1.0,en].png |  4 ++--
 ...etailsLightPreview--0_0_null_1,NEXUS_5,1.0,en].png |  4 ++--
 ...etailsLightPreview--0_0_null_2,NEXUS_5,1.0,en].png |  4 ++--
 ...etailsLightPreview--0_0_null_3,NEXUS_5,1.0,en].png |  4 ++--
 ...etailsLightPreview--0_0_null_5,NEXUS_5,1.0,en].png |  4 ++--
 ...etailsLightPreview--0_0_null_6,NEXUS_5,1.0,en].png |  4 ++--
 ...etailsLightPreview--0_0_null_7,NEXUS_5,1.0,en].png |  4 ++--
 ...etailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png |  4 ++--
 32 files changed, 79 insertions(+), 62 deletions(-)

diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt
index 63868c8227..1e5d1a8ebb 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt
@@ -49,6 +49,7 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.tooling.preview.PreviewParameter
 import androidx.compose.ui.unit.dp
 import io.element.android.features.leaveroom.api.LeaveRoomView
@@ -236,7 +237,13 @@ internal fun RoomHeaderSection(
         Text(roomName, style = ElementTheme.typography.fontHeadingLgBold)
         if (roomAlias != null) {
             Spacer(modifier = Modifier.height(6.dp))
-            Text(roomAlias, style = ElementTheme.typography.fontBodyLgRegular, color = MaterialTheme.colorScheme.secondary)
+            Text(
+                text = roomAlias,
+                style = ElementTheme.typography.fontBodyLgRegular,
+                color = MaterialTheme.colorScheme.secondary,
+                modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
+                textAlign = TextAlign.Center,
+            )
         }
         Spacer(Modifier.height(32.dp))
     }
diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt
index 72b9d4c20c..fdf8a9d5a4 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsView.kt
@@ -39,6 +39,7 @@ import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.tooling.preview.PreviewParameter
 import androidx.compose.ui.unit.dp
 import io.element.android.features.roomdetails.impl.blockuser.BlockUserDialogs
@@ -118,10 +119,16 @@ internal fun RoomMemberHeaderSection(
         }
         Spacer(modifier = Modifier.height(24.dp))
         if (userName != null) {
-            Text(userName, style = ElementTheme.typography.fontHeadingLgBold)
+            Text(text = userName, style = ElementTheme.typography.fontHeadingLgBold)
             Spacer(modifier = Modifier.height(6.dp))
         }
-        Text(userId, style = ElementTheme.typography.fontBodyLgRegular, color = MaterialTheme.colorScheme.secondary)
+        Text(
+            text = userId,
+            style = ElementTheme.typography.fontBodyLgRegular,
+            color = MaterialTheme.colorScheme.secondary,
+            modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
+            textAlign = TextAlign.Center,
+        )
         Spacer(Modifier.height(40.dp))
     }
 }
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt
index bc0f0c04bc..a668448752 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt
@@ -30,7 +30,8 @@ object MatrixPatterns {
 
     // regex pattern to find matrix user ids in a string.
     // See https://matrix.org/docs/spec/appendices#historical-user-ids
-    private const val MATRIX_USER_IDENTIFIER_REGEX = "@[A-Z0-9\\x21-\\x39\\x3B-\\x7F]+$DOMAIN_REGEX"
+    // Sadly, we need to relax the regex pattern a bit as there already exist some ids that don't match the spec.
+    private const val MATRIX_USER_IDENTIFIER_REGEX = "^@.*?$DOMAIN_REGEX$"
     val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = MATRIX_USER_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
 
     // regex pattern to find room ids in a string.
@@ -42,7 +43,8 @@ object MatrixPatterns {
     private val PATTERN_CONTAIN_MATRIX_ALIAS = MATRIX_ROOM_ALIAS_REGEX.toRegex(RegexOption.IGNORE_CASE)
 
     // regex pattern to find message ids in a string.
-    private const val MATRIX_EVENT_IDENTIFIER_REGEX = "\\$[A-Z0-9]+$DOMAIN_REGEX"
+    // Sadly, we need to relax the regex pattern a bit as there already exist some ids that don't match the spec.
+    private const val MATRIX_EVENT_IDENTIFIER_REGEX = "^\\$.+$DOMAIN_REGEX$"
     private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER = MATRIX_EVENT_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE)
 
     // regex pattern to find message ids in a string.
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
index 89e83b58c6..8c2eecce8a 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
@@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.room
 
 import io.element.android.libraries.core.coroutine.CoroutineDispatchers
 import io.element.android.libraries.core.coroutine.childScope
+import io.element.android.libraries.core.coroutine.parallelMap
 import io.element.android.libraries.matrix.api.core.EventId
 import io.element.android.libraries.matrix.api.core.ProgressCallback
 import io.element.android.libraries.matrix.api.core.RoomId
@@ -168,7 +169,7 @@ class RustMatrixRoom(
         val currentMembers = currentState.roomMembers()
         _membersStateFlow.value = MatrixRoomMembersState.Pending(prevRoomMembers = currentMembers)
         runCatching {
-            innerRoom.members().map(RoomMemberMapper::map)
+            innerRoom.members().parallelMap(RoomMemberMapper::map)
         }.map {
             _membersStateFlow.value = MatrixRoomMembersState.Ready(it)
         }.onFailure {
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_0,NEXUS_5,1.0,en].png
index 7c5af2c6d3..e7a9157488 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:0e61170e31a1fb6d697e08c73e13dec40e88759f880a5c1720788a3b231800d7
-size 19587
+oid sha256:b32936346c597afcdc11ed72650e8651f77a5e2e3db88e12ae95bf65961c415b
+size 19662
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_1,NEXUS_5,1.0,en].png
index 0e2b1f9c70..47fea63910 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:37722b8d9c417f8b1a22083dcf9f1d48b166e39c64f6e11713b6f85b6182ce6f
-size 17510
+oid sha256:a9143534f5405494e1cc2c0335d0652460d4a29aafb9efd1e1f6940c5917f281
+size 17542
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_2,NEXUS_5,1.0,en].png
index 1eea369e16..ba77e9e73e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:d979a957761c00848ca0ef3b115b5787d5a26eee5e4d4db6ba5b837c1f05be77
-size 20035
+oid sha256:5834c50a2faef01976abcf85b9d1800f4dd94fb02684fa2951d5367c326831f8
+size 20114
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_3,NEXUS_5,1.0,en].png
index 7c5af2c6d3..e7a9157488 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:0e61170e31a1fb6d697e08c73e13dec40e88759f880a5c1720788a3b231800d7
-size 19587
+oid sha256:b32936346c597afcdc11ed72650e8651f77a5e2e3db88e12ae95bf65961c415b
+size 19662
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_4,NEXUS_5,1.0,en].png
index 7c5af2c6d3..e7a9157488 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:0e61170e31a1fb6d697e08c73e13dec40e88759f880a5c1720788a3b231800d7
-size 19587
+oid sha256:b32936346c597afcdc11ed72650e8651f77a5e2e3db88e12ae95bf65961c415b
+size 19662
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_5,NEXUS_5,1.0,en].png
index 50217fd9f2..71459b1241 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewDarkPreview--3_3_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:c7ca068387cff8faf728a989488e7c4b5b07983c4b24162ff82a14fd90b82d05
-size 20609
+oid sha256:213318b5c5556a6667b9a48701f738e7c55a40c763f3bd79df05e321b1fdc74a
+size 20684
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_0,NEXUS_5,1.0,en].png
index 10c5bccfea..ab29e8c5cf 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:57a16092b5e13be5f76f4cc42cf3e0c2bad983695a6b761fdb400a885c239ddb
-size 20078
+oid sha256:db61e15a906b09def466314727c21809c8019863ba4365e9bca54bca1f00bcfd
+size 20100
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_1,NEXUS_5,1.0,en].png
index 044efd7841..b02bda6284 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:802a6878cd7eaabdccea21f1d430f0e498ef7470ca963c6b2b869913d2a2d0a9
-size 17859
+oid sha256:755af35da6c6e3238201b016c8de7af8e5adba70904ab99f3320406490753b41
+size 17932
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_2,NEXUS_5,1.0,en].png
index ef2f9cde70..6a4762181e 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f1e93f5807f4a1dad7be55a918f2bcbdfb9ea1b95140bab26005812c3e363e1b
-size 20558
+oid sha256:bf0402f33d86d5b112c1ba6ae50f9135d87ba6fcad1f73add6eed56ae520b4ee
+size 20579
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_3,NEXUS_5,1.0,en].png
index 10c5bccfea..ab29e8c5cf 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:57a16092b5e13be5f76f4cc42cf3e0c2bad983695a6b761fdb400a885c239ddb
-size 20078
+oid sha256:db61e15a906b09def466314727c21809c8019863ba4365e9bca54bca1f00bcfd
+size 20100
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_4,NEXUS_5,1.0,en].png
index 10c5bccfea..ab29e8c5cf 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_4,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_4,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:57a16092b5e13be5f76f4cc42cf3e0c2bad983695a6b761fdb400a885c239ddb
-size 20078
+oid sha256:db61e15a906b09def466314727c21809c8019863ba4365e9bca54bca1f00bcfd
+size 20100
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_5,NEXUS_5,1.0,en].png
index cab17c4d00..0306b6170d 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members.details_null_DefaultGroup_RoomMemberDetailsViewLightPreview--2_2_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:e1599be0c5a37083c015378ee47a78a90dcf670f4df5d85dd25d39f4b2edbdf6
-size 21147
+oid sha256:edd5b6edd1e7b3217ae5502758de3c65333244c308ae1280a61d99e10c3438c5
+size 21167
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_0,NEXUS_5,1.0,en].png
index 41e47678d2..575ae56660 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:41972465c065b84fe047cf59394a7e83c036ef21b2eed5e2ec8c8e57f76a1408
-size 53943
+oid sha256:34d2fd15e0a0d432b9e9399e69ae14e899676a7534f5860242d7fa7ec091b1ef
+size 54000
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_1,NEXUS_5,1.0,en].png
index e4cc063245..7929b4f76d 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:a744e1072897c73906d1e87931586c9124b73c066e0fa4ab7d2c5d41d4abb344
-size 45347
+oid sha256:ebe4c5e10556936d62f1545e84605d9daf9ca3de3cef3d3f0dbe9bb02f1cf1b1
+size 45415
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_2,NEXUS_5,1.0,en].png
index 13164ca1d8..80960d8c10 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:9b0d8fcf96f22d9f7c4d8b417c2dfa8956b46866c0123ad365122c3080d4ac25
-size 46231
+oid sha256:fb0b6c742691a788fb44896f738b936dba95c6c16eefbb9bd593aa6e6bfe4480
+size 46309
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_3,NEXUS_5,1.0,en].png
index 190dfb7155..542f79349c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:c134e38cf600120bf43d0329042c6b24ccd2fa25bd48c70c028606a8598dd546
-size 48409
+oid sha256:71a7a8ac71a073a6f4ce65b7ad3a4a2a35dd1512d7ecc4cdbf53c9d52be4dba1
+size 48463
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_5,NEXUS_5,1.0,en].png
index 85a3e18440..361ab0f9dc 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:855bd0ec680f2e8f53d7e186c2f6228839f39c6e7b56ce3361f6b71740584ccf
-size 60275
+oid sha256:e73465a898e07b1c0d78aa87a771b574d50f536655dd63209426b79e1156efcf
+size 60356
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_6,NEXUS_5,1.0,en].png
index 85a3e18440..361ab0f9dc 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:855bd0ec680f2e8f53d7e186c2f6228839f39c6e7b56ce3361f6b71740584ccf
-size 60275
+oid sha256:e73465a898e07b1c0d78aa87a771b574d50f536655dd63209426b79e1156efcf
+size 60356
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_7,NEXUS_5,1.0,en].png
index ec3d5c8824..13872ec88b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_7,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_7,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:368f31f5485fe14f49b727c05328027ed7026bc6ec466f0c08d013832abfa084
-size 49190
+oid sha256:a8ea686919da2a33f9ecde5efb70ac9c434a24c28c30138f425acc56350a5590
+size 49227
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png
index 8d253ef56a..0c4c60a45c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:6e7796a991784b30ec4117d304d7eecc09777019703bba1eb97deacf175f566c
-size 54205
+oid sha256:c90d81b34d6876b7d4406adce0a775dc67b48da91eac6b4a814eaae9a7b028c2
+size 54262
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_0,NEXUS_5,1.0,en].png
index 10ddb50653..dec71d834c 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_0,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_0,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:f1a1489ff6a9e2929209aac79d57b4ec8868e7a17d4830bfac4cc764a1af4714
-size 56054
+oid sha256:34ca7db5dc561482c657b7617740848aafdbd144fe755276d8cf92f5dbccf077
+size 56119
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_1,NEXUS_5,1.0,en].png
index 0955f6d69b..9765798cd8 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_1,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_1,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:7862b2b99c0879ec4fda3904795cecb87a7beb6dc317635c4efcaf815a7eb6b4
-size 47428
+oid sha256:bd706136ddd94395eb8587e068578d281f5a4d20908121e4584bf98459533299
+size 47485
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_2,NEXUS_5,1.0,en].png
index 0ec49ca16f..7394a4c8bc 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_2,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_2,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:7e22a7534c46b166706ea0161f57b1b826d31e2a6ed971afcec7878049e820f8
-size 48394
+oid sha256:7007f5a568a31a9580ceea6c694dd3311066ef22c76eaf50e5e62476b48ae31f
+size 48465
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_3,NEXUS_5,1.0,en].png
index 3e8ac11ef0..4a1333ff1a 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_3,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_3,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:34862731fdb1ae399e38ba2f45fef6a976241652b1188ee66e3d8be4c5fb49d1
-size 49668
+oid sha256:c8d76d954e33c53fd9d658e8cef59ca7c2ffbd0f51322572a99e8e74803d9a9c
+size 49731
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_5,NEXUS_5,1.0,en].png
index 1800fdf719..8b6ea82885 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_5,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_5,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:10b32d81962ba03e246ee359ce64fec133345459e5bafc9976f8c9e1f2409960
-size 62345
+oid sha256:613eb68e1d88b3374c5b23884f5c992c548e636bc958e215cf3e5fec34186a88
+size 62364
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_6,NEXUS_5,1.0,en].png
index 1800fdf719..8b6ea82885 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_6,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_6,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:10b32d81962ba03e246ee359ce64fec133345459e5bafc9976f8c9e1f2409960
-size 62345
+oid sha256:613eb68e1d88b3374c5b23884f5c992c548e636bc958e215cf3e5fec34186a88
+size 62364
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_7,NEXUS_5,1.0,en].png
index ad0685df73..f514a29a5b 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_7,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_7,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:297ea8debad4e91466865615e197b51b4ef719fbba45d26993f4a7d82e6c9a85
-size 50504
+oid sha256:977d10c74342d1d25ce19add8793cca2b4d4b400881163e58c5f8298308e29e7
+size 50563
diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png
index 240dbb251e..494bf134d4 100644
--- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png
+++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:0b062c4ff23b014d270fa414cef3870aa47eba6c5096075c1508000d16631d8e
-size 56292
+oid sha256:0e1999174751655dd87b042f1ea22f281cbf9b5deecc494f05307f125d6e9dac
+size 56358

From 00f2a0bd847de9d2613e77da5cd83ae35d004ed0 Mon Sep 17 00:00:00 2001
From: Benoit Marty 
Date: Wed, 26 Jul 2023 17:15:51 +0200
Subject: [PATCH 159/251] Update screenshots of the application

---
 .gitattributes                     |   1 +
 README.md                          |  17 ++++++++++++++++-
 docs/images-lfs/screen_1_dark.png  |   3 +++
 docs/images-lfs/screen_1_light.png |   3 +++
 docs/images-lfs/screen_2_dark.png  |   3 +++
 docs/images-lfs/screen_2_light.png |   3 +++
 docs/images-lfs/screen_3_dark.png  |   3 +++
 docs/images-lfs/screen_3_light.png |   3 +++
 docs/images-lfs/screen_4_dark.png  |   3 +++
 docs/images-lfs/screen_4_light.png |   3 +++
 docs/images/screen1.png            | Bin 128145 -> 0 bytes
 docs/images/screen2.png            | Bin 65911 -> 0 bytes
 docs/images/screen3.png            | Bin 325181 -> 0 bytes
 docs/images/screen4.png            | Bin 210218 -> 0 bytes
 14 files changed, 41 insertions(+), 1 deletion(-)
 create mode 100644 docs/images-lfs/screen_1_dark.png
 create mode 100644 docs/images-lfs/screen_1_light.png
 create mode 100644 docs/images-lfs/screen_2_dark.png
 create mode 100644 docs/images-lfs/screen_2_light.png
 create mode 100644 docs/images-lfs/screen_3_dark.png
 create mode 100644 docs/images-lfs/screen_3_light.png
 create mode 100644 docs/images-lfs/screen_4_dark.png
 create mode 100644 docs/images-lfs/screen_4_light.png
 delete mode 100644 docs/images/screen1.png
 delete mode 100644 docs/images/screen2.png
 delete mode 100644 docs/images/screen3.png
 delete mode 100644 docs/images/screen4.png

diff --git a/.gitattributes b/.gitattributes
index 0542767eff..2062142284 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,2 @@
 **/snapshots/**/*.png filter=lfs diff=lfs merge=lfs -text
+**/docs/images-lfs/*.png filter=lfs diff=lfs merge=lfs -text
diff --git a/README.md b/README.md
index 87fdb668f2..f924ac168a 100644
--- a/README.md
+++ b/README.md
@@ -30,8 +30,23 @@ The application is a total rewrite of [Element-Android](https://github.com/vecto
 
 Here are some early screenshots of the application:
 
-|||||
+
+
+|||||
 |-|-|-|-|
+|||||
 
 ## Rust SDK
 
diff --git a/docs/images-lfs/screen_1_dark.png b/docs/images-lfs/screen_1_dark.png
new file mode 100644
index 0000000000..8bdcd59305
--- /dev/null
+++ b/docs/images-lfs/screen_1_dark.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4515f7589c422197a82672cdc3e64814ccca9a9a022b806facee44dd67d51ff2
+size 1116864
diff --git a/docs/images-lfs/screen_1_light.png b/docs/images-lfs/screen_1_light.png
new file mode 100644
index 0000000000..8eba38af82
--- /dev/null
+++ b/docs/images-lfs/screen_1_light.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2afb667a8b679f4395c28407b014f074e8b74745f6ecdd6ad699a6650cee2bc7
+size 771160
diff --git a/docs/images-lfs/screen_2_dark.png b/docs/images-lfs/screen_2_dark.png
new file mode 100644
index 0000000000..9a0102940f
--- /dev/null
+++ b/docs/images-lfs/screen_2_dark.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:910f9ab58a197a16b1295cd6b65d406ebfff1298c9c128a326edf1ec834d7fa7
+size 332936
diff --git a/docs/images-lfs/screen_2_light.png b/docs/images-lfs/screen_2_light.png
new file mode 100644
index 0000000000..1dd3106e5c
--- /dev/null
+++ b/docs/images-lfs/screen_2_light.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f7b80b9124c5c04c9db3824b68ab35883d41d05918abd415f7565f87d2713cfa
+size 338455
diff --git a/docs/images-lfs/screen_3_dark.png b/docs/images-lfs/screen_3_dark.png
new file mode 100644
index 0000000000..ccc17333e7
--- /dev/null
+++ b/docs/images-lfs/screen_3_dark.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dadff79ae955ab1da5c0a0567960fc567526ad0d8d2ea418d8adacf3a7a400cc
+size 243201
diff --git a/docs/images-lfs/screen_3_light.png b/docs/images-lfs/screen_3_light.png
new file mode 100644
index 0000000000..2116f1dc4c
--- /dev/null
+++ b/docs/images-lfs/screen_3_light.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e2f328b3e8bf2ebe4abc4c130e28f6fc2351f5ac339be0819e5fddb657f4a560
+size 246731
diff --git a/docs/images-lfs/screen_4_dark.png b/docs/images-lfs/screen_4_dark.png
new file mode 100644
index 0000000000..5bd122a9ea
--- /dev/null
+++ b/docs/images-lfs/screen_4_dark.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:519fb54f0070833b2a4671e1f0fc7b2ec60930d69b592b8a760f52a73dc4fe38
+size 132247
diff --git a/docs/images-lfs/screen_4_light.png b/docs/images-lfs/screen_4_light.png
new file mode 100644
index 0000000000..ee82f3be0a
--- /dev/null
+++ b/docs/images-lfs/screen_4_light.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f683a228d7168c75d6f42c353c1a0e169cb22cb8fa6696e9e3ffeb80854b2441
+size 131610
diff --git a/docs/images/screen1.png b/docs/images/screen1.png
deleted file mode 100644
index 9f9d7747ff34512382cab30c1800a9bd7a26ce6b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 128145
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�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�Y
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
R2W}TIJ$?RE(Fy0@{~vpnLG%Cs

diff --git a/docs/images/screen3.png b/docs/images/screen3.png
deleted file mode 100644
index 3edb49d08651cc8fbc1d356f0051ab67e0fc0593..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 325181
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μ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	<
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
z
zp8f@l&mQy$cr5Yc~~((kWp1dUOiP~{5gUl
z)}uJc!IcrskLi5eAoDtXGMyK7m_F7|mp!W-7gM*D1N)38sQ-E!6F1{xv50y9y_mKl
zr}4Okr0kDhYc7Lic<~2+PU*FZ-SrIz$w~xky6im#$4!?+rJE70{V{zcqdHg+eX6dz
z6#IX@m(b8@A{9Y!$$^lQ@jucbviykP|9ByJSZOc5S>L)};dRD-w6m^#dkwJ^Je8(t
zJr9oCw--xP#3%kCjb=-bV$35UZf8cM=OWBY4@?eZaPkX}H3d
z?8BdtF5}NcZyy{9l9$;Tr!$g`zuhOHHJVS(1}MSG`J`!FnEO!8dfH8+l@}d1W-EmC
z-*p`@TuNqUePtZYkqu@CO3kD>^@v~aSS}|CFytH)t85YJIge_rXG&hkKh23I%cs76
zjr)8mG^@4L1dEQmBs(k#>C*Vxy(u=I2vUU*9-ybSPBR~P0Uw{co*6DgT7~=S?KeLn
z^z_W2>p9zLsY1JN-_4q8U5^9Z+Ps<$aYm1|K3TC`sSZH}gjr2rtAyX#zCd?e97!jJ
z3YZ9z!Pl+&hoc`Z0E#4@GDgT8Ze4PCmAi;SUvnO!ypdj&%l7sQvjkhynZlsVn*cCQ
zc(tf?L|JZ@_mA&pNg}Fa@=$xF2whRcN^LLR#z#?dn#)uSS$hr3v^yR}sq5W;?}B^A
z*}TK`@llYq6E;5#Uge*=w0$Svr6I)3kcIh{gM+LO@^T;yhulV*DcFL!G7WgXp#%Yo
zzJx^rG43^rMxbc=UgbbWde}k-3#LISAv08^;E#ROLW>_@EnkF+$U|B>e%F%!FJ4oI
zBmOBW36hVwUpkFW8ESW(OofC5O?#=Px6TTcQHnk>-6v-0lXAm|Nj!wiYhV3ogw6)r
z_f%XJR4vkeLz=a2>n30#pIAwoVpqV(LGHO&}xL9Vb
z*$k2aA{XSF7mVHi9-gwNUz_^_GBiw=aPMlV(UlP6M%H+Rho(SpLvs(keUEpYGfx)?
zmQI`91RQT1F}n3wAD3-Mibiw9B@6Fm?Ka_7Nx}U@X~-rjoaa+Ge9Xv>1jbNpve=)B
zQYyZLlEr2eExuCPg~ln<#kQTB)S^}eFDp-s{WYRT{nE)YJ
zh4nM>4jvyWlRLc$IjQ+g1W;-2lwOmmV9BS%nW^TiR>d`J$60-N7nk_6R7d58C!6IP
zp8LgI>9npmcO3&C!uKb`d3stUqc@TUIxYx&rt7yq;h+unNj+1U09YPS%QYp7K??NF
z36=)KM7Z+ahxzin=$Qr(xg*RX@wtPxDh-BM%B!rFZ|=S(xj#4DQZZU%OU~M-!5jOk
zYQUoV)lwNey3|;mObdJoH`B|w>c7)=LRZ29+tHu6Y-@pcYFfi9Qa{r-pFI&N(ti!Y
z+@VJenAUsYpp11kIIhjj@oxtcxW)7XGkXE{*`NJox$
ztF;So%#jqB(6q6!-%)&H83F!#aK0?$v->RcuSTnV+<82{sl}YDV@cG#6H^0QPbZo*9It{R0wc0k%O6*}BMQB10hTME?+d5=sO5KSloiH8=~NQ~meJwA$?`vM
zky?|X5_(e!;oFuN@)c=?sTHM#&CF}cDGo+~2PvM@PNtS+7^|6$s(G;~VUW;T+Dn}GRHP4Vb
z>@Bx7dyM!_!p;<*k?mAA?*+3&#vhDa*_!w2=0VXhPmzAwBxMZow-j{e44*UzM%@3D
zKkerNnCoH+=CUqy>Q#+|>Wuxp)L0QuOqs@ot7LfRP8dhkE>)0-`J?3BfEpK8k}9Ft
zPqP#|jje6g-2MVz*2)IEhIrd?i^hIBq3;XEVv&nKF}5(ChYG|mLOU+v#toD`z)dEu
z#p2Yl+8zf3!0%JKt(w5FRozRmbp&-H_8#>9gpv3k=X%4sgQL&@d$sQ=4UM@NW9C|O
zGfTx+zD?oQqL$gNR=)^;B8H#Acmh0Jhxz~LEd^EA!J<Dak+
z9e>T#+SwkL`-@id`r*W*)4zxc=+S}#0NgA(Uj>L`XddUcQj*mcxIB;lH+$7q>ut~L
z=?Nk7$;b5`*&D?p`-KSdKD62Ye7@x}9E*J?biz;u%{Ei;7W-!FYCxvQs_XeJP&@oe_i|P6(mq32#s+0WvzgA)H
zW59K(wIP?@c2GE0+Z2r;${J24@B1`mWaT1?SYp6HM%CvP6GIM}e(MCmk6weyG!#a>
zI#^YoPY<`Hi^o*ZY#eE-09WkrGJQBHFf!tZF`SuEL_3ZHrawisjS-ExY^doPOV&&z
zydAa8{0iZb1^`*AaCjLRG&F>V(2lFFNd8oj85!4_@^j6*aSL8iPh+p<(eOXFRO&;&
z6e-7f_!a`AZR1hBkU0_htGYbA4sRwmp9H^ey7JB;#Y$b`3K>?fqgT^y1TmcD4*Ypx
z+vySdLI4^2?%MezrafP=AC>=6<*Uv#fhuB29Ss3Z88s|Hp+xr6uwk^Sg&xP+ezIL;
z7%d-}Dl#r+ZgS8}B>YM?fL%Utl=-J-acMM;Tq;o%?Zmg!o|L{~Al)(W9W|GJTj~u@
zxJE}s2lRdF8>3v!VOK21^53}x`<&r^XxO+%37pvNu-T>QB7gNPsI8I<-0yzantn*P
zjlNC4O5<5gzhzDc3}ut*xUk;do_BJjghl^-SfJOMH)Q@w{{%DIPH1dC#k}Fj(>_5P
z%YAZq+m9r(xJ6x4(93iTd|pHuf{94T$GGPvi(!U{l?Qrwu_0)yy;NL_HuwCrG_1O5Y%;^wtnna(u
znHii+djMw&p|_$_ley@@yL4X>Fp1fkPWi#w;aMzgcY)x4xd?X$bDum5PW>=k7PNrj
z7NH+vzE4Npxh!hhWE(N`3k~xJ4;Ncnn5wr)=%6i%Ivf&u;m7D1-Clkse!XsR4Os%5EzCqh*T9X_z9ni=SoVi&$iYNR>+7-(?+8h?zML1`I=HlX(}!
z$y!$jF^LRdM-qy{iAv#K^*6+VcONN0hxr_zX&RR6|sfoJ))ZCq{sQFiswcp7pPZoC-%R>)-DPg9l!i66f
zbqy7n6Rs67vv~={d`&myN!j6pmntd~Lki)-lSUS#Unc}4hp9eHj~si&8&8xP7%yY=
z8meFL5`1>ykY@En#W?pSywUf^n7|+(yk3qk*hlloQ*4|%=D7F6)=@o=nc!pMv>fb^
zQz#@j-Z%}0HqpGstVOb4@jO^*`tm`EKaxE<-cj~Z&Xd@TDnQhIt0_+7`$bV4WkcjL
z>y)EP`)pM^(vNz2Av)`2m$Z~M(XXxj%ZB1kDE|=Y|B{BUU6;*o8=K`>bf-j{UiX$5
zXil)mb@_xjMQrc@czte5*gJuy4KPSD(?!PFrJZbgBE|WlRbK#-?Eja+E-r2`VteW#
zi+9-Tzna6@cQwqroj;twQMOgg7F;wME~H@>A)HnOR9GuL#+H
zUt%UHSq)9tQhITPjVaR4^|eNn_F?J5idku5>g9(w=rc1fNv;gcC#W?t7~615sJ%%O}G}*Ms`TtA|osROhvVrXn
zI-hs};71L%c;Q4J4o(vOK|($cwkP0FNv%p&UjEK=_k-zL;E)ffeVU2t3-gbOu_l`>
zOX|0+#0cw;illGwyHhKb#~eP
zDdoC{{K@b(SBywv?fmE!M|nS=4H^(iYD~lGmzC_ScQ6qfQuE(5ie=eIPll-^dW&@H
z4+flY{T%QOm?FW55)=r0w_FvnfWU~>JR8-STwYqQ3rJ@!;SRJa$vEZyD<}UV!|G$8
z!YsP_)9w89wUCxd!yi#T2j5K@@U$Aq^3*`0uSE@T?mp2A)a~NN3-#c&n~pb|_?F}M
zqXcQm|7-inPW1l(Pillam`Dcv@Qp$ah}cQA%xryE2C8txYkMf}^s(*=D$GFh!!UxUKRW0nD
zRtgOPk|pL0&_SN8l&35IxsjRCdxa389!}|L{U8|wfv94c-hs(!=qXPkm;75NSH}M6
zjjCM-%WE$jiR7$53eu=!gWWqe>apc6EI0M#yZYCyKkLx#d-DlKpp(kfls(?BP+1xZqN
zp3a<7CGt&F7{&+Nw+yS6w7x`w1fp^_biv~ZkwzEPZJR+0g{cm5
zo%ON@?hENyfjh_azn+2y+qJsW`|kV8JQ+uyDj%)W1>Sx7W%E@H?Rtq&IGK;_%j!|n
zU6@pKYF1jO7~B6Oh~8}-xw);-x%&n`@QF+BU7~=Lf^a;~XhtPECD&jZSyFy|5B*P<
zz_QPb&9Lb8R`c_9#I&Q~)U^-cba?>K_L?zD7?&U+meYx2=v!LgJa*!8URs7`@|_|g
zmu(A$Hg%%mPv#E_uKeN?%hHx9h-I9HG!^LN(azmq%9gKOLz-o=3-YmmvgFH{jY`gF6%7|Qq3N$@z=*J0`B_0}LX
z7nOHeswa^XwvqDH5aaWWMeXOmqiqj6y6Ml4j^0HuFo7^~l42U?69iCinD5&hzkLLS3N;f!ho)#g;$8$*oCwtj-;!vwx%+IBb4bj&
z^Qd#%6~EIKX;s7~95=B4ZfeFOoTE}-TB~zPdeHw6#Qf=K;l2?M8_kr;}u80rE{1dmP*
zjvck)6ovyAEpAw}+rKRD|?OX$2F_zZkI}^2JOgNwVQD~4}WA1cas2L4iGhn-I!|9`9CDJlJ!Ml
z^!9gY#qCV7e2KJPAzRIcP%{e=Bg#nX2TFvO$YP)uVtu=5he1LXlxp`XCxzj}<>VpC&@TMu+g^0v5H!FSIYEz|h;
z*gFZ73~O%xaN0dJbVti+35FiU7u^*UV+usRTM6|dthbx#-UFLwoClSZ|9SAuT>5eV
zLK5G87@1utf~LD-GLSpcnd;xGz^3qG(u0V@c6T$9>(}J8pXxh-ch`I)F6+Ru?Ed?z
z294j7zo$s1%!crUfw~EnfpBCmI+5?cYXtcYra~HXh$?neg%{lvhHIAH81cD0faX^}
z`}cv}_X^UhQEmX$Y6g;BhSLPx-}6SFzZ=#I7{xJ!jO&35*8)|Kg-s|&Z2b|1zE#QW
zXWRh1fl84lLO6#tBtLUJrlm58K_Tt(CPPgpVYk9s`T5trODPh7RTG#BH}b%t4i+PY
z8u*Zd4#)68X0f;;*A?m{C~-FIvFf{Q`OB+
zxQ8k6PKbHodXto8NtZ#Vu0>wu^;Y&pPAt|0?;bd|lf!$MF2>(gh+nyfoI#17qFT%*
zU3`DRG#@WXNh!*jr#&%N-XD9FH_?WgfKc4s@%)6)v^w~Y94gXu%6kK5C{P`ZoG02
z%lrY2P>|x~QHOHSw<%-hgSS%Jvc3=XHh*pRU$1Q(X(K3SXm{41OxYtF2ci34MaXgL_%k(ehjOHw=`P~QnJ0!VZ5K;(bn^}>#oYagHX6<
zOBnbNQ|kx_sEv&(6JlFW4j!MeyG(y*vmfNysE0^xw1T+*c%A4m
z`|X=-)SHnb{`nr<@zFslrP4w4tb878yPVv6OX!0UcV>Zgr@rG*a?mOvY|`)Y3naJq
z@xKft?jsJ!Rmr%JD5>}fHLbb(#}|8)Yw^dLHhT{DfN}Tef3~`(PjZD7Am5#5ZzWm%
zYXoW$Cf`w|;8~G$=xdc!-xM3W9gHD8vF6mA%x+wj&eE>e081F({H~!Q9FFbth23y0
zL8J~6^3aIzfJM*GrTTggQ5Nx1Mxpc2YT7R_Loq~_e-sy%5fh{H3zT3S#qOey?4e@b
zkBFWtCfS+!+D$LjFpDo~E;LuOZ-a1kQrdc?JH0Gl^_qU$44Z#A-Tt>=`weoR4M9uH
zQCu@}`oD0oI5{kNnM^Jk>iNuP%9noc2hBB5x#{=+(D(n%9XT5~Gc`=3)n~mgtGWMI
zERMnwXmVg1bJvbR
z=N<#FJsLF)TB3)qk_F{KIl`mG{Y8klR7OAL#mqA=j-4T{G04bh$PAiI?d9CQa&tIyF>BTay{ccMUKjaQY9~|!5oY1DYm)$ZI;{0;WMVj2*!%FIM
zcPC@yZg>WFa#qLy|=r4l(KScbG|<_zWp$)RjiW
zF`4b}72Y0%=|s9QNK05Gd!KBeo=D4A$2R09;H@{KA!-{Hj{zLiA9dKRppE#yNf+WT
zv%FM=XD~E8wqcRC73|Qqv}Ifc*UUg$!Z1ycB9($0Q1&Zpkp#YwmSFnP)2NHY5}I>R
zcr4i@p-z2n>)Jw2d#%1H1PnPH(Kevq$2kH3V(h)!;ny(UxrD`g_yniHiU_S&|zSPTHo@HMXF;OEZzgE?R_oj1NJ7vk$^`+H?VG`TjX?SQyZKL
z9ppUk^hV6KA>sjw;I+V+>xUon7)pSVjvl8X2Y3jV#_+Pq=%I1eKN`rI)2$i`|5CA2
zQEv9gvSDy9WQH6JT3WVa{0$=`m-X)w@@&<0c49FQOg8`z4Ga$C2uot2WzinM+SG}Ion4lf^^OX7ck
zBjM-VmvL4*U4@B&=eX;RVLvEq|3-hTs(}
zXDya_OrUCXpZGWBG@~qjnid5wwo=t>^GQAsqS$6mB*D#E04-MP#n^zfKwyW2y?$3ir!r-6PxQm#-uAjoyuMw~IHCaS5HaPjSNzsS2(60*kG82$S0
z6K)BHh@ksc9h!3EU>kH+ofutsOEX*3=(La5+Aeu;Q)3Xo`k^b??JS4BF1WC^%IkyU
z*wIn|B2drX!*tuKF-HM_lKy$?F4y4V*KeGGHIOKyn#kL^KXY>@OaxB(?J*P1wZ3x}
zvwj!zcEh#T?fUN2?%+`{2KE>j$_j@P-rN|F_=+fNYcJ&}#08{sNw}O@?Kgk*B8694
zS_oW9=jQQzIuh*(OfP+b(o_BHL@iI^)s;cC2VY*U=eQ`Y7BSsJ{tgT%!>jvF_T6=;@1;jHz$IBzW9GWvbXeiDD8~B~2@|lepeN
zGZAH>5;;xlNkrMV7Zv**_|NS?BaPlG|Dw1qLc5Y$(N)SMBl|TZv1-JDAEiPKH9dsT
zdiXO+OIfTKoeu8!}>p7=TwQ5OAY~Us!XwJKON0ZQ{^LDIOu~A
zky*zOq!>5U0FIg+w1njwl-DV!!;anYm19ymBfsPabvk%YLnzdI%U4c`Rl)L60(06+
zTFynx20ikVPL~Vnx{FTZ;G;th14eXcpWF(C(_fhIU4Z;D@!lYGrW80kHEk_G_&5}R
ztv4CvvX)X8Y3+tD>*Bt?gU$mFtq#YzPe}!zrkao^e@OMn*bR>nzmPH^`+7M;qcN0+
zJg#Nz3-$I-f01#EWq;ml2Lst~zZajev#|#%$kzPr6xd{L88I|fN>t?`sLq2dGE$!!YsrjHd<`wU-rOhoj2ElgAt$UV
zN2~BV2q0@Ur=RV;bQ}{rNf?r_!xtLJpeSaqT&if_ZUv(E}@^4~YITKfRll%}>>uq*R;2{{J$asCl>
zg#SqHgE;SkZuGZ*nd7W4bLIL3+4kj{P4NEJgMh;ajj@G1@OV7d=z~H?KTgQZOvC$F
z`VY|!eubl7XFx4#hZz=AvAMSkEUFz8XHf5m2HOk`@vmhb!JBt~XYks~ysRa?pi`9R
zF6pjPDqLjcT&Zbj^PTFNlKf{M{BW;9L$GhNT?=p|&Kq*m#4o~5T15q)JW3AI=Y%iL
zvc-^DPYmWY1+z2%H(F|se)*yU6Of;Pv)e!C=e2%(Agb7#t}oO|Jn}cXZ;XCMJSE1v
zi25vg8^mT1jEvua;fF;J8@DIy@xezCrvwVv1WCMH?N7%dcPm_VF`NrC#zD*F94G{e
z&7Gz?f?ghOj+mMLc>UNo3Y?+qN!hD;7~UJ6xM88@YP4$q@I3A$=gMFlSO}w#${MnG
z8n|j3vS_SD^vFZN{FMM`R3}42#VSOclG!BVn-X3(WHCUZiPeuwOkFRfY-+%T(2Z9+
z+^qXR>9x|#{u%h*CR6{t{W|_Vou1KD&%1kmQbYP>nD8-0QxB5J6{6Th9W~T^Rd#7w
z3Yub)IVkH!s95k^)0(nb&q8v^D)4a)O|{g;;g*04%Pu@#s?&+LU`f*?LCBcII;I;r
zrKW=n7fDgnISmWCqw>B*TF~kS|iLw`97U%GMb&p|L~zP
zx7qZUcDuycMQ3UkZ_TbhSVzPCVxu^q{N~58FtcnPJl~I~vwxtE>I(Gw?=BleGL3(#
zCg8woN{)Ni+(>T;ZKy=U&IN#=5O{$Pm2Fe7PSB-g3!iIwl_k9y-8f<`m9O{RuXOPX
z5lvz|zcHj;%Z&pEk*=vXS|sq@Dl|f*ob5uX+iVdTo6iGJNxcX4={}5H;;f8oNKI}P
zVygGTRcZdBs(CSVZF7O8vl|nem&54WD*A}Z{cBL85@CR-Iik^=OPg^qgrOPHxN*H;
zf6;@Y-sqEQ3-G76Uqr!6`WgG})(L^I3t5GpA6kWS7qT5$did)HJmaQo6OWsU(W_$@
zHlx9Ha>I%GX?FA9L^M#*jdXNBtfc~^Dz|m2G9BI_4^nsucN6VQH*t+4|1vkAQkf6X
z^z$!1glrIWaoOX1bR*4RbfGj?YF(FHbW$}*C15X`;CXDn&Fl=;HH#lj(+4$(ww#LD
z$?AA6fMDx^^HAH0%7psRco*FN28Gn2T@5Y1s};=e%{eF;M8DBRfkFaXO5ThPI-etY
zq-RvS0xYME{H-@3F9%WU&3zy$)y?vMd%?K7X#KGx^7{DkuJ^EBlnR&h$=?h@`;>l*
z0ze5C!BtX*MaM5c{_WFtpY+)n1!cgKdYyUGv3s)`18RCIHzl?FK!K(i(1TxvJ1ylq`$n+tBI<)%rNX9#NSxvA1Hy64oDZAf2ETWm(
zOr%(^BIQw2fm<*La@=~Xps&eJOWNZ{q9I^v5A9kcJP6Nxrt|tP?f+2pI+oAiT5b|J
zh;f6@A|W1&C~LKa*Il~tb82}ziXTp<;66~bOvv$bhhh)@WNkab)GwCZvNcq~?orBS
z2)Ct(8|$y_OBhaYyj1oqZ^50ChvB7!3y1UlyBJ?jGVtaVW{tNaces*4$W-ee@mvVL
z;n-j_ht(wA=S)6?$xquq?Qif3Zwi@aEr1`7doP?&4Inf%`t^9N_FF&iWa3O7Fg`iG
zfUE149pH3hQyrJ=;)$s3MIoK$`Rh$jvd3Rw+-f_KfuQVgxy)AM6>5c{fB=
zMMzbZnh`F}4n=`Nynm#;+?{uxLK-~DkI|5gUAFN|97)LoQaBQS@ZZtg%bAbL`A6CF
zvyiQfW5=6KOHOyr4#n(n`_^g6+D`LA`Cg8?Z`Pp+=H04RD>yWFM6u_y>g?v5Jbye7
zNtrz%1gZX)$V(&>!wnB{#<%02W=*eAKW7P|RbOOBH1(Tc&NXmxLSmttLg+@ry16_0
zhoECO0e1gou^){-B6Jsj`itx!j%O`LmoqmJ9H1Gj+P1vm$o_HUUM;_PWUFt8JmpSgrOY1ov_}u+(gzmE*iIqv-(18w_LG}+*
zqk5eN{nefNBez;x!@%jDKsUB-sGT(%->#Y!jSxA_@4QM*mz};3V5vyo0T`s&;ZMCM
z;Ea;4FP@jfzDLzG?j>_}+dVE{aDB7yVj?4>5aCF|n%?eOXT0`UA}yDY4?EDr?*lo?
zX6)#(bsxeD-I>xExkIbXZB}&egY&=Y@~z#9-3XZPt||GjpI&7Xw0iN{Z#})Eqkp>q
z&tOa5wV?pI!zrsqDT>gDqpeM9M!VP14d(%D_28L^^w&QV>22P`CFh=GA6v!R;?QK0^`IvxNCP^8kp
zBy6oxqW?B?Yb<#I-Enrk0&Zp&PNnWCBuI=j*d%p~+wyY`)28Up`5E{qj;W|qQ89i&
zb^MWsHZ2@c5>hMX8%-u_wMmRTz8#53in(4zoG5@Mx=J>Px!Jy_=OCjoHM{BlGT@!tGu^EZc%p_om+RY(&~A8iQjhh^M!!Vgk^1m
zojAKD08?4yP2^>imMxCt%dc7|#-HT0l$3y4f*p#EHk$JMv*)CP+%0dDF?M*rxxWKn
z5#-a5kfY=VoR+UB)C;`FEkq?HheKfFs2ZG(^qYvBS(r1>uzpmcov%(p^A7yfxjDNT
zk7&ZRwrk^7q|GY1nLwPheCKSWCExpd>uw{)ZKA)CT(u(
zZ!^0sQUqmHyG4EP6q-f(Wn>Cu2_S
z9Xa+;YB@G-WIYtQze!CRWi@XwXY@+cl}HpDuj1nr)D(FE34ppP4Z@%&iV%)O>O25M
z(toh3paLA9m9<^LT0(I%WHcZyi9$1EH6V{0pWLn5LjN1ZVkB!*f@iWi!k$(gmJSB8
zJ6-K-Kc9ALU(V@j7d`7;Epb%6Tz>fDck`ZqQV%MNB@!pa)*Wv*8Zf*s<3BVsdY`w-
z(&#k&qA=?PHPvJP>z+(KVPZTZLOHT%;{;C5GPyp(0uG=rq&sxmAHq1
z$ou<4*bXwuV&)xEzCvXI^nB;E-IU6YZ2*EFdQh$MC!&9h-i-!VjwOSnI3NgESWgkK
zamLQqwY~LsJ50weFLsfZD|ie*A8H->QQ=6tMN$Unvh(moh^kAUb`y~ZZgz=aWWUQd
z!?_X*r=uLL%bk8+j-$he%az&Ms}-5;y`Q!I{RbAgGPzz8>}O$vp5U|X%asU`om0*A
z*~j}Y$L$AJ3!N^Mb2T|AE;l{w8a5wnUeE~|Z;`7YJ1!)TcWhmr*W`i^XP}kZXFqUe
zZvEK}`6XCOWT#fWYhPo*lwUcc)c5}U=}6NXg>eqf^zAhSoK~b32n_{=5JmNNpNB2q
zcAO1dWb@v;B>~&9R5gQumr1YDP#k2>r!|)(=(ERzBca_HHBjE!@sZ(hIMR>N?;yGx
z-Hb%>L&{%@ElfZ9TmF8Q%>6bIgxcTo0g7XnU=H0`JhlBb)Zk
zReYPzc4hj@TBr8Ytd{oeYua(ACpDPa2gNVdL1vM-gwpIXmYw?;R(F$C_Ck;#iF`W_
zZM0`7KVWL-ZvJg=Oyq56Jz#ogI^b>5fesh0d`C98K3l+}+{EcFg+dD;I+mntua=Y~
zKm1Gn(-(boQg5K`({W+^T{NaDa^f@c-2T__vh&rX5W97E#(;lu
z5`1uHfZ#efgPkk){hWHAx6b(lr+)h{in?kBuHL(M_gbsFP2&$3QVAD|*-{qqyI{BJ
zmiw>s*5~+w)=;Z{q=%=nJ@#I$KIXHQrB;3i0uoM*lHTDkfzCkZ@siPGf#_`1pIana{`lga3^U)kusYu4uv
zn0Kqq6SZhdDpFuF4K_N@RkI#*2=dwC54^h}zh5LE{AZap01ot@?_(?s9(eNf-`^6V
zNuq!io-}T!3G5mK*kH^-#gE{KAf@ueg78K@o`!kDJbuT{BfOrvqXzaG50?XiBpOK))dW506$x0
z18+478O{0=k{Je441JZ3RiwnXv1{J-DO|$&7TbZJh5oH?clDt!Q$}lJ>bPTLwj;1l
zHCvh*c6~+9{`g?%`^Rnb@v)m@^Sw65SjAZ0jp2<92aeyr4h*^p2xa3`614B&_@e;<
z=yhL#-qn=YHPSXL1|M^h-v6$vX7R-nWg8io_$+i1#0NpS!^^(gcFUIZzzWKJpmxyW
z;saxW?)gs27J@e1?!HQ19cKz%Us=Ss{E|?qN|bS@3gQ@3!}x>qkW5N({O^BPID~;h
zLq18l8PtJdArpub$GH?Wu+CClLqk20ZlgXZ=d5iS?SdIs&FMmQnP`R`;EgWx_?}AU
z-k+$9B@{kJ`NN}MW>EbeH|Bk|K^XO0m^{zp`fz5)N5u!7+(+UdPVH>zl5eLQlr$MN
z*k7w&N8;lLMhCIL{^QC?I)Qha!CZMlTCW_k
zyOrcC<8fEYx7Xm)L*nbrOkFP3i-0Ff%gPkRI0l99u$KYU6&%ebDN{75K2@Khpod?-)El#U9;DI(iwV<6)8Q
zKUy?7Xl;?x&*XC4^ZESETz2)7tkm>%xFB{l!={z9sviY)>h@RpSE9u-!T4z%2^~Qw
zT6Z2)Wm=D6LS*v|9Kec1BddL!{+E`?>$7$dyoqm_95B!@hC%gN!#G`CFNNx`#2
zHA8Vv+L;f_aMq#Q_Qlg95CehS=jtCX<}ewUKcLAO)jl@|+D&wv(HSeXPkgd9uC>{2
z^IcI%8}&-6tQ_?{c6c+k6zr0wcJ6>>KU?6dD{YeYU3rynU*K+Q_e7VI{l8cC8PJ3k
zM>d_HBVQZ+Cc9G@HL;E_M`=yOJKsi?4qb3?+@V6w-wTsw9&M+AEPwK?d{Q%sR2m~r
zU?Z&$JviZ3ArA@aJtM0R016hv<%ryDMrY%fF-RV`;@?ZaM%Yjn4uz+A4zCI)U&c;+`WoV(I8bzud!yH;
z_a}cI9VON;R2LK)bj#7C(Nqgkw0IiQK+UPs%8mb%e7b1%Wn`|RD2|cS_$S>2m!Spd
zTh8#q!D*1N53>=VW%#dUmHPsdW)d|b+{Qnt>!yJ?WD@Dx3<_CSzM`LzM9xW}GO5Y$f+P!+NOj};K$=zUH{+cp1iyttyO0W18|IEp?PMZu6491l$$$)
z!^(Z6U-?gWj9I|{ZBF)KF7^-`Dhla$=yr`HYb#01i4PW@oz1Hp`i+Pjs=J3L$bt0r
z!ReeB!b-QrdklSvn0;ZCS`^IkZRFtFf`qp|BMsyU>7)e%FGIVqzw+CDQLf6ART>(%
z;Z2{RHcJz)e+a|XY^EMR3*sUK9YqaLcN&j`L4&b30@;+1Hz>%1l6s;D;h=-7U*|O>
zVu?~dBz=!f=sfD!imjwOBbB}RpK7GApqa+O=ISh>Sx)0lJduYJ5^H{a6{vV<|Ft4~
z(OAID$n+-%rOLVu0kkIRA1$%NLd?xWG!4IZXKritoF$SY<)J4HVJ6J0A-leL#ZVMD
z@hvPg%98@#^IRH_uP*Q8Lf7)KDo@eI6;s?p4f@GW+0B3wQPQLvlKTw{#J!$e^IX~l
zXW*1^D&Q@#q;}k;cHCB9Pm`lRi8)T(Xm%hjQ(%V=UOa}M_FK^XFU%=3{oqo&2*;YV
ze5pnnmwD$_(`t=+3tVLb6h!T3C~v>zG3P9vkS~TzpYNgs4pEWCD<6vTb^T2iQ2D7LOO81)Ow;
zxsOCxJyMLmbggR7qi>gbRj1+dLi^9hH)7O?n
za314aVdVfhjtjzbbaOh%n}A91_^9DLuvcG1<&>MYqklKP*{Mk@7&2invqaY@q)#=w
zgxY-}g$U?%(CpwKjBj0cM_uQ?f4nu=9!ZsGf
zg&h{he-z`;X7)jOK($}S-xuUJ6S=6X=`;o~QB@8r&u@PH(72w&-r>jN=UU!ZB(oap
zU?3AZ>wmfNb9vxo_-9gQApED}(1>yeg~zNlI_Y=0yb2lfp1iKC1uq*=tk&-?&^1b(Xn4P2$N6Awt*N*W~R3z_O6$O{Q-oN
zy(1ajABPQ1ejVFh@@eN6dkXSXN+M7T59!VigCdEqR4C8i?+n*l@t4s+46=OMF=;bz
z!2Hj4Q`BDCRVv=I_*Pp4;Hw#|gzc%%|o%LXI*~Xj{^%T1#
zkc;h^d|zX_hLr_moTt5TwsKSc(Wl#Lk1e79y}G&OFlh+KVUv>JH-U~!orxBgMMEuk
z5<-L3zV2w^=`VYdZIt7hDRO4sotq2_WV{N+8xntM9!Q?c=|i#M*2=$isqWshJ54}A
zJkpL2qNnZmxY4%UDfJV!_=$weHB%r`hLJPxb*{rNkgkA56PjbZ=_s@aN2tyf*(3dksc8E5LsMVCckUqaV9nVe0S5CvdPvbndO&ju}Eu?LjcjHvd
zn`cdwrHNB)<4yd!mLhr3=Kmo?Dscdv_cp)y3#&uxjnFk
zmTBkBkiXLTckzc4fl7mqm~AKq*p)JpdMnmm^qe`i+Tuo%xpjXiLDrlh66XFA2pFzokRS4U!jHo(0Pr*vj5Yt9*(U+{+Pn-CzCGm
zbBC#z*)LEBUefn(a&#H%O3yRCj=NNn+_&>ViaE
zLPA$hFSM;qa?V+_zP=t-GeFmqDdceeN)Io6D1zJ~fKW_s$-WUmSzVfAj`z%i1Z)qrtl8O
z_HP(Dp~pcJyG3X%V~Tb~x|KKV8blBR)0!_)lAvG!vDqEOPOLFmQXLII^d
z^l3hVtq(A}J)$J8c8AokFJM#41Y|?PdC9g%7
zoui}v7-F__O)ct4TKU4ZjYNraGfhVGJb8I}1gC0IM~6(|Jy=g8!^P*P`SVI@w>AN5
zN$B;>SP&gv6pE`DEgI>l9Wxhr>HVx3;d|)^c#@5Cd}%6HMeyg1^R@LjzFfhoiaX7j
zuzjriZI!^s+tt~BPEy7!5GSHGv0P0^DY{M$!1|4Sg!6EkjIT5O$LnpS(7Ej>hAS1z
zfSKxEI2i{!Vo$6w}2Wa`o_fG2hO{_*0r_6yYJ
zi^8^bOEJHQv&vF5FBtU^uYEO3(Ta)LRilLItDf{_mJ5Y2DW)Dq#f!#fHFU1S+GzKf
znn{yzJ49`4@eb6!QsJ?db!yuc`GG2UpH>VR^DT*|c0oQvDol_p%CHUe4Nd<~?$hYT
zHI1}MLeNU!txNL@Xk#))Gg82AN&4S6>DC5EcWv1%1*Ukctv(#CT914dnOq1T^<3fb
zxjXXi=mA=Co@=tjXC`(<3lW0a&vK4;zA`7c1)gOQ9!7aIpy8RYn0btd5N@c1a~W3l
z79~l4anoC)krPCI8sx9Rs;@wsV*RyyxL^zPQwKQ;aLICe1*nObV3}jG4z9qwx$4`T
zlQ&G@E?gIyxw>6}mpo>1?ZbjN_wp=sZPg0sJH_MBY$s*?MA}4Cbd>LM-uBNU(}eS<
zU&b~ZV0rQt+^UGQ2ysGh@I`UP?U;)&q>Llx+Io36EYAhom2g7BTv_k!OXEsfe?L7C
zNgs;m3S(_$Hq)V*Av&*dNwu`EA+hw+cr`J>w9_ERMbPTgVHEp}MBR_)hhs-R&i8pm
z$Dai_x87~YSJiXQr`}$S(d-*e*6
zmicxutV9YKvvPv(E~^qGQq`f
zR~{dZAI)8-Qr**4rzy^}V>)H&B)l{%To(9EFrqv}(Gpu)#5uxu2l#1g16^Fcx4((-
zK>dwDbGO24QqNw9ck_!WX(ui(9VYoPElp{BL-l|+9N5*sod*|~^e~c@UAqe-0uc-c)SiBV13t3*gky`4MC<%-`Nmk(^fCgceoWB;}dkzqdpnX5O*sWU8WM0
z=rEwg#Kt@;WDqD)V0+u^;B9b&TJ+OUlyZp4mXZ-A&0b&&onD>I)GE{lBQdP%>A)|a
zXFSaj<;{*LqCu^cA!Yr+-cQP?eO6^+Cl~mY>V?4QO5hm?dSx*6gL|HjK8pF%6<1Jv
ztTtKxmr*cBy6DQ*q`-ae}B@u0F3vH
zG9)aYC9NG#EWu$ccbZ&^&mDk+6^HSk=TL~hO`Lqn0`67j9JNA2i#WqpgJqzaac7TH
z=*u0Ca3U~
zIJbQ^|E^BQo*Vi{IDOOYQETcJ(76}zb4cWzO7=Z}NDWOH!g%p;aDKJY;~e}BF3Nj2
zFJl^v{3WrZEpuPr&Lt0aci;SE2(&U^9}9Z$pxoa0F>BrfP)|$)ciq^0zbwmU&AiPN
zZU~w)Vxknb??)w$d%riXaN3CW5?i|h6KXy-{oWbiH#@Bp4AsqBv;CHw+1s^7yZE)T
zg|pC1%0V9Ha|a(_Lf|H+Da#$g<8d;4wwR0M8nM>1SU?3cg=!AMD)3OX17G1}-!cPt%CvATe
zVBz!+S15v;hjVc~J{2&`xd&e+=X6(v4LdI&#D-aVk$aFLDDZo&m6g2CSn9Kg>V
zjxw+*TtFP$#n|!TTIx?BdCX>t85PVkj4_2hwRS^{dnq_H={si6Ki}@}rwTii+9`8`
ziATP1B3hU?Y-m8>7Z!F{DVg5rz5Y7|QQ*TLvr1Fg@XJNFSwkTOL7NPYc*K;)SgsV}
z3uai?+lxa*n-(-Y``hO833Vm}3n23L<8bUnwwVlo$63UetLqJaj=?DY;0Ih%;Y^f<
zbrFd#-SDPq%E;7U(Md1!da$stu<8G9Ni@7F4H7PS?O&iwI?AYxKff@LpQ8LC&$@3X
zB_Rrxm3D~pU;uk=8Wh^R;dm=(Sn!Y{;)nXpN578rE}%eQ7RMx
zR%CQEyA~M;wJjqVNw0&Ax5@Mf4kKlc+ZeGTI`nF`zz%8SWV6Qfz8p^jnZ+}UZ4
zc!@!SmCTQzs~A~pMgopzl=$DhQi{z24rd@Z>H3OBVL~E3@-(P30v=3!>~DG!b8(&*
z^n)}<{j970yPxc{oNVlnB7b*&d5x;R$eCZk7}t?YOBZgXmtCp
zs(?){)l9s{b@GmA$2eza=PB}>(8d)%u0lN3$;aK$BXoqmbBr-k=lv
zEn*EnDEie`gWmuopUQSIjU&4zewX3eyqOmLoMqnC=XszO&>}Ap`0KXMn$MeAV{aJ2
z!&^(G8#JQG9k1gzdOZX@6E>KFoE&-luTQKXpPDif_V;e^`qlk6KS2Pm+p)7M*xMCW
zwEnxO3Z<$z|0X=Um9Rf+uo$BjE
zX}(+k)8D-|Mka-+te^NLD~jh09_cnnx@&Qy*2?i=pK^nyfwP
z;PXAvC7{N9f8Dqj$BN2pdf}2BZ$^L(
zjI?w88zlC5)_5J71njC?#9DsyEUWl@<&f*kT8NwCF22t2$|eJ^@IuwS2nd7L-=j<9
z@ct9Ddd)f1=NIjgfZE{+ZVKnh@8H=uKxDeAP_Nj2y&i`sjP$vfHiYLK2e1_;INEY&
z@w{&6t(Q|n-91Xhrh`Z@NAU7KYu=gdP-Yc$Thi#GmiV63^oyRJUfTqA{aZVbNH^-R
zA6bkDAHHh3F&;z8+}4?iPnog9Qd}(aoT(P?!!gzoh(hNyGmgG6vgRT91{X_yR(xZ
zTPwZYuiNMFN7&BZUV>v|LxZk`r>ra{4TIxSz*B;gr+v6dTajC&d~{bEngdRbcb*Ji
zc$~&t<2g(mo})iqmYUlRM|+x=3cN$au-9)bC(MPqOgym+DsT28lB`SyMai;GXqrB^
z5o**Aqxde?y@hcn4%sN5gI_<2peE6g`v(+(b`Hg3#LKr{9tJGr%_z;B|yhD(lCABN37?lQhQ8{J5<&v`*7?x|uCfTbrAx%$v7S*2x-0FWOM>E^k-W1!$+cWFVz<9*_*wSW7%J-~M&?h%1NL|^_=(C>e@
zM&lB=k<6%HDC>WbkszHwwqBoqGd~tc2KS3@>!HJ5I#=)z5M1v7+G49?mqa9p|b?z`hT0*qE
zrFLUdUZthp%xv}wU69T3yIfWY&`SMGw^5M(R_(Ggs2`#bUoaDRS4-AXK#^t1C3jxh4`v@48mI=ksu_?xt+7?adG$oZvDCV9Zn3RN!}7W
z;p2tHBPrb6*f_Hi%|;>X>F3|X(Dir4>M);&n{6fpV>7!JnQ+w>vYB>iM?4y@9L0%&
zOxo@T15NmZbd-(gt*}Y7a@`!J!#Ckp(_*$dU$J$-B1%W^;J)
z&Ky++w)iuj<5`K0Z8zVUruGGp|HlUMj!sVMYL3`7jg5w@wx|>CW>WNUV7Ob%d!wM<
ze#vYg+if$?YYP#i6-+aJ^CsfpYkp_iOXoUAA9!DSDa})~Z151Pxfni;53T6ydE3mV
z^cqV%U{6ntSjIJouxNuy$QCf!4di?JayTmk8wuM2mJjYaOD=8|LE~?HHdgdRo3X+v
zS|(Gz4?k%vB6tJ#r1cArqcnu~V+iAqT!ogMxleeD+qI(G
z7DppqNa|17CJ*`sU9UwkEin9b>JTW0$|ePoV!d>;(MA%(vD)tHMkpttg;!dEYE%0|
zBCUk&B4YNpCnn8schPZNvBi~hSdG#71_qmEZO85M@%oU(!@HM+>*oM*}kh3IP8Mll4aDHd?
z`+d=7sJ0SJ1)ZauCtf{P4cWokj)?s4*o!+{YFb`QIS)v
zmX#IeJp#i1!p_t=@=fZM3ntjRRfo5XQHQ;v7QY
zHrqsOC9^0a`=lf`XR)|(ujOH$Pm0{K`?9p@`iH{C)~c=~gGV3r6KlC<3%4ear?$z8
zq*{>=DXrTIt1A1vaCj;{%}{p1CvZzh?n#`7NzB=eUgl5gv8>>mya`9mB#^H{Q{7Jx
zH+BmSQLMun&w3S)luBzhBl*z`Lat;%8@afjYVmDcrXTjfq7LoEEUsaVd9vpBW>RB2
zeM(5RJ#tZJNkl{hqENpxPB_eFcRwPP+L?E*8KlOd_xxBni7vr0>W>^7ncnkA1*<8C
z9as7KsSY@|d$c3v`<=9I+^@R**}(o&LQ=XxIIo|q^+Zzhnn>-Ay;+%m^X5%5wk5&hE813upJF(0`<9~IO(Bg}?ZHhs&
zq|tHGjjg{ouJM^nS1C&rs|4N|9Eb2@-i~w)^5eJB5hj-tgnG_Y{Cbf*Oha4;ksh1g
z7ANK=31Q0d$cpvIK+-B|Hc7!@|AHSpxIYSn71X+Ef36tF^dM$Z7KFUkZRZm!s^ke^
zC_@hN(<`2f3wk5jov~!wE)ZjZXk487rZ^Yb!lDcT*X4n)(xVq@ZXhD__o=79?{-c_
zN9#5=S$$_gf|KLD9Om2FBYBS*R$;yo<>{1ZfOFwHt-2sJa3563cpHfw3lB`pb(Z>PaMD>=M=mri~&)2czX+H@M^L`Cf
zbTL^6AjRK;wjwZ#aM|k64^}&Z7UCOz;dYhtt-y0Jn~-K2Rr~8KpEX(CoR`Va>WWeA
zI*c7~4|fXcQ;aAIt|OBR0y&LOh*Cm-r-YGdy;eF-IXS)(BvNox0ek8iKssb+)tD(Z
z%`K9;=rJ3<2PJ8wcUm^YMZTd-u{$@_bF-_5?9f1&q90G^eFjy&T+B9((^WOdkII2}q=kRbL0k*az
zTmcB8x78R``u<*-toFA7xp8CfON}s)m0Pw&Nw@iVo?Kk0n)_QbEVwaII?<*M0=WfF
zi%YgN?Q`uq7BK(5S~-1xe{|ISExzUUsM))Knr|!_UCdKQX*}?VJs=i$t(DyH=tWk1
z7Ry?2y8871FY{aZLTdVT&C&Tcq8Q;2)^MMCE;br@@o
z=ChX0v3j7nhk@C^DV}{V>Tu=T%WO72!uR&dr@I8Ylv|r(ba$_YhMzpd3frxZjj;
z+uXg%TjgH>v#Q_Ieg``*)N>R{K2P<8phC7TTv9p^(^cn+MWJ6VuiCGEMk8ueYQlJV
zmF#EWmcqvXZu@c!2&z2iqtS{4n1>WZPk^0cojcz9>XGg^zuyEy5Kpnb^;^|XvO6o`
zg^Ut)n56+N#Ntnb%i_7>Bs!gt7#Ja-Gpycw80-D#;=O9Al1R7dIi(YW^zx0U;G-6E
z7JgDvMdii&-}59OP^1x~P}lM2b~@kv??CFMg(PI4dp9MNjAN8;?)OF^2rg(A@ga7^
zO3*`+eEZBapi<~>oMmt0>fncWCYHD=0)dpiND}iJH5SJHXatwm^EG=F-Rq1=v*t>t
zoJcd>A%&)~O~t7$7#K;&{x*CNy1+=6NC)w#l{9;89g@adP>aLwCvDS(MU#8ARNnfI
zmYKcM#ire!B4)+qEm$hYrIhADYcH+!ux%rHw7E2f}bfx+&Vhjqf4`
z#Xad?dzUBM$t0TO&?~p@g6k&ZLxCdmW}tNFSjk^2#Ix1iNUfM!liy$fXdX~9(HEXX
zXsv{LxRc6ZHPmN1U9@P*CiLY1p^JZ~?CXq#X(P!s!Zn2jUtq9fp*6Y
zvi}-Un3@syH!s35k}x}|Y-^_#gzoDL?=!svc!khVWC*l8nvD1vAp@InwF@K&Ua8GM
z0m&m;rXl)03^&alJ3I7X0YV`GY7dD?50FU!r=a<8A^`==#Nuiz%WZ}{OM$cMD^_v_
zZpg;>#zf6afKH{FWDJ{YJf7x2R_0zSY;K0s(tMX*4>90ga}hH7LWsB((@u+Hsm;bW
zszx4ISsxg8UIt__OfAC+J3|#p4)&DdC3eU{1utVqd`bN>D4C0BAf<0{G5}D5LG&&(
zY+eM=H3Y@YIwnKi8e$S;B0eyB8mpD4$C9A$tOz7Ge9C&@jYX+<{P3O&%CRTHyBOZ@
zG3$BD|=o>`L1-t(tkh>ooLOznW0
zgJhD&tpNbhsha_$AUPPmL{X6k{JR?;8D$1ooUk`xOA6XoNB8%}p~m>@F)}e|&ZqH1
zyg?Vo1B3Nv0Lvs*C@CZr-q~<*oQiD>i%uI%n&%?YG*lgDlQN-JV!iZ$mOv3$gXkFq%7|6%q~0J32HYjz)i7bJZD`)ak!!aG30
zBkRwPkzYg{pJrc0s0>GT${K#qp|5JI)dpgX{(&69L(ZYeK|mv!zX%q7X*jFh0NEM0
zT5e6YRQd=G3EQ-^W6!)x%i3_%mM6GxzgW;LUSE{%T5msq^aW9)+N+Z7#Cn
zPC|-{0@&Va+{h*RId5gxhZ18GYE+a!bQI?ak)lZx4jY^L*I&?_ic8QZvjux%aDZ7G
zWa=@N$#cK9ZR*~nTFQoZLetGcTs=4z$m95^ZFIRSx*Ot5bRO}u#yul&FCGWbX^2p4
zl;r4x+IGHDyy)cPP5kU1N+)ab_ByOf%s5=pxc4fgiO`l`$3X{{j~$Kgs;fdx{waI?
zy*u>%TVCigMx;BL&3c~UU8>7zzzVjc=c4^bh%>24dZl8=bA0^Gk2d^6kw+zVJ)!5#YQn=#0LIY=0a
zv6XHVVB=UI$7w58`FVNvNPb6q20zr>I;`R*0n7EscFzgT2x^Fl3rNL@Qk=WQageb^rg(kxzdS^3!w;p;x9(0SM5ze?_@Az3;dV(r!4Mh1VSRD0O_k&;z
zR_J=76Jg8e*xO)n((Vc+TzoK
zmZSal%_xja!p)rsO*C#C2RTyQ)fv%nrPQHWcByA`JX=k#hw05;a!R*TDf?Xw%`w`n
zV~=e-Uj=|LjNPK=X8kR#b{`9#rQOOhMyJV!*}|${22+!?RPl4E3R}#8^>BRuBm>;N
zLZ{$_XjUm3<%fRw4AkuT)mJKHVG%hfv)>$uTWpj?TVU@|#rT)jW?)*n2W
zuVX93$>a?QTSb9bFq3E2A)yb&dF28m^+Q#=qsD>oFD;Uh^|!nTX1yN-WpSD9xTgS-
zI7Kefd&3ImS3>)~T-kcm65T+w*31nap1uj-4XB)qAOfx106RjTe}BI;v_3E)@}-#}
zX(7`x^1$UGzIMadi?Ryz$%e=UZ3P%ivk7dqK2@>P5re}{#i{>#F}RTN_Rs3gP2WUq
zcpB}R3;ZI2D%x`mlg7H|nEGsxf?RX;%Zr|gi#g{8EB49A5fYH#A%(EOFMEEex9p0X
zX1acwUEGYRGt|b46MjVldN?gM{I?j(ujHr$DBZ&3qI@7{r{eW?0yb)tCX{X)d7{>C
zBSM`VxUOZ9R`|<0OXGK1HB_G3Sis<0_LtYa9w~6SFj=J%>1U}$@nJXm$b!S=Bx1#R
zYA;g8=FwM)m2s2E2wYHYJXK_wuk(!DeH5N*q26tPv09WfSPgsDjtrpz5P>fS-y&=x
zaLwlXc2bWx;5;B(^t9i@T-khCID!jJ0ZfPv1MEj6ddc-*rTR0zI5hxQRq`hM{Q2{_
zVi_7b$8@7A89DiW+$e=9vpY&wRu(nXBKmyVPN&*PWwVXC*>YS`{6ER>HlWN9FIlp9
zI8Bch#Q97`x0{Q->&U@=@y0G8R2lFBmY-FC5L099Ou_1-NTe)&848mX`)95R({t70
zw+yZyV;sddWGnz89`}z3zlr7Q?ujWGXlpMZcW6w+7$6m-?fvkmOK5XB@*tPS5JRGh
zmP$?qWpOmTN~z{uheZ;M_$68UP;fHCC*%R3Eh2vjjcNhFP2*|xH*e6TKq6mzoq&k%
zc}Li|B}15Ac;m|S@4(%WeNOsW&y7xtT<7GG(K!0>^wH2YhMDzvQNjg}lyr93BbdHo
zL`fwSH{McAbI9cczeI7fzPl%E(n?UTB2FLRlhRV5r25E*uJYMk
zqirpnm3Cr@VM(nAJI_;~Kq6Kt4&PNTfEY)VJ%Y9)NTH2QG!n^M=ShJZ#&(x7Je)BG
ze}ke&b!-v*rfB^=?hzARcEBAfRl8|O?ZNbpC@HKUUJr+tHztEmYWMR*<|{TcKlEY6
z9n!OdYO=!ja8^(Am%;LiV_i~Ios{TmxVO<{M1M)uxj$6PBQ1ZWOp0X@dwYA)qN=XK
zlV}go-%NgR7LE~Q+KT}OJO(6AgohvEeTlIDJ-YgNs!?+2Xt1c^Bv+;X90h|?K>VORJtOFXOR!sY~FLi>1o`wH=PIHh4fK4Ple>s}d>Ums>
z4tz`ZmTx4vBMJ6
zP{Tf9iN=jUmPOD&($}T&jcZxI0@tsu`y!dxjZm4uC(plgQjpuo!9HXYfQ-^M(#fch
zesOce&v$*Mx{+L?{g?w*EGU$%33s)0HpJe0R(}GH3)%<~vJUM3!^#eW+P1|L
zkLA4BH9UoS{#J4xskRzgugw>UlU#Y{Y5Pefo%Rj1+lW>pGwW3tuZLN6c7*kZ419s~
ztGLrcV``m?-#<=h!97d}@LO5KEP{c)nq*#g?o$zWZrA1hx6**113=o302Kx?6#uud
zi%{2J3{jz8b%ITDnO_Yek#)qNBsT=JJZRhg*TAWT!E{hlslQyhRy+NnM-rg7kV@rpOBzW;R#g3zT%oK>e36I!FHyopF-ym*{Ng{D*hOq;8*-9D%W#p
zr(RHBjSzh}Vqzj}UeBc0W@Er*&hQcWj>$ap;ldQPCv}2OA$5j{L|b@wLeOs^B5V=A
zaBo4f{shYYegNbD6;bHkC0&Td#yp=ys3&sVrlg>5p-hEFfc+0kSvtA>
z+U+N)ZenT69n|I<`OkUYu_DI-XWO+D;5zkQI=5l+%{@6&LsvSpC+UdHq&l|?y5G0h
zkLwFh=C>u$@Zv>iOv`0=)ImiFf%)sLfS`h`rPKZTWUbu86A+lJU&A*L>-zlkb>+i-
zH1ohD|NByzX#IvL;&6Nzm$}i=dCL*#$KxtNFfBp
z3)TZuU_EMw6KR8HI%tt9%eps0+U~s$Rts(-(Kx{28s93G)H;~VQShJqe4LIgEV;_R
zWvro0hl3nr2!|^tGF{T=bphDZX}_GKfBk3Y)zdh>I8#aKS(xwttrx
zD#T=FW=oS~DVdb77;}9K1Oo09ntALm>VCY4rC2tePU*5~{QZq1UUkBSBZ}!O*-wU$
zp10{|a{K8clqEYu4Fzix3FWu3v_9INgYg0(m=oR13MMtzOBg<;6L$Rr7!_w2O4MbJ
z)T1w9R~?TK1&iY3->#A;c;W3lf(G^eSd@rU+uG+V9ale(73&q>$y47aZfL1EoR9-0hzUaGeHt-yI_${q0tVY!-)luxfo!`Qxft3kUV?9
zn@36h>UT18d)n}J_jzA8aCg%Ry;9kH|B8VKAQjmS1TeNe!=r3L}Ye(lvyRa5A=U&zk|86G;VyC~bR$}G|pBqon51BbI@igMn?GVQQ`8ba{n?={O
zRH&Uf!VHno6Oz?|joV@n>~_l~%b#^^xc5LNFxyJj;IAZfAjWhw&w)yUFLG5CCZ%I`
z;29(#l*$EG)Wo;vnO}+tCURYrqn>TN19aY&>aMthe5Mhx>a%Mjmnh?rimU5FFq;>W
z2xZ}Gxl}ngWGw~eT8fFTJ|ej83$9Bo-t$bu3TKCa~oS<(`No-
z!pBX%AWA6S?~nd5mIc@}f3Lf$
zn2Z;~)Hafq=5@cwp{TFFZ)@=skD|&$o`OzVLJ`JaYkNOrk(J>g#$+ecyB{bVPJ_Jy<0UA=T8-Ah_>nU0i!%;;*8W!*jd@YO`;Cg+xaiaGJ
zw1&QdglJ!kO(PZcv`sHcU+-U6!?F8_X69Smsmr;Y5Ms>nRtM%td{7_V0VrwnjClq>
z7C_!G?4?yHZVvkE`0h%TH=Ub*#56E%ruy;NMo4RY)BvfYO?t4zNP0vB9&oLMO;(i+
z9P`j-97TGR!hA_xpvq|Z(&c&k2R%2hti3IF_^rIqFUJh*kTC4t`1tr}
z={M{2>esQ6Gcz;MUMl_11iArO6tHQ$QwoN8;LT=q3$x-ArhoHSp`nC!+S>2mH7$fW
zkJ`>gqg!mh?LSE#-ty-ab&CH~T;*|pF}ok9h_*4dv!A>Fe;nAwMz^GL^@lyfjtTOF
z7@z$@f`7nIg)cxLj48Bh#n
zEH5`7&lo)7mJqLv1*%cP(vs$MS|st^E@;wZ6@P{ZK+~~@Dc@C!o9Mt!#(5%8{+ROP
zz3pV{-zTd3qZDjx;s`(e!ye1`Iw5j1V#m}p^l(NAhv&-&0~*1)_=u#sY|0N6DuP`@N&l1P>s(YGWBb^_z=ZV}gKykV;wQdrij=i`z+WXT%8%EDVN
zA2ZYN?#aXg@T%YK?a~l5c_efr1bT9Dp~5@2fhLMI5QZ;J0Y(4ac2T3y
zojH692w6{h=pXnCU89G2Q!wlWBW;s4@_i!lF>#XV3+c=}Pl
zldQWa?7TYx6j`7`-ew)tL`MVtdTzm5MywEO2whUUYaz07ZJNLupvT%?|CcENN}vLU
z&uO;2xfhyM9)PNW8S*5oe$)*_WC+yeyJj?S&^Inf6BUM7O^!?zzn~?CJ+;VlPC8=R
zy~86)lDh1*4O(nG9`B$HIoUYe+XG;6OmoAY2!fTJq$se}c7hn*?rQEVhz}JnZ>}ck
ztqsLxQYVv^>0y0jPht^!$5P_uH%@1P>F5!OummM6D!+FgD?Fm`L;CsfT)PsVla9W~
z(cce*jzsM$4;N546%yAF8jdGK6=lyxX8j<`i
zv!--FLoDN&7++=b;SUJoPwKLljG4IG8eQ~dj@Y9F%>^a5io)aX|fvv2B6M@Wu{3fOtr)jTUq2x&~0A61~Dh2+AJw*|P
zoJ2C}{JjeA)&>u^L+jW}-d;?mbTRX)_)4xjngs6dUp`qGj@%J&|cVnB7jslVy
z`S(7M`1f?B>n$xF+gg+o;az@J&0AD&Fv-7bSMFX*weEfSTC6gBJd-Qsf8NJTk$t6Z
zfk<&qom~RtN#UYh+f;ggRV4ugQK2D9lejG<;Rr6is>EAL=Ve5vt#4_K2=Z9c35fA8
zjY6J@{BwV3$I+Th?F?-@qO)u2i))^~^I9MJ3SYyq4HY7A8v8cZ+B0_Ky0Z`qCq~o9
z0-21a-T-X-vCzK)mo$AjuF;!D$ueU-5+sXK=+Z&H5={TVPG0DNqVaqvyyo*TGndea
zT2~K?dD%sb@?90fv(d9P^0CgqWprU)z87Qbdd{W$m^`AjjtO1$EhSwqiqizU7(w4O
zdR}8G8+_8BUv&7=Z)x9biHWY+mwF)fbUvMwxvus+Q~g9oi3uU27t*jqL=z+pK?>Bv
zETSJV0s^z(xCeEMZ?`~1}qm2_~CGbbb@T=$E$6Y5GU*biB%EgA8D_U6S1JPD)MNI}x*xbEZYmpmeP
zQ%Q9k#Yz*CXw?$`Nex;)=Ce($$Og$FcrH`JZwPpj)*pyK`Qjf>!pcR3g;hL!avdbu
zTtu_tKUDG2GmcgDMB(qZ#y%1IgA-d`rCn*-Of)?`eR?EL{;*W+HeajQF9uD=P587L
z;*)VGtzScc|Fnu8`VFMQJ8@$yP%R*a1EjnBM@<`v<&+-XfTO`a%DWryODVeO2hT~{&hmd>oi-<6#L#E
z407&h3q&Y8qHrFDC=B8W$qxNh)bW^x&sz|Hl3(F4wX5rbosTbxeZc9k`F-ySuv+-0k4*?k>R{
zg1b8ecS3+50fH0Ug1h^{Avgr+oxb<$?)U09gE3C+s#UdSP2p~S-LjwG$Bs?#4D;YU
z9-+tn;yIKRO=+))!8Z`Op&ymHt_AM?VVoCO0xfy~RbhCbp)}1~n=pG8dLvdzGDf@`
zM+=JzM_67TEAu-=hd5!ba`cRA3ilm0gK4p3NBOqD7GJ+2I5dgtrW7I
zs5^EPVIQF#ErUdj{tSOi>CN?X3HkcTq2iaN7fw8r5J7O%9(ow(zPx7pDdM3
zBUy~wlxl3gY*e24#X6%QC_T#Mo_7eq
zG?Zo-PbB~q4-c#?QDZBd%}2i89v&*)>2*w~tF6tg7bPxL3QM!m@esyOjYcByjfvyE
zfgJ|*$yY-a9uXz?FJ6FNDwK?vEwU-RohUS<2m^>o|Yx
zg&{o;Pi3KeYn@(vn=6zTxP!cX7vq(W)u#wqIn-NWYzz6>MHU-fLNX8ELe;W}2A1IY
zG)!k?8zY<~I3Qsd*PiLt_=;lQ{=1?JstEmn_X^f=n2Ec*Z&;tsMu}4)@XG`i>4r(*
zZFl(sToU5g22aKIda#SQhG_>$+LKkA@!4m}>FBLpHUVkdv`R5$T!_K=;AuwcY&l(=
zGgCmquMcFSro^bRRGe)!w*E~&<(oAk56^_o9zD$)s1FH2U+R5;dK2Q^t
z
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 Wxo*64yUbp2((rWkb6Mw<&;$VG1r7oL diff --git a/docs/images/screen4.png b/docs/images/screen4.png deleted file mode 100644 index 53da801a1b2a5a7120a798b89bb94c984123e235..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 210218 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 QtCKXK+>=V{Nc3YXU)*$Yp&37U!)Yg=jfw_(Lh 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� z6tCPren`UWX5ox*a3TRu>@lim6;^F|o1Bs%(tab=ud3aEK^+=&4uC1{d%hWu>Da~D z9O4#*gzVe0FU5g(vM0ZOWn>qN)Zt#T<%#7{01Xp2mWRzyzQ>99CAko^fh?Wk@lngC zD6P!#2RCipc#LTg2A28PMgEz1XZ8kUFoeET^4Wj3Io*La=q}$uDv)XW&%^xQYw zw~wiMD+(znaEvmv`)-i!8d>HUF!6(u|5pUnRW?0ruVIUJU)R4bt%T}rCUxG&jIhw> z+l<`3G9G4E{M=N%NYRs)VtuKjLJnWzttLl|HN!2pE#lxP2xQZC-yV_hRtREys!t#C z)DGP?>UPn9qQql+wV6V8hNmzq>w9MT*E^MFOyKuJst+=4gB%pd70|DDnb=XB>I%Q& zo)-6lrH)Bmp`~=x6-LYlJr{KO>ib;gG$iqpi!> zjQ(A(d2wLpup`2q-#R^0I}0&#B{Iy8adgt`DZUOsbowMBcGIHK^KZmNvX}~Nw=fE- zqnxBHQZl?BHdp0kH`wM4pbf}ojd_P`im+PlRIj($+aEh@gcGn>g%S~nEo(2hOD^aN z$oN+b)WX!mOb_;(HBHY8H1e01_WZaPS!Vr!bur%$UJ+DEubZiv)m~ivan?g%>NX;!12K~xpI_D9^iC@)rrW5f z0t~$E-T#QKDrXjOX$jrZP*$5aNDvyhBT>?plR_8}?e13OLZg{XO`oW@cs?ywNY)Cu z)Gq?|3Wla7RGLt0eX%g8pf5zgre&<2rC)Iy&f2`U;t5dh^%_K<5mKu!7Iofrzn8UG z{MgDvyB{KBua}bYr1g{*0Kw)GvUzti_4kr74b0riI-Z9z*9&yQgL z8u)uo3X4hGFt=ds%U2u zqR*;qzr=5e;co6$mbz_T-Za}x-W)@n8*mM>%^BP6d0)4y*$5qhueVpiliH6vHiq_g zYdORr5jS+0${ObEkf$1FNTsn^!bbrci}=0aJ=`38vn~T8rVR60@XdXBq^%g+ z+(>XBcX88Lre2MDNpv*0K5gm~d7#53mNU=Y)?dBH%D7;<&uPx@d9c^WuMrx60mS@e z1+|Thv&PTmK7a-N+MFCJ%tciQLcEj%=5S|~54`FIzhRPWOzW&fJsF{7+X#WFw;y>V z%oK9M7w#VCjQ8`)YqSR0tK}iSK0@E-HE=At&ADFd?7a|-!rd7Jn)t8Z&wTM1in{$o zH%-=Iyd2Gn$>GU7$W39#yftolOFu5pD_Vk0M5wJNi{L3=M^-_)b@#-fajs0pMQ(@XzOud%=gzk&xdxAKJ?3pXqEch*=#qmvWPvpjUf2tXADr` zGj%^0khdE8gqNg|))SVnJEw+W=8Wv|hW=TZm+nQ!)-bXg8tNDpY1QXScMeND6jum1 z7w=R2NfkY`3!PF4325SLXkC5=>xf5=Hk;-o#AaDc6}q(#I&U6tZsv1h79F{Y`>Olh z%IL;@9A%)MFlXmMPx5;Qu@C9$EpHD90&lCvoU~dlmYw^3*Y6!o3IsN)&t7a)CiK{B z=A|3qAXkx={%6&gaW;Nw)Ij%pOqO#}$PX>#!7^f|uv~$%NpyuOy)NG2N4U8PDNK`o z{U#)h@q-x<^fK&*Y;#4Zp)UgR6z?@vus38*$oajDA}TLxaj4Pcz8|GJHFYwHKt}c# zkko6zYYmm}az%0)U;hptI{2x>vGL?GHS~rq%M8x$3+J7EA4cfs8b*$pilaunSyomW z{Ao~&gp!m6>j!V`2QF1bv&jNBJo{o(w|*j#W-id?cnFj=QOI}a(?-5nzMNfK{>Iz& zVF^0X9q+ioa=mZO6GGrgD6bNRq~=#hQd-k_lUFOZ3bH69QX8Zg2E^{CG3_ku4Dndx zbA^sPj7AzQ!i?wTk(S?^0Q-FA17|}%EhTLhg#Z!pD;siPeW8@~6(y`p-zzReJ5e)#2$c5A|Si%~EyQ>LF*vqkE5S`Bt$+34&urH^FDh+YuS(_KV z^lhtWh(@(|5wBb~^%|2~`xi8QQ`e}@{%@(L=#Fxli^4O+RhP|@mHslik#HR=fOj`) z`PmxBQh$@i{o%bZ9*zR<`zMfJ4}=it4a$n!4G{Pbb_3Z{0UQOcOPNOdoJ9vqh_LXN zrJ0vZp@bw@Mx4TQzc{V!vTnrBM|tZMY)xRez@U(QUg_JpXA%4_hi)m10!mEMB3>~+ zGuQI_5@0XGJTbn~RzxkFM6F>SMN}HfP@uU@edcV_nSBwRjTqa}Cz<75T>e|7U8+jO zbU=-})d9JwYe?~?fUFS~zd~ViCicw-gEjxT&BEl$i}2|*p|V!UZg#B;41kT3pSk<2%5&HCgHhTtd&_)RC z)K?|GL+esjhyE-__43`bPMJ4~n+G#)aRD$z>K!X`LD!~+HJo5A64UR5KHLF(>q-r^ zTzhq-`DcccKjMmaK!K%1>kWak--opJl?i}u3c^M|59FzPD;!tv*e7%PzM^pG4Q7rr z?YaeNpkk>g1pkwrz@6cI0xv?Sh-hTb_Raa!lq#0Up@6bZiM{8xTiFdwVT@tt!)pMFC`=;VYs&e8FWf zXv;0DA+6d1e~(E7(J=Jt-WP<0P&mwRC;{-xxyt3efxby>FKfL63%e2x;nqUrR6nD$ zSTow-uCxqGaZ`X>8n(<%Pu~L&WA3|9Hxu-+a5b(Ra=m-d`MJJxt#}r(>14$t!;Zt$ z;b*KX*G-^eU*C|oC_+TIvCwrot{@HfPb z>d=LXFZ|taPar<5%X#ry@|ws_-~ysS*3|kKbIBeWWF25bM%$+%417g?7I zPS?RFP;W7<92b*uvB*)G(K{)ZY#3rHHpYpg$Xg0YZMXCE1@90puK2hp)O;TE-KryM zv40p#gee{%H>QYkgw}HzVRo5^El_21f%B=Eap^0JNZEUS0W>LlT1>)7AakWJOC3Dt zlH>~HY^rPfiJpz7lUGNgHEpmsyK}-CQ$SjBB=3H{3QT^CBa+K=xFfqO?%66VaQ`ho zEVW>exiEz%F543>OzqtHq5qlRXsBb&G*zQTB0KNp9X`UES1$oo zL|m{#4sm2Yy7R5Y;7-b0v>*PXpm^3x#D~|!?F(ecf@st6`mm=aU3le~vA$Ofw&1c) znR$q{{cAUGFCdrIPg5x})KLi5n<7LbOzd;C2)LHtvscL4$DG1>qc(E~^4UYmO^AJh zj*3&L%&2JuleNXHz zQ@1Moa2UPyXB;~QT7>1LGA6L9zv!gM?6OW-L^8fCPWT3bPHRT)$-e$O5ntk~-KR(R zrmBHd(K=oZ;QC;Gl*_Vs8KP+S48GT?Mbo*w}CQ*y^V{Mq$R8k}voQ_hJR|kNmO-@ZMq8qBa zaiS))@Av14aeYcyf4!?&?_D+ArbEV<_B1bBmtFpQIe)Z>W|K1<)21Lo2;}QE9hpgk zPrSnrdOey*Aq5fzj zd{e~(iFDDzWpTPUK;}LTI+GKnJsI1*;5%)uEQ(${pt_q=fuC|vFaT_z>^J)s7SHzR z4Mv0S*ncvX;6P^uHk@%U2c0tn%XnrsS0TU$In6^Eo7RMgh4kjb+15#{u4CKpD|$&% zF_IHI%G)jrWGNebXZFi!3yLp;xE;VdGHteurn;PYexorLC{bP%#0g86JGML@=j(b` z&T;t)aGAp1^vM8IuqG@V7z>?&`8GN8W?lI^#U-kO!Et6)Zx??0Fb>>~%$2ewBl-mV zJg;;cAjXyvxiM6!&TB^^fUo`&Nqr}+s;^xS_3HPk|sDGeYl2(i3Y z!%3j`FC!Y3RWk-qD+r5gHWL*teJF*Q{omevTrg}xtdc$cUa#cdNtcLA}mlq0!^Eb|)jj6cQEpO~5qwgmEkLL@$`dUA} zFL>WLjxxHTRviY!w37j9@EdxQWvG|EzMX~L6%@w+++gIW2O*L5_m!F@8Wi^(RnjX;WsxY z@Vg@~tyZ*4fIh4~Dt_R!Y1ypy=t(N9XzUS3)8MdeZhY1x?$|t!tAZcW{s5R+z$nTG zLh=gOrRq&X?kH@Mvc{E=;2fS{_cq>$_guFBSZe;LH^VEZn?U`5^)GB>!9snecslE* zr=xu~rwL6kQ3DGWMtN)AiclbTo0+;}epuVS{KWoG472+f z)ULPjnmIEy^zNAz+&i%90+0{7Cs3;ru70&yCM^3P>Zpkyv>eGV_T-rs)a$vQ1PSZ7 zMotdPRJ|*FNsFk5JBYwinaZUp$9(4vNVBBTd4+ID0MBPzo=Yq4+WoTL9PQxbg3~nw zDo~b@-Ht&=x5)5hjEVu9G;jiQy&x=3JI_s9Mszgx+YhESOz>>diHzLiIVIc=d_MQ> z%Q+waR1V{ z)=|(N;afKbDsMWe0S-(2z0}(zdmm~1M`>1ge>-lNtwegw;k(N|;Y5?#oKN6C>Oin^ zH1$3C{Dptuu~wa6zs!nB4P=d>VOM1T%xPZn;+`^B@+9T(L)=~l$27K##_b;~STU47 zIGW#9a9VoD=c{5!*=9*zb(?R!Rb+NiMBlm>`}vB{R_)cxx1qv4Noc6;pnhmU6jo0( z+0ivdTHmB?@Us5uM&ROrr2^ilaqoEVb8W9J&|pV*sf4y)I&yuY8}li&fx1_f02bL- zQydbj)~^tPv`Z;R@m$Jp&sFp_Rn9cT%Vfg|eS_tf6km{Us-ztLuE+Oj9{v1}_+2e4 zy@naj7i`7AJzRuWlnrW>d_0=+owI3h#Fl4k|6CS-yF+U5`$*u}sJ(Ju!K3>jc0lN_ zYMuIU>0;ts|4m|%-4^u3QtbzLp~41KUSCJxE~BeWb@5;dOd{4!BzW_$DTO=9S`t<%9RsV{fA_Mvl%pkse4}B~nM^ zLSC_enwovLld3-{>S(dv*D`X+j_C-5gP6#=+Q zyEVtb_Oqot-SSHIMnFp{m-EEQkSc^BUn$bOml(j%FCUI|zS?o?`cag7p^Nl)Ql>Zc zsl@s(bK-Ns3J>spR)@Hir9i1^H3a;{(WI1b&fX%YnqM=+ON=deTj_OU02vzHwBY$9W&No_7?P(0X7>VV&vw&1gVCK1f9p`Ahfpc&7I9As37 zCS3YN%3#Z4+x>C=Z@sLU0Ae(Eu?y{W%ZvX7acE^HfuH&D)C*x+- zZ&YH2T{1&f-Hclf5~Q!O?9W=9ue44TUI` z&YL}HZ%48L2kN#JeD-ZuC~)rnj_}RX^)`86?(w&&i80qh#tM4Hw#FhbVp2+JL|j3MZ2|bMz{`4gypsp1f%&fd4R{1{eFKR^ODO^ zkiAn<{z4LGy~Db^OZW&2`_&QT-G~5rhO<$?nOvtRuVjAMW-3+!oXH;WeROw}p;SBp zn^N+YAM$nYg;r<5A7A}B`upjEN4>#WC3KrZik|XgJn@CtLJ4OcLqI=@e_G{Z&RHWM+R9;2@1y zd|l@0(EbPz;)@fBcvSLd?^%v}VC~n@L5Z-WDl3&pQ39}!qenqwuz(MZTqz#w++BFR zBr#4C?>yuGlSF*-gqONQ%hoDNHlyZ}O>{N5dQ25%>*Db7*@m4>5#6zCf5;5H8#k&f z$jBaP_5$W&`_bU?{JXK8y(^$4WYQJt$D-oZk3n9`cJ<#@a*l35tZ6kLj)34R&u1P5 zb`)UG^4i+4PJi8y>b)IBWorQ%uYQ$D;NP{=oiWw!MJyUn(;|3**(;zEC%5qdF;*}} zK_YSK3y5>can%(WT$)d%$R?&Gk_9W~oySj!r@eWtt{7qN+XikLKps$HXTNLE9v}8% zUFDc(aJkY>0k&x~fI;ws@%@xQkby_>ZA%AVTd(?&uRQCDoIIRBu0jJqaA^#8JCAZ~ z$-nFLfFhfilb{m_M1i1_G&Ph9qt672VP9mPi^pQd_J%EQc|LXV5(ObX(lgu*H?}MW ziu(8m&^NS2Q9;He$l-VdKcn=_IN$8DuWg#Lm79eNh;&hcPs@*H_Re}o`=ZyL_ZxS; zy0x$bcqPcCpNu*3vM@^y1GAuF_I&D4QTuy&9j}{NTM2R8MpmbHT`J6^#5yop%5_iH z-_;a477X=1Jaq!o?0H5Zg84JM|HoPn;7LEN=q56aUYNj_?AquzE=>0`&V-DrWBgnX z=erj$eo4pV_J)xjvc-R6j@lmHCQMaop8(?y{Qp1x?~y>?qbH9GMDM6N?=iA>V9!&+ z$d4auwg7>lnqRLyVTaC)KR>1|TTpUja1WhIRb!DBp+o>-ACP(EW%j4Fn=T`J!w`t? zQ6QAgjwQlDIF*W<=ilX=WTZq&Ro6k!|7tbg!Vi(n=K_kBshEF{|p=f|4PdhQj58_B=edd*xUt zcMUwp?UR@+KJUK;5=ZQ9oucko109M-_V%8ko?r{=K>YgMX{rfIUFzfmGMtiNDu}v| za#j5_HErc#nQ|obk5@1kA=!OYi&p0;-D+4i1a2UY|Zg&)DF*Cg{65rtdOd zu~6LBfuF%s(goPLL$=Aca_>dD{{3KO8w^@U8MN()e6Z9N4J*ts4Lwx9<+(Ng4G&WJ zMQdCqOV2H7GM}>Cy4v_D|N8AdMWLCM`}bXh z1~iv^S=cvpkC`6I3U=_h*$3eM6?PpodCjcsq4IuIObZ1kl~I}=^I zu;xp<9B~)p7M$pvYW8@b`${-so9yY5Z7d)L+fWE~vM&3Ce=B$z(D!m88#kA}G`qal z<8`BV;C5u*78$qE1~Muzc7pQRb&Tw)YK`w3X7}nP4GAJt^_Z)3WZc_RTLS`e;F4T< zj*qNK>a*f!+Pr6^O;vW!(lAkejN ze5iw=2i3XqrDCtS{IIzZq6t;Rd9L+t0)NwA(l*867Yn24a&gPoITdTJ)=)b!Q=b{2 zV#C6DXc01mlF&gW5G32JDC<5q;Iiq>&3wS#mdX~*3z}lS8)t)9jp}L3)Ezc`k0Aag zCh=6;*UP&nfKAisFC{sbY(Ev~zVD*t5^rdnEl+721X+{Incb@_%IA_sSqS zp4a~+ZE7j*Sa>_fc(Az*};djLi^^`-hrKcMaTqOC7O zF}ho{p?E7;akWhrQzIXBI`vk|Dd{h1L-1VhqOotpMAbka(F29oSp5Lt+==JY?GyQ# z0`HgQMY*D4zx)z_gkFu-YJ;myxc3MgKe7mL&d$gT+WPkX*oWDvz-$Ts^HW)A(Ervv z@aCF~waL-k64v7M+NHwWVW08Q1sbPJ$ZxyJ8dtR@;Z@s6*JxK*DlJ`B*y+m^D?`?E z_6sUUj{s~AJS^dUnyfMW+j%L~?K}Ue2kFIKyUy54lTIl8%gWSe#2<&G?_3cRI^HxL zvN%neMA>x=jEtM{t8TMHJhII`yICz9R6nVs2u}E!^95<=>A>E4x#DwSQ+sz;NKkNq zGq+7RdUeI?w6w0}`*|Q;AA*`?MNF-HzT+O;{^CKI??GSHC`8LYJec4}MS3!EvR1n6 zn?8j6BonkvTH;vyn90LhM)ofk*0c^{qP8?f^RuYu^S>qetK;euSSL#<L}5nXa0)leTQne*ocC`_)p_Y-U8dcP!Y%k{ygsI;5!R=EE}=vr}|3 zZ;meuNnmO)vVXM}f;rCyVvlU^b?M?Vrx^y6QOX_QMRJqzQy&ynr7%TY;P(68K-|_( zB*hvnEL4@^hlHM*%-mttGJ$VpZUyS-4AnS%RBcVBfy(cV6+Y45P zX0;zXZTqcD7w#S4u6ls6w?E@KK|5gS`QlEox93{*s0rFH0y&mAb_43J>P>+i=sjT} ztmS`35gYjTgqV&+(E)o<_Bej&%Seeu^dAMNxZEatbE}gF)7EyUKIO{=unearQg)cS zO{Twb74Fi;AQZ%e&PvY!d06b~ix#c_u%&eKLz^9VAZkkI{&O0@HK&OoLulLaE=5wN zWF;gc2iYL6|;gsS$SRTy9tQJpQbh-lAlpDqHOAL#) zRqs-qNS;;{55Rl;hFhC3cea*mB6R95#<@Er;$61LMGGWLS5|lxlUbn^)w&cn0&cDqg>G8jxs0^|! zaM&a&Us3%GakVc>LQ-$B*tdZz;Y*16F|du}ahfYrOjfjoVzJmXge7VQZ70#hy)Q9%7iClKS*H z&d{^-`>x~6SI1DfV-@z@=8Fm)~^926_G$lBjN5N9JrkNJ|*IQJAlqh<>+oG1yeOv-v(3)5EJM!$I>b9t2L$ z4cJrB&zvWq!#P3O7DG@P@b_}K#SmwX>_RyC@got+i6*-_$9eBtRm2BlQs`)&1;r*U zryb2BtF3jN306zlgn>jUDk>9hg;#QU5z43HFIk06tzQz(AKkb2k-|>CMUEeTePy6gTF zfT4}S1wEtrN~$YJ8)8Iv&kbG53cg87UQZ4*Q66qe$u@IClk7A75Y%J>D*<4O*DgJ#+=-Y&i+mhPK@JKGe% z(sz1Rn-luBiF(TVWkTL+TYpoKXl1J0Um23mR8P>I2ocy0+Imd+8qQQnaxe5&?AA+{ zPo1myIzc&|h&xF{cW58E5*qtxk-Cc(-8W$)Sjw6Y^E&+cT(B)4tf!2!F`iF3x`p2v5OQGvXbB1B_6fI`W!l=q?}=gnkmg$!KvEwohV>ZH;(@0W+JHlOT zJF6?GpeHjV=sXzti(Z?{6{)8^A7?WAQ03Pb(w5HIPn$2|Bv-oA_d0ejgCU5N9WpiUwE~(pH?i8c~o+r*6J3oblqGmiryh&g^3{uU$dRxTHf*vJ4=%+N10) zxgUN>B$DKYSY<(vt9_=RRPbUj5WFw{M5Ty3v?gD)Gl@x&aOl|3S<-Z>+PZ)6+a7(K zFZU}XBPE3tFPYTS*Eemj_*SyNFH`?dPWxSx|43jlE`C})0TSDo)!iYw%ayaEh2Gby zOh~VeCnRU|7olB^B|T+GQidd#V6*&DMUT3@s8I8xQW05isKUyfh z^1J&@@M_w{i*NOHdo-si4SNhE3^6WDQYJkQB)qNIq{g%=$86eNc7|e@D Date: Wed, 26 Jul 2023 17:36:36 +0200 Subject: [PATCH 160/251] Fix compilation issue. --- settings.gradle.kts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index ede03543d9..751c65d388 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -29,10 +29,6 @@ dependencyResolutionManagement { repositories { google() mavenCentral() - // To have immediate access to Rust SDK versions - maven { - url = URI("https://s01.oss.sonatype.org/content/repositories/releases") - } maven { url = URI("https://www.jitpack.io") content { @@ -40,6 +36,10 @@ dependencyResolutionManagement { includeModule("com.github.matrix-org", "matrix-analytics-events") } } + // To have immediate access to Rust SDK versions + maven { + url = URI("https://s01.oss.sonatype.org/content/repositories/releases") + } flatDir { dirs("libraries/matrix/libs") } From d427407b50737dafa910cd4904078374b32b85b4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Jul 2023 08:43:04 +0200 Subject: [PATCH 161/251] Update dependency androidx.recyclerview:recyclerview to v1.3.1 (#974) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6b54842015..04af1ba607 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ core = "1.10.1" datastore = "1.0.0" constraintlayout = "2.1.4" constraintlayout_compose = "1.0.1" -recyclerview = "1.3.0" +recyclerview = "1.3.1" lifecycle = "2.6.1" activity = "1.7.2" startup = "1.1.1" From 676692569f866062e9d22c1e4fe2c8315f7ea5db Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 11:32:39 +0200 Subject: [PATCH 162/251] Remove unused drawables. --- libraries/theme/src/main/res/drawable/ic_chat.xml | 9 --------- libraries/theme/src/main/res/drawable/ic_check.xml | 9 --------- .../theme/src/main/res/drawable/ic_check_circle.xml | 9 --------- .../theme/src/main/res/drawable/ic_chevron.xml | 9 --------- libraries/theme/src/main/res/drawable/ic_close.xml | 9 --------- .../theme/src/main/res/drawable/ic_computer.xml | 9 --------- libraries/theme/src/main/res/drawable/ic_delete.xml | 9 --------- libraries/theme/src/main/res/drawable/ic_error.xml | 9 --------- libraries/theme/src/main/res/drawable/ic_info.xml | 9 --------- libraries/theme/src/main/res/drawable/ic_lock.xml | 9 --------- libraries/theme/src/main/res/drawable/ic_mobile.xml | 9 --------- libraries/theme/src/main/res/drawable/ic_thread.xml | 13 ------------- libraries/theme/src/main/res/drawable/ic_user.xml | 11 ----------- .../main/res/drawable/ic_visibility_invisible.xml | 9 --------- .../src/main/res/drawable/ic_visibility_visible.xml | 9 --------- .../theme/src/main/res/drawable/ic_web_browser.xml | 9 --------- 16 files changed, 150 deletions(-) delete mode 100644 libraries/theme/src/main/res/drawable/ic_chat.xml delete mode 100644 libraries/theme/src/main/res/drawable/ic_check.xml delete mode 100644 libraries/theme/src/main/res/drawable/ic_check_circle.xml delete mode 100644 libraries/theme/src/main/res/drawable/ic_chevron.xml delete mode 100644 libraries/theme/src/main/res/drawable/ic_close.xml delete mode 100644 libraries/theme/src/main/res/drawable/ic_computer.xml delete mode 100644 libraries/theme/src/main/res/drawable/ic_delete.xml delete mode 100644 libraries/theme/src/main/res/drawable/ic_error.xml delete mode 100644 libraries/theme/src/main/res/drawable/ic_info.xml delete mode 100644 libraries/theme/src/main/res/drawable/ic_lock.xml delete mode 100644 libraries/theme/src/main/res/drawable/ic_mobile.xml delete mode 100644 libraries/theme/src/main/res/drawable/ic_thread.xml delete mode 100644 libraries/theme/src/main/res/drawable/ic_user.xml delete mode 100644 libraries/theme/src/main/res/drawable/ic_visibility_invisible.xml delete mode 100644 libraries/theme/src/main/res/drawable/ic_visibility_visible.xml delete mode 100644 libraries/theme/src/main/res/drawable/ic_web_browser.xml diff --git a/libraries/theme/src/main/res/drawable/ic_chat.xml b/libraries/theme/src/main/res/drawable/ic_chat.xml deleted file mode 100644 index 1fef824a1d..0000000000 --- a/libraries/theme/src/main/res/drawable/ic_chat.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/libraries/theme/src/main/res/drawable/ic_check.xml b/libraries/theme/src/main/res/drawable/ic_check.xml deleted file mode 100644 index e92733095b..0000000000 --- a/libraries/theme/src/main/res/drawable/ic_check.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/libraries/theme/src/main/res/drawable/ic_check_circle.xml b/libraries/theme/src/main/res/drawable/ic_check_circle.xml deleted file mode 100644 index ad3aacbe28..0000000000 --- a/libraries/theme/src/main/res/drawable/ic_check_circle.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/libraries/theme/src/main/res/drawable/ic_chevron.xml b/libraries/theme/src/main/res/drawable/ic_chevron.xml deleted file mode 100644 index 4ecd3f16b0..0000000000 --- a/libraries/theme/src/main/res/drawable/ic_chevron.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/libraries/theme/src/main/res/drawable/ic_close.xml b/libraries/theme/src/main/res/drawable/ic_close.xml deleted file mode 100644 index f334767b67..0000000000 --- a/libraries/theme/src/main/res/drawable/ic_close.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/libraries/theme/src/main/res/drawable/ic_computer.xml b/libraries/theme/src/main/res/drawable/ic_computer.xml deleted file mode 100644 index e2748c2d4a..0000000000 --- a/libraries/theme/src/main/res/drawable/ic_computer.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/libraries/theme/src/main/res/drawable/ic_delete.xml b/libraries/theme/src/main/res/drawable/ic_delete.xml deleted file mode 100644 index 413a570210..0000000000 --- a/libraries/theme/src/main/res/drawable/ic_delete.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/libraries/theme/src/main/res/drawable/ic_error.xml b/libraries/theme/src/main/res/drawable/ic_error.xml deleted file mode 100644 index d978824039..0000000000 --- a/libraries/theme/src/main/res/drawable/ic_error.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/libraries/theme/src/main/res/drawable/ic_info.xml b/libraries/theme/src/main/res/drawable/ic_info.xml deleted file mode 100644 index 69865e325a..0000000000 --- a/libraries/theme/src/main/res/drawable/ic_info.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/libraries/theme/src/main/res/drawable/ic_lock.xml b/libraries/theme/src/main/res/drawable/ic_lock.xml deleted file mode 100644 index 2ada59e82f..0000000000 --- a/libraries/theme/src/main/res/drawable/ic_lock.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/libraries/theme/src/main/res/drawable/ic_mobile.xml b/libraries/theme/src/main/res/drawable/ic_mobile.xml deleted file mode 100644 index f2c46be357..0000000000 --- a/libraries/theme/src/main/res/drawable/ic_mobile.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/libraries/theme/src/main/res/drawable/ic_thread.xml b/libraries/theme/src/main/res/drawable/ic_thread.xml deleted file mode 100644 index d3293fab5a..0000000000 --- a/libraries/theme/src/main/res/drawable/ic_thread.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/libraries/theme/src/main/res/drawable/ic_user.xml b/libraries/theme/src/main/res/drawable/ic_user.xml deleted file mode 100644 index 5f61985ce4..0000000000 --- a/libraries/theme/src/main/res/drawable/ic_user.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/libraries/theme/src/main/res/drawable/ic_visibility_invisible.xml b/libraries/theme/src/main/res/drawable/ic_visibility_invisible.xml deleted file mode 100644 index 3f20783ee4..0000000000 --- a/libraries/theme/src/main/res/drawable/ic_visibility_invisible.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/libraries/theme/src/main/res/drawable/ic_visibility_visible.xml b/libraries/theme/src/main/res/drawable/ic_visibility_visible.xml deleted file mode 100644 index 1283a1512b..0000000000 --- a/libraries/theme/src/main/res/drawable/ic_visibility_visible.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/libraries/theme/src/main/res/drawable/ic_web_browser.xml b/libraries/theme/src/main/res/drawable/ic_web_browser.xml deleted file mode 100644 index 080ce75905..0000000000 --- a/libraries/theme/src/main/res/drawable/ic_web_browser.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - From bdefe357b01a330e551e16f5a53066bdcdb79e65 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 11:32:47 +0200 Subject: [PATCH 163/251] Remove unused strings. --- .../src/main/res/values/donottranslate.xml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100755 libraries/ui-strings/src/main/res/values/donottranslate.xml diff --git a/libraries/ui-strings/src/main/res/values/donottranslate.xml b/libraries/ui-strings/src/main/res/values/donottranslate.xml deleted file mode 100755 index 910ce31c41..0000000000 --- a/libraries/ui-strings/src/main/res/values/donottranslate.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - Not implemented yet in ${app_name} - - - Cut the slack from teams. - - Crash the application. - - - © MapTiler © OpenStreetMap contributors - From fbc627c738444729495b907811fa462851f9277b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 11:41:10 +0200 Subject: [PATCH 164/251] Remove other unused resource. --- .../src/main/res/drawable/ic_homeserver.xml | 13 --------- .../res/drawable/onboarding_icon_light.png | Bin 44244 -> 0 bytes .../res/drawable/ic_baseline_reply_24.xml | 27 ------------------ .../res/drawable/ic_content_arrow_forward.xml | 27 ------------------ .../push/impl/src/main/res/values/dimens.xml | 21 -------------- 5 files changed, 88 deletions(-) delete mode 100644 features/login/impl/src/main/res/drawable/ic_homeserver.xml delete mode 100644 features/login/impl/src/main/res/drawable/onboarding_icon_light.png delete mode 100644 libraries/designsystem/src/main/res/drawable/ic_baseline_reply_24.xml delete mode 100644 libraries/designsystem/src/main/res/drawable/ic_content_arrow_forward.xml delete mode 100644 libraries/push/impl/src/main/res/values/dimens.xml diff --git a/features/login/impl/src/main/res/drawable/ic_homeserver.xml b/features/login/impl/src/main/res/drawable/ic_homeserver.xml deleted file mode 100644 index ee061f7007..0000000000 --- a/features/login/impl/src/main/res/drawable/ic_homeserver.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/features/login/impl/src/main/res/drawable/onboarding_icon_light.png b/features/login/impl/src/main/res/drawable/onboarding_icon_light.png deleted file mode 100644 index ffd8631c4777248a3eff841ae579fa91a724bc07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44244 zcmV)VK(D`vP)%0DYo!WJ}dXl=U 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 >|Jg77D<$&zDPx9@Nya}3+L4)zccC46W5uz#nx z54Kf`2p_H~gIGWFK5T!sb6)<~)_(r^g!|OL!tT=hmit*GxB;6>p10E)q+6nyyxtj0 zQ5Es4p}Gt7uj;!=Gz(M~X-)kSQS}x6*Y}Oh#_s{Mk(i2^fHbolIqnnt4W`>Cb|l+w zEi%Q4IwAN1wGzg%j;w`dr0Z52r97_ODnLqkT)H)+HJ5l$zDhThvdf`Z8uVNC`wjOg zY(rVzb{_K@nCG+-u&AaF_)$p+G1D>-tP&bF1+%dw!a|~{ zD#dYCX6==34BLlXLce``zAc=e_R=}UTDBD5kpT@Ovozmcx4!)4 zFAvX0D3-j_$o8}L#2k-3W_DTm{TSSD>Y~1Ha={7)ySnt;mfoLCQdOtSd0g-|q z5_>S%g@86Yy~;uU*%dO7?OZxI+_H@bAsx#DiB@FH*4B~?CPX4It3#Gu0RVxtK#DRb zZNKt1_T?C6emmYIge`4bR%Bq{KrSPFCShPqinIgYkcKhwO55op;xdo;R*R~<;v zCc`FFN z05e)|W0N}O85pQE_@%HduoD?L*d`1rvpbllga7cl92=5BhYiVqyXtLkQ-31gi$qmB z%YXullxM#;z3ENPLY&x!eZwF7S~NX&5XZ5N!4D%8Hc^IKh4Zlw+c+1W`Qv^#FUN6C zTQ6#h=zZ~b%{A8yBlp>lZS2E(@!Yzk5YTf)usX}HxuUN?F$s;0y>S@O&O70r&Buy% z0n^DnW-uKnE}%yqZRk8sN^m)LKWA=OYNvD>lTg75n#bu;k0qE6axZR4a+FIS}Q zjE*694l)bQ2CWOm8>NvKl$Za?y1Y?gNUsP3lG#!HH_VbnR7@Uv`OPRW^W#6hQ-4&- z&GW-kiV&u>%E6BmYAGsdP&MmvuBVC!i8qT%dX;-}>R?9u=_hG!A&lBI2_&i%j0||g zxmm&-s9T&%&Yhyt@@K7}_+wvNXFr_yY{OScyykSl;52_BJc=AMwsC&`bP|vOZx070-tC$2P=n>v*m9ZuF8md!^$;*IV6B*hmC;IJhid1Kpq9h* zv^-xoe0ChIHyiInR1WPKz2_6!f%;`hEiHpI=Ay6w*SL>9gJC>e&Vqn2NIWWLz!J>G^#Ed z-C1OOmrRJV9d}&4lM*WF-5SJJo^4u}5^oh6>gdHRR+ezX8kobKLs**m3w_bCvuu

);pai>8T`>ci+P%#st&GLD%)U)$L0H7d-tX=E6{#Xjo3Pg zW@h)4w-@Ksb&f0VGrAwD7}vE34cnI`$J%~!&!#%+{i zx;&nD%^*V7oPseYs@Qht2h(DMGHd0wN^cGYW+uA57x1QM7%yIRRa zcV{W5ah14NU)ttJ9Ll3WgsSP5662Qbr4yEHET6AzW96kd+`N=tGwQf5-_wSwxIycY zR+D_TeN`#Cp_W}3^oo^u7YLOBp&>)%BauDDwhe~cpbgrf4LTY9-$EUC<}w`m4FCWD M07*qoM6N<$g3+ZiZ2$lO diff --git a/libraries/designsystem/src/main/res/drawable/ic_baseline_reply_24.xml b/libraries/designsystem/src/main/res/drawable/ic_baseline_reply_24.xml deleted file mode 100644 index 96a220a5cd..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_baseline_reply_24.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_content_arrow_forward.xml b/libraries/designsystem/src/main/res/drawable/ic_content_arrow_forward.xml deleted file mode 100644 index 739053947d..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_content_arrow_forward.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - diff --git a/libraries/push/impl/src/main/res/values/dimens.xml b/libraries/push/impl/src/main/res/values/dimens.xml deleted file mode 100644 index ce2fee2015..0000000000 --- a/libraries/push/impl/src/main/res/values/dimens.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - 50dp - - From a6e11ad5ee5c9e2bb5e30f193140aef1cfbd00dd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 11:41:35 +0200 Subject: [PATCH 165/251] Ignore unused generated resource for Firebase. --- .../pushproviders/firebase/src/main/res/values/firebase.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/pushproviders/firebase/src/main/res/values/firebase.xml b/libraries/pushproviders/firebase/src/main/res/values/firebase.xml index 163717db91..b73238c79d 100644 --- a/libraries/pushproviders/firebase/src/main/res/values/firebase.xml +++ b/libraries/pushproviders/firebase/src/main/res/values/firebase.xml @@ -1,10 +1,10 @@ - + 912726360885-e87n3jva9uoj4vbidvijq78ebg02asv2.apps.googleusercontent.com https://vector-alpha.firebaseio.com 912726360885 AIzaSyAFZX8IhIfgzdOZvxDP_ISO5WYoU7jmQ5c AIzaSyAFZX8IhIfgzdOZvxDP_ISO5WYoU7jmQ5c vector-alpha.appspot.com - vector-alpha + vector-alpha From 5ab76eefe3d5c2416d7422f2faee8a8b68448ab8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 11:42:23 +0200 Subject: [PATCH 166/251] Lint: do not ignore unused resources (except for strings) --- tools/lint/lint.xml | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/tools/lint/lint.xml b/tools/lint/lint.xml index 914c9e7b68..7a04c735c0 100644 --- a/tools/lint/lint.xml +++ b/tools/lint/lint.xml @@ -43,24 +43,11 @@ - + - - - - - - - - - - - - - From 4361de9dc5e17d3ebbe8bb767710a3d48360c199 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 11:44:40 +0200 Subject: [PATCH 167/251] Ignore unused integer resources. --- libraries/androidutils/src/main/res/values/integers.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/androidutils/src/main/res/values/integers.xml b/libraries/androidutils/src/main/res/values/integers.xml index ecbfa4cdda..2f9e641bdf 100644 --- a/libraries/androidutils/src/main/res/values/integers.xml +++ b/libraries/androidutils/src/main/res/values/integers.xml @@ -15,9 +15,9 @@ ~ limitations under the License. --> - + - 1 - 0 + 1 + 0 From d656c29ea5c47dfc1ec43f916108400e7f314457 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 11:50:16 +0200 Subject: [PATCH 168/251] Fix lint false positive. --- .../features/createroom/impl/root/CreateRoomRootView.kt | 6 +++--- .../element/android/libraries/designsystem/VectorIcons.kt | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt index ac8a05f448..7aa5467b11 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt @@ -42,6 +42,7 @@ import androidx.compose.ui.unit.dp import io.element.android.features.createroom.impl.R import io.element.android.features.createroom.impl.components.UserListView import io.element.android.libraries.architecture.Async +import io.element.android.libraries.designsystem.VectorIcons import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.dialogs.RetryDialog import io.element.android.libraries.designsystem.preview.ElementPreviewDark @@ -55,7 +56,6 @@ import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings -import io.element.android.libraries.designsystem.R as DrawableR @OptIn(ExperimentalLayoutApi::class) @Composable @@ -162,12 +162,12 @@ fun CreateRoomActionButtonsList( ) { Column(modifier = modifier) { CreateRoomActionButton( - iconRes = DrawableR.drawable.ic_groups, + iconRes = VectorIcons.Groups, text = stringResource(id = R.string.screen_create_room_action_create_room), onClick = onNewRoomClicked, ) CreateRoomActionButton( - iconRes = DrawableR.drawable.ic_share, + iconRes = VectorIcons.Share, text = stringResource(id = CommonStrings.action_invite_friends_to_app, state.applicationName), onClick = onInvitePeopleClicked, ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/VectorIcons.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/VectorIcons.kt index 0e33567129..47b951baa2 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/VectorIcons.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/VectorIcons.kt @@ -25,4 +25,6 @@ object VectorIcons { val DoorOpen = R.drawable.ic_door_open_24 val DeveloperMode = R.drawable.ic_developer_mode val ReportContent = R.drawable.ic_report_content + val Groups = R.drawable.ic_groups + val Share = R.drawable.ic_share } From 3dda01a08ae5ab378b16e548e1704646fe8ab50e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 11:54:51 +0200 Subject: [PATCH 169/251] lint: remove unknown rule. --- tools/lint/lint.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/lint/lint.xml b/tools/lint/lint.xml index 7a04c735c0..fc775eb730 100644 --- a/tools/lint/lint.xml +++ b/tools/lint/lint.xml @@ -68,7 +68,6 @@ - From d78e1d32079f901cadf54688ed82605b33e16f7f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 11:55:49 +0200 Subject: [PATCH 170/251] lint: ImpliedQuantity is now error. --- tools/lint/lint.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lint/lint.xml b/tools/lint/lint.xml index fc775eb730..db1a20701c 100644 --- a/tools/lint/lint.xml +++ b/tools/lint/lint.xml @@ -25,7 +25,7 @@ - + From d3cea1e69c98767f01f437efcc98e88f67a55630 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 11:58:33 +0200 Subject: [PATCH 171/251] Sync strings. --- .../impl/src/main/res/values-de/translations.xml | 8 ++++---- .../impl/src/main/res/values-de/translations.xml | 4 ++-- .../impl/src/main/res/values-de/translations.xml | 8 ++++---- .../impl/src/main/res/values-de/translations.xml | 2 +- .../impl/src/main/res/values-fr/translations.xml | 12 +++++++++++- .../impl/src/main/res/values-de/translations.xml | 2 +- .../impl/src/main/res/values-de/translations.xml | 6 +++--- .../impl/src/main/res/values-fr/translations.xml | 2 +- .../impl/src/main/res/values-fr/translations.xml | 11 ++++++++--- .../src/main/res/values-de/translations.xml | 10 +++++----- .../ui-strings/src/main/res/values/localazy.xml | 1 + 11 files changed, 41 insertions(+), 25 deletions(-) diff --git a/features/analytics/impl/src/main/res/values-de/translations.xml b/features/analytics/impl/src/main/res/values-de/translations.xml index 979048344f..44890927a4 100644 --- a/features/analytics/impl/src/main/res/values-de/translations.xml +++ b/features/analytics/impl/src/main/res/values-de/translations.xml @@ -1,10 +1,10 @@ - "Wir erfassen und analysieren ""keine"" Account-Daten" + "Wir werden keine personenbezogenen Daten aufzeichnen oder auswerten" "Teile anonyme Nutzungsdaten, um uns bei der Identifizierung von Problemen zu helfen." - "Sie können alle unsere Nutzerbedingungen %1$s lesen." + "Du kannst alle unsere Nutzerbedingungen %1$s lesen." "hier" - "Sie können die Analyse jederzeit in den Einstellungen deaktivieren" + "Du kannst dies jederzeit deaktivieren" "Wir geben ""keine"" Informationen an Dritte weiter" - "Helfen Sie %1$s zu verbessern" + "Hilf uns, %1$s zu verbessern" diff --git a/features/invitelist/impl/src/main/res/values-de/translations.xml b/features/invitelist/impl/src/main/res/values-de/translations.xml index 1e2fcc2e86..2cec59d6a0 100644 --- a/features/invitelist/impl/src/main/res/values-de/translations.xml +++ b/features/invitelist/impl/src/main/res/values-de/translations.xml @@ -1,8 +1,8 @@ - "Möchten Sie den Beitritt zu %1$s wirklich ablehnen?" + "Möchtest du den Beitritt zu %1$s wirklich ablehnen?" "Einladung ablehnen" - "Möchten Sie den Chat mit %1$s wirklich ablehnen?" + "Möchtest du den privaten Chat mit %1$s wirklich ablehnen?" "Chat ablehnen" "Keine Einladungen" "%1$s (%2$s) hat dich eingeladen" diff --git a/features/login/impl/src/main/res/values-de/translations.xml b/features/login/impl/src/main/res/values-de/translations.xml index efc7c0cf3c..965cc30c4e 100644 --- a/features/login/impl/src/main/res/values-de/translations.xml +++ b/features/login/impl/src/main/res/values-de/translations.xml @@ -3,13 +3,13 @@ "Kontoanbieter wechseln" "Weiter" "Adresse des Homeservers" - "Geben Sie einen Suchbegriff oder eine Domainadresse ein." + "Gib einen Suchbegriff oder eine Domainadresse ein." "Suche nach einem Unternehmen, einer Community oder einem privaten Server." - "Finde einen Accountanbieter" + "Finde einen Kontoanbieter" "Hier werden deine Konversationen stattfinden — genauso wie du einen E-Mail-Anbieter verwenden würdest, um deine E-Mails aufzubewahren." "Du bist dabei dich bei %s anzumelden" "Hier werden deine Konversationen stattfinden — genauso wie du einen E-Mail-Anbieter verwenden würdest, um deine E-Mails aufzubewahren." - "Du bist dabei einen Account auf %s zu erstellen" + "Du bist dabei ein Konto auf %s zu erstellen" "Matrix.org ist ein offenes Netzwerk für sichere, dezentralisierte Kommunikation." "Andere" "Verwende einen anderen Kontoanbieter, z. B. deinen eigenen privaten Server oder ein Arbeitskonto." @@ -31,7 +31,7 @@ "Matrix ist ein offenes Netzwerk für sichere, dezentrale Kommunikation" "Hier werden deine Konversationen stattfinden — genau so wie du einen E-Mail-Anbieter verwenden würdest, um deine E-Mails aufzubewahren." "Du bist dabei dich bei %1$s anzumelden" - "Du bist dabei einen Account auf %1$s zu erstellen" + "Du bist dabei ein Konto auf %1$s zu erstellen" "Im Moment besteht eine hohe Nachfrage nach %1$s auf %2$s. Besuche die App in ein paar Tagen wieder und versuche es erneut. Vielen Dank für deine Geduld!" diff --git a/features/messages/impl/src/main/res/values-de/translations.xml b/features/messages/impl/src/main/res/values-de/translations.xml index 1486e35726..84c903844e 100644 --- a/features/messages/impl/src/main/res/values-de/translations.xml +++ b/features/messages/impl/src/main/res/values-de/translations.xml @@ -20,6 +20,6 @@ "Mehr anzeigen" "Erneut senden" "Ihre Nachricht konnte nicht gesendet werden" - "Fehler bei der Verarbeitung von Medien zum Hochladen, bitte versuchen Sie es erneut." + "Fehler bei der Verarbeitung von Medien zum Hochladen, bitte versuche es erneut." "Entfernen" diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml index 276fc30c94..5f9f0223aa 100644 --- a/features/messages/impl/src/main/res/values-fr/translations.xml +++ b/features/messages/impl/src/main/res/values-fr/translations.xml @@ -5,7 +5,7 @@ "%1$d changements dans la conversation" - "1 de plus" + "%1$d de plus" "%1$d de plus" "Appareil photo" @@ -19,6 +19,16 @@ "Vous êtes seul dans ce chat" "Message copié" "Vous n‘avez pas le droit de poster dans ce salon" + "Autoriser les paramètres personnalisés" + "Activer cette option remplacera votre paramètre par défaut" + "Me notifier dans ce chat pour" + "paramètres généraux" + "Paramètre par défaut" + "Une erreur s’est produite lors du chargement des paramètres de notification." + "Impossible de restaurer le mode par défaut, veuillez réessayer." + "Impossible de régler le mode, veuillez réessayer." + "Tous les messages" + "Mentions et mots-clés uniquement" "Afficher moins" "Afficher plus" "Renvoyer" diff --git a/features/onboarding/impl/src/main/res/values-de/translations.xml b/features/onboarding/impl/src/main/res/values-de/translations.xml index e36fa31a2b..82e20c3509 100644 --- a/features/onboarding/impl/src/main/res/values-de/translations.xml +++ b/features/onboarding/impl/src/main/res/values-de/translations.xml @@ -4,7 +4,7 @@ "Mit QR-Code anmelden" "Konto erstellen" "Sicher kommunizieren und zusammenarbeiten" - "Willkommen beim schnellsten Element jemals. Optimiert für Geschwindigkeit und Einfachheit." + "Willkommen beim schnellsten Element aller Zeiten. Optimiert für Geschwindigkeit und Einfachheit." "Willkommen zur %1$s. Verbessert, für Geschwindigkeit und Einfachheit." "Sei in deinem Element" diff --git a/features/rageshake/impl/src/main/res/values-de/translations.xml b/features/rageshake/impl/src/main/res/values-de/translations.xml index a24318545d..b316d8b45e 100644 --- a/features/rageshake/impl/src/main/res/values-de/translations.xml +++ b/features/rageshake/impl/src/main/res/values-de/translations.xml @@ -1,14 +1,14 @@ "Bildschirmfoto anhängen" - "Sie können mich kontaktieren, wenn Sie weitere Fragen haben" + "Ihr könnt mich kontaktieren, wenn ihr weitere Fragen habt" "Kontaktiere mich" "Bildschirmfoto bearbeiten" "Beschreibe bitte den Fehler. Was hast du gemacht? Was hätte passieren sollen? Was ist passiert? Bitte beschreibe alles mit so vielen Details wie möglich." "Beschreibe den Fehler…" - "Wenn möglich, verfassen Sie die Beschreibung bitte auf Englisch." + "Wenn möglich, verfasse die Beschreibung bitte auf Englisch." "Absturzprotokolle senden" - "Senden Sie Protokolle, um zu helfen" + "Logs zulassen" "Bildschirmfoto senden" "Um zu überprüfen, ob alles wie vorgesehen funktioniert, werden Protokolle mit deiner Nachricht gesendet. Diese werden privat sein. Um nur Ihre Nachricht zu senden, schalte diese Einstellung aus." "%1$s ist bei der letzten Verwendung abgestürzt. Möchtest du uns einen Absturzbericht senden?" diff --git a/features/rageshake/impl/src/main/res/values-fr/translations.xml b/features/rageshake/impl/src/main/res/values-fr/translations.xml index bf6ad2d215..53b95af6be 100644 --- a/features/rageshake/impl/src/main/res/values-fr/translations.xml +++ b/features/rageshake/impl/src/main/res/values-fr/translations.xml @@ -1,7 +1,7 @@ "Joindre une capture d\'écran" - "Vous pouvez me contacter si vous avez des questions complémentaires" + "Vous pouvez me contacter si vous avez des questions complémentaires." "Me contacter" "Modifier la capture d\'écran" "S\'il vous plait, veuillez décrire le bogue. Qu\'avez-vous fait ? À quoi vous attendiez-vous ? Que s\'est-il réellement passé. Veuillez ajouter le plus de détails possible." diff --git a/features/roomdetails/impl/src/main/res/values-fr/translations.xml b/features/roomdetails/impl/src/main/res/values-fr/translations.xml index ee34445805..2696cc99ea 100644 --- a/features/roomdetails/impl/src/main/res/values-fr/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-fr/translations.xml @@ -1,7 +1,7 @@ - "1 membre" + "%1$d membre" "%1$d membres" "Définir un sujet" @@ -12,17 +12,22 @@ "Impossible de mettre à jour le salon" "Les messages sont sécurisés par des cadenas numériques. Seuls vous et les destinataires possédez les clés uniques pour les déverrouiller." "Chiffrement des messages activé" + "Une erreur s’est produite lors du chargement des paramètres de notification." + "Impossible de désactiver les notifications de cette salle, veuillez réessayer." + "Impossible de réactiver les notifications de cette salle, veuillez réessayer." "Inviter des personnes" + "Personnalisé" + "Par défaut" "Notifications" "Nom du salon" "Partager le salon" "Mise à jour du salon…" "En attente" "Bloquer" - "Les utilisateurs bloqués ne pourront pas vous envoyer de messages et tous leurs messages seront masqués. Vous pouvez annuler cette action à tout moment." + "Les utilisateurs bloqués ne pourront pas vous envoyer de messages et tous leurs messages seront masqués. Vous pouvez les débloquer à tout moment." "Bloquer l\'utilisateur" "Débloquer" - "Lorsque vous débloquez l\'utilisateur, vous pourrez à nouveau voir tous leur messages." + "Vous pourrez à nouveau voir tous leurs messages." "Débloquer l\'utilisateur" "Quitter le salon" "Personnes" diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index 10694181da..0f5259e663 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -94,7 +94,7 @@ "Passwort" "Personen" "Permalink" - "Datenschutzerklärung" + "Datenschutz­erklärung" "Reaktionen" "Aktualisiere…" "Auf %1$s antworten" @@ -145,7 +145,7 @@ "%1$s konnte nicht auf deinen Standort zugreifen. Bitte versuche es später erneut." "Einige Nachrichten wurden nicht gesendet" "Entschuldigung, ein Fehler ist aufgetreten." - "🔐️ Besuchen Sie mich auf %1$s" + "🔐️ Besuche mich auf %1$s" "Hey, sprich mit mir auf %1$s: %2$s" "Bist du sicher, dass du diesen Raum verlassen willst? Du bist die einzige Person hier. Wenn du gehst, kann in Zukunft niemand mehr beitreten, auch du nicht." "Bist du dir sicher, dass du den Raum verlassen möchtest? Dieser Raum ist nicht öffentlich und du kannst ihm ohne eine Einladung nicht mehr beitreten." @@ -164,10 +164,10 @@ "Neu" "Teile Analyse-Daten" "Medienauswahl fehlgeschlagen, bitte versuche es erneut." - "Fehler bei der Verarbeitung von Medien zum Hochladen, bitte versuchen Sie es erneut." + "Fehler bei der Verarbeitung von Medien zum Hochladen, bitte versuche es erneut." "Hochladen von Medien fehlgeschlagen, bitte versuchen Sie es erneut." "Dies ist ein einmaliger Vorgang, danke fürs Warten." - "Deinen Account einrichten" + "Dein Konto einrichten" "Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest" "Standort teilen" "Meinen Standort teilen" @@ -184,7 +184,7 @@ "Fehler" "Erfolg" "Teile anonyme Nutzungsdaten, um uns bei der Identifizierung von Problemen zu helfen." - "Sie können alle unsere Nutzerbedingungen %1$s lesen." + "Du kannst alle unsere Nutzerbedingungen %1$s lesen." "hier" "Nutzer blockieren" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index b17d8f337f..2ea156d327 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -40,6 +40,7 @@ "Open with" "Quick reply" "Quote" + "React" "Remove" "Reply" "Report bug" From 1bfbef1ab5c2029e42b00763774a5ee3f55a2fad Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 12:59:16 +0200 Subject: [PATCH 172/251] Previous code was `throw IllegalStateException()`, so keep the existing error, even if it should never happen, being compliant with `UseCheckOrError` detekt rule. --- .../io/element/android/libraries/maplibre/compose/MapboxMap.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt index 82b5e1c237..5af79e7524 100644 --- a/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt +++ b/libraries/maplibre-compose/src/main/kotlin/io/element/android/libraries/maplibre/compose/MapboxMap.kt @@ -236,7 +236,7 @@ private fun MapView.lifecycleObserver(previousState: MutableState { //handled in onDispose } - Lifecycle.Event.ON_ANY -> Unit + Lifecycle.Event.ON_ANY -> error("ON_ANY should never be used") } previousState.value = event } From 6b916ae9ad24a42360e231f0973d0951aceb72f1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 13:04:39 +0200 Subject: [PATCH 173/251] Add link to the blog post announcing Element X --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f924ac168a..f24efcd828 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ Element X Android is a [Matrix](https://matrix.org/) Android Client provided by The application is a total rewrite of [Element-Android](https://github.com/vector-im/element-android) using the [Matrix Rust SDK](https://github.com/matrix-org/matrix-rust-sdk) underneath and targeting devices running Android 6+. The UI layer is written using [Jetpack Compose](https://developer.android.com/jetpack/compose), and the navigation is managed using [Appyx](https://github.com/bumble-tech/appyx). +Learn more about why we are building Element X in our blog post: [https://element.io/blog/element-x-experience-the-future-of-element/](https://element.io/blog/element-x-experience-the-future-of-element/). + ## Table of contents From 952238d5fecec56426b3491c69ea14bd9964b535 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 13:24:34 +0200 Subject: [PATCH 174/251] Detect missing translations. --- tools/check/check_code_quality.sh | 19 +++++++++--- tools/check/forbidden_strings_in_xml.txt | 38 ++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 4 deletions(-) create mode 100755 tools/check/forbidden_strings_in_xml.txt diff --git a/tools/check/check_code_quality.sh b/tools/check/check_code_quality.sh index 9e8c964499..f37d661379 100755 --- a/tools/check/check_code_quality.sh +++ b/tools/check/check_code_quality.sh @@ -38,7 +38,7 @@ else fi echo -echo "Search for forbidden patterns in code..." +echo "Search for forbidden patterns in Kotlin source files..." # list all Kotlin folders of the project. allKotlinDirs=`find . -type d |grep -v build |grep -v \.git |grep -v \.gradle |grep kotlin$` @@ -47,9 +47,20 @@ ${searchForbiddenStringsScript} ./tools/check/forbidden_strings_in_code.txt $all resultForbiddenStringInCode=$? -if [[ ${resultForbiddenStringInCode} -eq 0 ]]; then - echo "MAIN OK" +echo +echo "Search for forbidden patterns in XML resource files..." + +# list all res folders of the project. +allResDirs=`find . -type d |grep -v build |grep -v \.git |grep -v \.gradle |grep /res$` + +${searchForbiddenStringsScript} ./tools/check/forbidden_strings_in_xml.txt $allResDirs + +resultForbiddenStringInXml=$? + +if [[ ${resultForbiddenStringInCode} -eq 0 ]] \ + && [[ ${resultForbiddenStringInXml} -eq 0 ]]; then + echo "OK" else - echo "❌ MAIN ERROR" + echo "❌ ERROR, please check the logs above." exit 1 fi diff --git a/tools/check/forbidden_strings_in_xml.txt b/tools/check/forbidden_strings_in_xml.txt new file mode 100755 index 0000000000..ee0e4c7136 --- /dev/null +++ b/tools/check/forbidden_strings_in_xml.txt @@ -0,0 +1,38 @@ +# +# Copyright 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. +# + +# This file list String which are not allowed in resource. +# Use Perl regex to write forbidden strings +# Note: line cannot start with a space. Use \s instead. +# It is possible to specify an authorized number of occurrence with === suffix. Default is 0 +# Example: +# AuthorizedStringThreeTimes===3 + +# Extension:xml + +### Empty tag detected. Empty translation or plurals? +">"">>>>>> + +### "DO NOT COMMIT" has been committed +DO NOT COMMIT + +### Tab char is forbidden. Use only spaces +\t From e351e87dbccec2aa94bc9e3257fdeb630fae4a85 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Thu, 27 Jul 2023 14:55:58 +0200 Subject: [PATCH 175/251] [Compound] Implement platform components (Switch, RadioButton, Checkbox) (#982) * Create our custom Switch component * Update RadioButton colors * Update Checkbox colors * Fix padding in `ReplyToContent` * Add `indeterminate` and `hasError` parameters to `CheckBox`. Improve previews. * Improve Switch previews. * Improve RadioButton previews. --------- Co-authored-by: ElementBot --- .../components/TimelineItemEventRow.kt | 2 +- .../preferences/PreferenceSwitch.kt | 2 +- .../designsystem/theme/components/Checkbox.kt | 84 +++++++++++++++-- .../theme/components/RadioButton.kt | 29 ++++-- .../designsystem/theme/components/Switch.kt | 89 +++++++++++++++++++ .../components/previews/SwitchPreview.kt | 51 ----------- ...tionDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...ionLightPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...sultItemPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_7,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_7,NEXUS_5,1.0,en].png | 4 +- ...eplyDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...plyLightPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +- ...s_SwitchPreview_0_null,NEXUS_5,1.0,en].png | 3 - ...eckboxesPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...ioButtonPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...s_SwitchPreview_0_null,NEXUS_5,1.0,en].png | 3 + ...owDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...owDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...dUserRowPreview_0_null,NEXUS_5,1.0,en].png | 4 +- 52 files changed, 282 insertions(+), 157 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Switch.kt delete mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/SwitchPreview.kt delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_Toggles_SwitchPreview_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_SwitchPreview_0_null,NEXUS_5,1.0,en].png diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt index 4fb6e65797..0099c958ca 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt @@ -479,7 +479,7 @@ private fun ReplyToContent( val paddings = if (attachmentThumbnailInfo != null) { PaddingValues(start = 4.dp, end = 12.dp, top = 4.dp, bottom = 4.dp) } else { - PaddingValues(start = 12.dp, end = 12.dp, top = 8.dp, bottom = 4.dp) + PaddingValues(horizontal = 12.dp, vertical = 4.dp) } Row( modifier diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSwitch.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSwitch.kt index bbd3583688..b9a8d267f5 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSwitch.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/preferences/PreferenceSwitch.kt @@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Announcement -import androidx.compose.material3.Switch import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -37,6 +36,7 @@ import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.components.preferences.components.PreferenceIcon import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.designsystem.theme.components.Switch import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.toEnabledColor import io.element.android.libraries.designsystem.toSecondaryEnabledColor diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Checkbox.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Checkbox.kt index b754b3d420..a1a854ede1 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Checkbox.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Checkbox.kt @@ -17,15 +17,25 @@ package io.element.android.libraries.designsystem.theme.components import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.material3.CheckboxColors import androidx.compose.material3.CheckboxDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.theme.ElementTheme + +// Designs in https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&mode=design&t=qb99xBP5mwwCtGkN-1 @Composable fun Checkbox( @@ -33,12 +43,22 @@ fun Checkbox( onCheckedChange: ((Boolean) -> Unit)?, modifier: Modifier = Modifier, enabled: Boolean = true, - colors: CheckboxColors = CheckboxDefaults.colors(), + hasError: Boolean = false, + indeterminate: Boolean = false, + colors: CheckboxColors = if (hasError) compoundErrorCheckBoxColors() else compoundCheckBoxColors(), interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } ) { - androidx.compose.material3.Checkbox( - checked = checked, - onCheckedChange = onCheckedChange, + var indeterminateState by remember { mutableStateOf(indeterminate) } + androidx.compose.material3.TriStateCheckbox( + state = if (!checked && indeterminateState) ToggleableState.Indeterminate else ToggleableState(checked), + onClick = if (onCheckedChange != null) { + { + indeterminateState = false + onCheckedChange(!checked) + } + } else { + null + }, modifier = modifier, enabled = enabled, colors = colors, @@ -46,6 +66,30 @@ fun Checkbox( ) } +@Composable +private fun compoundCheckBoxColors(): CheckboxColors { + return CheckboxDefaults.colors( + checkedColor = ElementTheme.materialColors.primary, + uncheckedColor = ElementTheme.colors.borderInteractivePrimary, + checkmarkColor = ElementTheme.materialColors.onPrimary, + disabledUncheckedColor = ElementTheme.colors.borderDisabled, + disabledCheckedColor = ElementTheme.colors.iconDisabled, + disabledIndeterminateColor = ElementTheme.colors.iconDisabled, + ) +} + +@Composable +private fun compoundErrorCheckBoxColors(): CheckboxColors { + return CheckboxDefaults.colors( + checkedColor = ElementTheme.materialColors.error, + uncheckedColor = ElementTheme.materialColors.error, + checkmarkColor = ElementTheme.materialColors.onPrimary, + disabledUncheckedColor = ElementTheme.colors.borderDisabled, + disabledCheckedColor = ElementTheme.colors.iconDisabled, + disabledIndeterminateColor = ElementTheme.colors.iconDisabled, + ) +} + @Preview(group = PreviewGroup.Toggles) @Composable internal fun CheckboxesPreview() = ElementThemedPreview(vertical = false) { ContentToPreview() } @@ -53,9 +97,33 @@ internal fun CheckboxesPreview() = ElementThemedPreview(vertical = false) { Cont @Composable private fun ContentToPreview() { Column { - Checkbox(onCheckedChange = {}, enabled = true, checked = true) - Checkbox(onCheckedChange = {}, enabled = true, checked = false) - Checkbox(onCheckedChange = {}, enabled = false, checked = true) - Checkbox(onCheckedChange = {}, enabled = false, checked = false) + // Unchecked + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + Checkbox(onCheckedChange = {}, enabled = true, checked = false) + Checkbox(onCheckedChange = {}, enabled = false, checked = false) + } + // Checked + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + Checkbox(onCheckedChange = {}, enabled = true, checked = true) + Checkbox(onCheckedChange = {}, enabled = false, checked = true) + } + // Indeterminate + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + Checkbox(onCheckedChange = {}, enabled = true, checked = false, indeterminate = true) + Checkbox(onCheckedChange = {}, enabled = false, checked = false, indeterminate = true) + } + // Error + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + Checkbox(hasError = true, onCheckedChange = {}, checked = false) + Checkbox(hasError = true, onCheckedChange = {}, enabled = false, checked = false) + } + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + Checkbox(hasError = true, onCheckedChange = {}, enabled = true, checked = true) + Checkbox(hasError = true, onCheckedChange = {}, enabled = false, checked = true) + } + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + Checkbox(onCheckedChange = {}, enabled = true, checked = false, indeterminate = true, hasError = true) + Checkbox(onCheckedChange = {}, enabled = false, checked = false, indeterminate = true, hasError = true) + } } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/RadioButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/RadioButton.kt index 6b0c1b377e..a8b186a6b2 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/RadioButton.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/RadioButton.kt @@ -17,15 +17,21 @@ package io.element.android.libraries.designsystem.theme.components import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.material3.RadioButtonColors import androidx.compose.material3.RadioButtonDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.theme.ElementTheme + +// Designs in https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24202&mode=design&t=qb99xBP5mwwCtGkN-1 @Composable fun RadioButton( @@ -33,7 +39,7 @@ fun RadioButton( onClick: (() -> Unit)?, modifier: Modifier = Modifier, enabled: Boolean = true, - colors: RadioButtonColors = RadioButtonDefaults.colors(), + colors: RadioButtonColors = compoundRadioButtonColors(), interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } ) { androidx.compose.material3.RadioButton( @@ -46,6 +52,15 @@ fun RadioButton( ) } +@Composable +internal fun compoundRadioButtonColors(): RadioButtonColors { + return RadioButtonDefaults.colors( + unselectedColor = ElementTheme.colors.borderInteractivePrimary, + disabledUnselectedColor = ElementTheme.colors.borderDisabled, + disabledSelectedColor = ElementTheme.colors.iconDisabled, + ) +} + @Preview(group = PreviewGroup.Toggles) @Composable internal fun RadioButtonPreview() = ElementThemedPreview(vertical = false) { ContentToPreview() } @@ -53,9 +68,13 @@ internal fun RadioButtonPreview() = ElementThemedPreview(vertical = false) { Con @Composable private fun ContentToPreview() { Column { - RadioButton(selected = false, onClick = {}) - RadioButton(selected = true, onClick = {}) - RadioButton(selected = false, enabled = false, onClick = {}) - RadioButton(selected = true, enabled = false, onClick = {}) + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + RadioButton(selected = false, onClick = {}) + RadioButton(selected = false, enabled = false, onClick = {}) + } + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + RadioButton(selected = true, onClick = {}) + RadioButton(selected = true, enabled = false, onClick = {}) + } } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Switch.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Switch.kt new file mode 100644 index 0000000000..ab4c9dee05 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Switch.kt @@ -0,0 +1,89 @@ +/* + * 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.libraries.designsystem.theme.components + +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.SwitchColors +import androidx.compose.material3.SwitchDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.preview.ElementThemedPreview +import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.theme.ElementTheme +import androidx.compose.material3.Switch as Material3Switch + +// Designs in https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=425%3A24203&mode=design&t=qb99xBP5mwwCtGkN-1 + +@Composable +fun Switch( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier = Modifier, + enabled: Boolean = true, + colors: SwitchColors = compoundSwitchColors(), + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + thumbContent: (@Composable () -> Unit)? = null, +) { + Material3Switch( + checked = checked, + onCheckedChange = onCheckedChange, + modifier = modifier, + enabled = enabled, + colors = colors, + interactionSource = interactionSource, + thumbContent = thumbContent + ) +} + +@Composable +internal fun compoundSwitchColors() = SwitchDefaults.colors( + uncheckedThumbColor = ElementTheme.colors.bgActionPrimaryRest, + uncheckedTrackColor = Color.Transparent, + disabledUncheckedBorderColor = ElementTheme.colors.borderDisabled, + disabledUncheckedThumbColor = ElementTheme.colors.iconDisabled, + disabledCheckedTrackColor = ElementTheme.colors.iconDisabled, + disabledCheckedBorderColor = ElementTheme.colors.iconDisabled, +) + +@Preview(group = PreviewGroup.Toggles) +@Composable +internal fun SwitchPreview() { + var checked by remember { mutableStateOf(false) } + ElementThemedPreview { + Column(modifier = Modifier.padding(10.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) { + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + Switch(checked = checked, onCheckedChange = { checked = !checked }) + Switch(enabled = false, checked = checked, onCheckedChange = { checked = !checked }) + } + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + Switch(checked = !checked, onCheckedChange = { checked = !checked }) + Switch(enabled = false, checked = !checked, onCheckedChange = { checked = !checked }) + } + } + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/SwitchPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/SwitchPreview.kt deleted file mode 100644 index 11491a0a1c..0000000000 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/SwitchPreview.kt +++ /dev/null @@ -1,51 +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.libraries.designsystem.theme.components.previews - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Row -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.Check -import androidx.compose.material3.Switch -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.preview.ElementThemedPreview -import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.theme.components.Icon - -@Preview(group = PreviewGroup.Toggles) -@Composable -internal fun SwitchPreview() { - ElementThemedPreview { - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - var checked by remember { mutableStateOf(false) } - Switch(checked = checked, onCheckedChange = { checked = !checked }) - Switch(checked = checked, onCheckedChange = { checked = !checked }, thumbContent = { - Icon(imageVector = Icons.Outlined.Check, contentDescription = null) - }) - Switch(checked = checked, enabled = false, onCheckedChange = { checked = !checked }) - Switch(checked = checked, enabled = false, onCheckedChange = { checked = !checked }, thumbContent = { - Icon(imageVector = Icons.Outlined.Check, contentDescription = null) - }) - } - } -} diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_RoomPrivacyOptionDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_RoomPrivacyOptionDarkPreview_0_null,NEXUS_5,1.0,en].png index 9e1ddd2ddb..e4ad0b75e4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_RoomPrivacyOptionDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_RoomPrivacyOptionDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d47034cafef80cdac5d702e52092f07ebc0bb96dbdd7f9f0c1ea2e302cbe9917 -size 33858 +oid sha256:7b42afece0186cbbcb9ca5b716c3c535147d27bf1c57cabf033339fe9100edeb +size 33889 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_RoomPrivacyOptionLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_RoomPrivacyOptionLightPreview_0_null,NEXUS_5,1.0,en].png index d50e567f72..5d7bb98cc3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_RoomPrivacyOptionLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_RoomPrivacyOptionLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:84ea420320bd406588cb4d9aa3fea79d63ec37fc31a00d4fa77a06a7c8499145 -size 35452 +oid sha256:248817743f94c09d5bb02580687f5d9150dcf22b1670289955197491f375ad65 +size 35640 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemPreview_0_null,NEXUS_5,1.0,en].png index 6595be90cb..687420defd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d01eaadfd0b9508c44145ba2e6cd85a6a24fdb43f665188404f9da1cf3e03321 -size 86292 +oid sha256:a4f987d8b03013d3aacbcc2b5a7374148c29d2c24dc41211781db15a06f3d52a +size 86365 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_UserListViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_UserListViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png index 3245d6013a..2e34348af3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_UserListViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_UserListViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:45f4c5a3edbf61d815a7b1221a73123b09c8767d15ae2d9ae04f11ae3defe697 -size 67911 +oid sha256:90a7a6bc46fea5d6a02c9e3b6a29018e51fe270fc5034fdd7f9045f6fbbbc1d9 +size 67916 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_UserListViewLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_UserListViewLightPreview_0_null_6,NEXUS_5,1.0,en].png index 879d41bd8a..4f7bc094a8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_UserListViewLightPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_UserListViewLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6811324cfc868722c57a02f2544ffcbbb2f2247fc9284e4e6fe869e33447c7b1 -size 69939 +oid sha256:037e24e82436183ed2ff4138a997c232e331076dd5374958e11efcfa8063b4d2 +size 69962 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 74124f825f..80cfcc631c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0169cdedfc56ba1b55f133ed41146a0ec3597123a88ed086f7584f06d235da26 -size 57732 +oid sha256:3cb08dc14db764d1bc432dc58374cad50da83c5e30e7e48077b6f400aa7a3c9b +size 57811 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 50ee002c0c..9dcb049366 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a0cdeea89592fbad03e1ad29aa1616bd779403f022097ca11510ff9cda3457aa -size 83656 +oid sha256:d0a9c923401980b00b756159f9c77f153892b79cc2b806150734b6bd8f67fe4d +size 83675 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 6c4a4bcaba..eb7fecd069 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f61e37505ee1276d894641647ec34323f415f4a3cdc699de01579a92255c2a4e -size 60941 +oid sha256:4e543f94df5f2de717e7e870fe043ecf72b29aeaca23d33b9b16af76e2cc1636 +size 61222 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index c1f3859c0a..81b39fcbeb 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:630fa772c15d8f16461655b2c2763d8ca85dc29812fea7d01366d68e3a446eaf -size 86660 +oid sha256:1b94a9fdcf1f2c62fa2a0f574210b85de96ba068ba17ba562af79f21d7a38f8f +size 86854 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 9685f02b12..90ddee2001 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:911217e791dc46e76b4866efc7eed59dc721795b0434f0a985078a9847347980 -size 26652 +oid sha256:71cee553053649ba9c83f5219d367d860897bc1ca3a5a0c35a234563d6bb5c16 +size 26604 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index b1f7c53c54..d48b777667 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1ccba126f5fef03ee24c6089088b2014911452a7c54eeab3caa49389b9859d3 -size 26253 +oid sha256:599e2496f9364d55dcc0cdb48b4b4919c052b0a66634d69bbb40834b03559b97 +size 26204 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 882791b675..d5c01a3a16 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:093b9faa97e65ad0108882964e8e8af7a980a2fdbde32d8ff83d8420cf8b6f95 -size 26425 +oid sha256:4561451a8bbd583ffd2bb38be5c41fe9ae787b54500cc4c14af8d85989558fec +size 26387 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 882791b675..d5c01a3a16 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:093b9faa97e65ad0108882964e8e8af7a980a2fdbde32d8ff83d8420cf8b6f95 -size 26425 +oid sha256:4561451a8bbd583ffd2bb38be5c41fe9ae787b54500cc4c14af8d85989558fec +size 26387 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png index 882791b675..d5c01a3a16 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:093b9faa97e65ad0108882964e8e8af7a980a2fdbde32d8ff83d8420cf8b6f95 -size 26425 +oid sha256:4561451a8bbd583ffd2bb38be5c41fe9ae787b54500cc4c14af8d85989558fec +size 26387 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 80f1c30896..0772a81b3f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:162b68e63538b71e1b3a265281bebd7f56df3cd624caeafa94ab80d46326e2d4 -size 28162 +oid sha256:d3383f2ec12b187ad1d6402fb852c98d8041376c6ecee372e91529554bd30994 +size 28286 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index 099554b6c0..0f9de74d6d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:775effbea7088ea32b6eef036968d0be99bc267d4fdea2269d3ab1101f0ee240 -size 27542 +oid sha256:6665acc104fcc5477be20460ea5b3ba7cd75491bcd3c7df6fa244747ab55706c +size 27690 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index 739e84de45..a8a622afed 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95313cb1717aabbb275b4ed039741da2ddf95af02d177f426b354701cc9badd9 -size 27947 +oid sha256:00e3ad9d2805d4d8e856e30226e0da4297ef5ae25c1e3b5760bf23e0611a7b97 +size 27977 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index 739e84de45..a8a622afed 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95313cb1717aabbb275b4ed039741da2ddf95af02d177f426b354701cc9badd9 -size 27947 +oid sha256:00e3ad9d2805d4d8e856e30226e0da4297ef5ae25c1e3b5760bf23e0611a7b97 +size 27977 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_7,NEXUS_5,1.0,en].png index 739e84de45..a8a622afed 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95313cb1717aabbb275b4ed039741da2ddf95af02d177f426b354701cc9badd9 -size 27947 +oid sha256:00e3ad9d2805d4d8e856e30226e0da4297ef5ae25c1e3b5760bf23e0611a7b97 +size 27977 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png index dd1ef38d17..48efd89d01 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c00bf46d1ef6337bad18f6454cf4601141ed2190c6a697ddc46d5a156d4997ca -size 127950 +oid sha256:26878fecd5785eed15a1fa46fabb0b2942139bf86dbce15ae544bfa5edf791e5 +size 127652 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png index dce8da362e..57d6316ca8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88cd76c95c31061bf4e9e7528ed5d66a8b49813d868f21b2d2615e77318a9706 -size 133068 +oid sha256:cbdc89d08cd8e689bb603437dbbbeccab2870740752af313cd8b60ae48aee839 +size 132950 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.analytics_null_DefaultGroup_AnalyticsSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.analytics_null_DefaultGroup_AnalyticsSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 923e05f0fc..9f41d45892 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.analytics_null_DefaultGroup_AnalyticsSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.analytics_null_DefaultGroup_AnalyticsSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba708a48280a4f42196d61b0cfbb7f0bf6e4a818b8e76ba981e5cbacff37c9dd -size 25493 +oid sha256:e8c4254a13c72f1e6e710caafb173623697b3aba5472ddbf14a9a8a8a16722d1 +size 25454 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.analytics_null_DefaultGroup_AnalyticsSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.analytics_null_DefaultGroup_AnalyticsSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 9358cd9f18..4839079fb2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.analytics_null_DefaultGroup_AnalyticsSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.analytics_null_DefaultGroup_AnalyticsSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8b75a4207e4dc3fc0e3a124574e0352eeb7e731558d90b4d19060d3a046b76e7 -size 26656 +oid sha256:bfa0258b099d0fb6451eddcf38dc2184bad611355f56054ede8c874ea28b9fe8 +size 26579 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 597ebdcb61..b87cf23f84 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c0a16524e1017274eeb0f07a50a47c6351af9a901355b54cccbc9ec799e79f4 -size 45174 +oid sha256:4a9a3902dd33ab3e528fa9c534302228113cc468914025766a21397ad3eda9e2 +size 45064 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 597ebdcb61..b87cf23f84 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c0a16524e1017274eeb0f07a50a47c6351af9a901355b54cccbc9ec799e79f4 -size 45174 +oid sha256:4a9a3902dd33ab3e528fa9c534302228113cc468914025766a21397ad3eda9e2 +size 45064 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index e1ba6b44d4..30360f9f5e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:abb7854a1e47764a907434cdf32e138b95c947bab682da485eea86f9726cf3b9 -size 49946 +oid sha256:cd1cc710443af62efddf0ea165a6289912f471f669803255368d3266b872f5f6 +size 49829 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index e1ba6b44d4..30360f9f5e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.preferences.impl.developer_null_DefaultGroup_DeveloperSettingsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:abb7854a1e47764a907434cdf32e138b95c947bab682da485eea86f9726cf3b9 -size 49946 +oid sha256:cd1cc710443af62efddf0ea165a6289912f471f669803255368d3266b872f5f6 +size 49829 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 0a43709e00..c4fe8ba2ee 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07e1283e5ac86cf9c77b0695a6622682e7257abce1e7787440f50c8f42ca0291 -size 64632 +oid sha256:5ef9e33bf65764ef25b4184c9f49be8befe4f6fad77d5f6fafc5ffcb7c09fb51 +size 64682 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index e26b93adf7..888cba3741 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dac098de353f8dce2d8eea77bccdcebf997c642f05afaa15c02a9bcf230fa37a -size 200158 +oid sha256:a7f9a86df68cb0595c23c86e7b91f73af55531dd0495f0409471fb94165bdeb6 +size 200178 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index da4cf4461a..073cfa83f8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4b2f7af207e8d2f951474575de12969e0fdf0831ce68fdfde5b67b364c67156 -size 55013 +oid sha256:4ce0000ec9242c0b65fb06d1fea4622143bda5bebb691f8114890edf47f341ec +size 55166 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 5ebb85150a..1ca257c3ff 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:91fcd5dc74788d4f597dfc265a1d6a7511e5f4db47323d930f7c54c7df7d62bd -size 67464 +oid sha256:cbe0ca40022613ac7df059bad8a8cb3f60b1242a0d28b3cd2b5a59a7a26d828d +size 67488 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index b999544c27..6eb72a9709 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3583a0758b9c02c7e165dc593f7a090a54d70bcfa0f39853ba3bddb42850d404 -size 204354 +oid sha256:d10808329ac9280334c5bad3230eace88eb768a9e13b7d736c4e99cc74124c84 +size 204325 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 784d1dbe36..b9be444888 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3dac3ed1e6c46265e8b0f7bb479042271668178ff2ad34baae3cb2d777c7902a -size 59091 +oid sha256:bd04de63275687ad4a905a3438f47898f864ff75591f6fc191d074550787661d +size 59880 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 4c91cdb1a9..87e6a4037f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc5794eba22329603285fa264519fd9c4c7dae4ddb867ebe54f5c87f4af646fb -size 45438 +oid sha256:da3f4846d648ab399604db869980c41243ba6e7f91f16365cdae00305ce5723e +size 45416 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png index 58e7a0d034..311cf7c8e0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f67bdbeedafdaf59f1bb643d86dbfdf10442900d7d426e2e31cf62ed5fe43d9d -size 38555 +oid sha256:0fc4fe77d849f04f55c5142f95493a1a05cf2ba44293b5fe81414336d1687dad +size 38540 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png index 3e1241affa..f051ab3fff 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af4c302e55c391b0aa69a418f80f8df514157a9c30d3f33b28cf6c322c458182 -size 46942 +oid sha256:d1b2742a29099293462ef6a020f4440143db9512b70b5638d09af685c012bb11 +size 46910 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png index 37ba85ea50..ddf4f582db 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aca6a614db1bcaec2a0b8ad4c3a64c761fda757e32865f535e75f470fda97774 -size 40683 +oid sha256:0a5e412535f5c0fc384983f0b58319151c2e60a7b30f4f1e89a4411717ed93eb +size 40701 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_Toggles_SwitchPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_Toggles_SwitchPreview_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 4cd2f5661a..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_Toggles_SwitchPreview_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:265ddc2f480f343efbb8717a66f5f933714e5320e837f0d26d825ed595612f2d -size 21039 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_CheckboxesPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_CheckboxesPreview_0_null,NEXUS_5,1.0,en].png index 84068bb6f7..e4e7faaccd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_CheckboxesPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_CheckboxesPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eb51ba92db07682afe5727078f8e8b0a44cad16566b97430ca92132689bf25e7 -size 10188 +oid sha256:30dc47ea866c8d8c1faab02ce12e9d00441a70b778bb267a3c52a5eb8635e6ad +size 16686 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_RadioButtonPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_RadioButtonPreview_0_null,NEXUS_5,1.0,en].png index b7dfb49de8..97f012d11b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_RadioButtonPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_RadioButtonPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae7c673ab96eaa30a72ffa4b960b93f7c5971be1155aba7d03ac4c43652098ca -size 16361 +oid sha256:88c7be1a8cff74b057ee0f7ba09922c814c8c0b6a3dbaa69ea58ec287c425fca +size 14357 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_SwitchPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_SwitchPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..209eac0f23 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Toggles_SwitchPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3812e3a220997be68aca7817c6c93987009f81079aa979161f297d529b91d8a1 +size 21505 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 7bdf26fe6c..80b9d8d46d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:254850b3a638c4d54d9f6642c15caf3e4037c47ce50ae48102e2346df6e8947f -size 29282 +oid sha256:b49f3a925c6652957bbb90c71d2dc47618d47cf76ceec50d71d9ee8758a41ea8 +size 29262 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 8bdc8eb362..8ab9358d5b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8f5077f75ee156faae31e73d464840fda51f82b0666f0a8e661295eb193b133 -size 27442 +oid sha256:3a12ecd968f65e56fb995dcc05f7c9e3160a37dc1d9a95c8b6c52ba488fb1b22 +size 27454 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_0,NEXUS_5,1.0,en].png index cb3c20fb27..1446cdefd5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f59b518dafea7d576a888661d740afeb1a6e72d432527a5f27d4a0a695eab35 -size 29718 +oid sha256:f428d22f64900a84904f02d7f49c0c7e9b295facaba3b86cec7175d98385dca9 +size 29663 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_1,NEXUS_5,1.0,en].png index 26e1ff331d..fb2e423ef8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableMatrixUserRowLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7988848232d0f44f6fd04b1a8a8ce17681659be7e63a9f68f4f3743d51f3a679 -size 29382 +oid sha256:26a2304fe3e5ad79241843a8dfdb9cb81d47a8cd4b4c9ce7cc0552d8be91d03d +size 29404 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableUnresolvedUserRowPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableUnresolvedUserRowPreview_0_null,NEXUS_5,1.0,en].png index 1ab026adf0..6da1f135d4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableUnresolvedUserRowPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableUnresolvedUserRowPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f6792f7963608fd167889be3e657ac6ca6eafa6949bfbf03fa21f9a07b34573 -size 115873 +oid sha256:d192c21d0da760d33517a226e93d8e994d6a83bbcebc422deae1dcca7e512f71 +size 115822 From 006ecd51eefa473ab65394e2f121cd3a18727c39 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 15:04:32 +0200 Subject: [PATCH 176/251] Improve rendering of offline banner regarding fontScale. --- .../networkmonitor/api/ui/ConnectivityIndicatorView.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt index bf05dbc5f5..855bf067dd 100644 --- a/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt +++ b/features/networkmonitor/api/src/main/kotlin/io/element/android/features/networkmonitor/api/ui/ConnectivityIndicatorView.kt @@ -44,8 +44,10 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.text.toDp import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @@ -85,14 +87,14 @@ private fun Indicator(modifier: Modifier = Modifier) { .statusBarsPadding() .padding(vertical = 6.dp), horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.Bottom, + verticalAlignment = Alignment.CenterVertically, ) { val tint = MaterialTheme.colorScheme.primary Image( imageVector = Icons.Outlined.WifiOff, contentDescription = null, colorFilter = ColorFilter.tint(tint), - modifier = Modifier.size(16.dp), + modifier = Modifier.size(16.sp.toDp()), ) Spacer(modifier = Modifier.width(8.dp)) Text( From fb870a0903622eb03e580b1318aa5f3cb7d740b0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 15:29:58 +0200 Subject: [PATCH 177/251] Set expicit `fontSize` parameter (to default value 1f), to test different fontScale more easily. --- .../designsystem/preview/DayNightPreviews.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/DayNightPreviews.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/DayNightPreviews.kt index 201d6f7151..b91e6a1024 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/DayNightPreviews.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/DayNightPreviews.kt @@ -42,6 +42,13 @@ const val DAY_MODE_NAME = "D" * * NB: Content should be wrapped into [ElementPreview] to apply proper theming. */ -@Preview(name = DAY_MODE_NAME) -@Preview(name = NIGHT_MODE_NAME, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Preview( + name = DAY_MODE_NAME, + fontScale = 1f, +) +@Preview( + name = NIGHT_MODE_NAME, + uiMode = Configuration.UI_MODE_NIGHT_YES, + fontScale = 1f, +) annotation class DayNightPreviews From 96b7923979e5e424637a246d55588d17dc77e876 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 15:42:47 +0200 Subject: [PATCH 178/251] Improve rendering of "All chats" regarding fontScale. (#984) --- .../features/roomlist/impl/components/RoomListTopBar.kt | 8 +++++++- .../android/libraries/designsystem/text/UnitConverters.kt | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt index db0ea8c11d..0c8522e7be 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt @@ -43,6 +43,8 @@ import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.text.scaleMax +import io.element.android.libraries.designsystem.text.toSp import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.DropdownMenu import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem @@ -114,7 +116,11 @@ private fun DefaultRoomListTopBar( val fontStyle = if (scrollBehavior.state.collapsedFraction > 0.5) ElementTheme.typography.aliasScreenTitle else - ElementTheme.typography.fontHeadingLgBold + ElementTheme.typography.fontHeadingLgBold.copy( + // Due to a limitation of MediumTopAppBar, and to avoid the text to be truncated, + // limit the size to 28.dp instead of 28.sp + fontSize = 28.dp.scaleMax().toSp() + ) Text( style = fontStyle, text = stringResource(id = R.string.screen_roomlist_main_space_title) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/UnitConverters.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/UnitConverters.kt index 1ee7d0d603..2e32e32987 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/UnitConverters.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/UnitConverters.kt @@ -52,3 +52,11 @@ fun Dp.toPx(): Float = with(LocalDensity.current) { toPx() } */ @Composable fun Dp.roundToPx(): Int = with(LocalDensity.current) { roundToPx() } + +/** + * Return the maximum value between the receiver value and the value with fonScale applied. + */ +@Composable +fun Dp.scaleMax(): Dp = with(LocalDensity.current) { + return this@scaleMax * fontScale.coerceAtMost(1f) +} From 1d89584daffa66dc262fd4abb6c3ff4f1e72c1ce Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 15:44:49 +0200 Subject: [PATCH 179/251] Improve rendering of Text composer regarding fontScale. (#984) --- .../designsystem/text/UnitConverters.kt | 8 +++++ .../libraries/textcomposer/TextComposer.kt | 30 +++++++++++-------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/UnitConverters.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/UnitConverters.kt index 2e32e32987..e3a96c2db3 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/UnitConverters.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/UnitConverters.kt @@ -60,3 +60,11 @@ fun Dp.roundToPx(): Int = with(LocalDensity.current) { roundToPx() } fun Dp.scaleMax(): Dp = with(LocalDensity.current) { return this@scaleMax * fontScale.coerceAtMost(1f) } + +/** + * Return the minimum value between the receiver value and the value with fonScale applied. + */ +@Composable +fun Dp.scaleMin(): Dp = with(LocalDensity.current) { + return this@scaleMin * fontScale.coerceAtLeast(1f) +} diff --git a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index 8c4c361707..ecf721ac1d 100644 --- a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -73,6 +73,7 @@ import io.element.android.libraries.designsystem.VectorIcons import io.element.android.libraries.designsystem.modifiers.applyIf import io.element.android.libraries.designsystem.preview.DayNightPreviews import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.text.scaleMin import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text @@ -109,12 +110,15 @@ fun TextComposer( ) { AttachmentButton(onClick = onAddAttachment, modifier = Modifier.padding(vertical = 6.dp)) Spacer(modifier = Modifier.width(12.dp)) + val roundCornerSmall = 20.dp.scaleMin() + val roundCornerLarge = 28.dp.scaleMin() + var lineCount by remember { mutableStateOf(0) } val roundedCornerSize = remember(lineCount, composerMode) { if (lineCount > 1 || composerMode is MessageComposerMode.Special) { - 20.dp + roundCornerSmall } else { - 28.dp + roundCornerLarge } } val roundedCornerSizeState = animateDpAsState( @@ -124,7 +128,7 @@ fun TextComposer( ) ) val roundedCorners = RoundedCornerShape(roundedCornerSizeState.value) - val minHeight = 42.dp + val minHeight = 42.dp.scaleMin() val bgColor = ElementTheme.colors.bgSubtleSecondary // Change border color depending on focus var hasFocus by remember { mutableStateOf(false) } @@ -165,7 +169,7 @@ fun TextComposer( singleLine = false, visualTransformation = VisualTransformation.None, shape = roundedCorners, - contentPadding = PaddingValues(top = 10.dp, bottom = 10.dp, start = 12.dp, end = 42.dp), + contentPadding = PaddingValues(top = 10.dp.scaleMin(), bottom = 10.dp.scaleMin(), start = 12.dp.scaleMin(), end = 42.dp.scaleMin()), interactionSource = remember { MutableInteractionSource() }, placeholder = { Text(stringResource(CommonStrings.common_message), style = defaultTypography) @@ -193,7 +197,7 @@ fun TextComposer( canSendMessage = composerCanSendMessage, onSendMessage = onSendMessage, composerMode = composerMode, - modifier = Modifier.padding(end = 6.dp, bottom = 6.dp) + modifier = Modifier.padding(end = 6.dp.scaleMin(), bottom = 6.dp.scaleMin()) ) } } @@ -253,7 +257,7 @@ private fun EditingModeView( tint = ElementTheme.materialColors.secondary, modifier = Modifier .padding(vertical = 8.dp) - .size(16.dp), + .size(16.dp.scaleMin()), ) Text( stringResource(CommonStrings.common_editing), @@ -270,7 +274,7 @@ private fun EditingModeView( tint = ElementTheme.materialColors.secondary, modifier = Modifier .padding(top = 8.dp, bottom = 8.dp, start = 16.dp, end = 12.dp) - .size(16.dp) + .size(16.dp.scaleMin()) .clickable( enabled = true, onClick = onResetComposerMode, @@ -333,7 +337,7 @@ private fun ReplyToModeView( tint = MaterialTheme.colorScheme.secondary, modifier = Modifier .padding(end = 4.dp, top = 4.dp, start = 16.dp, bottom = 16.dp) - .size(16.dp) + .size(16.dp.scaleMin()) .clickable( enabled = true, onClick = onResetComposerMode, @@ -351,13 +355,13 @@ private fun AttachmentButton( ) { Surface( modifier - .size(30.dp) + .size(30.dp.scaleMin()) .clickable(onClick = onClick), shape = CircleShape, color = ElementTheme.colors.iconPrimary ) { Image( - modifier = Modifier.size(12.5f.dp), + modifier = Modifier.size(12.5f.dp.scaleMin()), painter = painterResource(R.drawable.ic_add_attachment), contentDescription = stringResource(R.string.rich_text_editor_a11y_add_attachment), contentScale = ContentScale.Inside, @@ -381,10 +385,10 @@ private fun BoxScope.SendButton( modifier = modifier .clip(CircleShape) .background(if (canSendMessage) ElementTheme.colors.iconAccentTertiary else Color.Transparent) - .size(30.dp) + .size(30.dp.scaleMin()) .align(Alignment.BottomEnd) .applyIf(composerMode !is MessageComposerMode.Edit, ifTrue = { - padding(start = 1.dp) // Center the arrow in the circle + padding(start = 1.dp.scaleMin()) // Center the arrow in the circle }) .clickable( enabled = canSendMessage, @@ -404,7 +408,7 @@ private fun BoxScope.SendButton( else -> stringResource(CommonStrings.action_send) } Icon( - modifier = Modifier.size(16.dp), + modifier = Modifier.size(16.dp.scaleMin()), resourceId = iconId, contentDescription = contentDescription, // Exception here, we use Color.White instead of ElementTheme.colors.iconOnSolidPrimary From cc2a01f483dc224e2eca52a00e121b4a038efb85 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 15:53:20 +0200 Subject: [PATCH 180/251] Ensure room name have horizontal padding and is centered if displayed on several lines, and remove unnecessary Box around the Avatar. --- .../roomdetails/impl/RoomDetailsView.kt | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index 1e5d1a8ebb..d1de0babe8 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -17,13 +17,11 @@ package io.element.android.features.roomdetails.impl import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -226,22 +224,28 @@ internal fun RoomHeaderSection( roomAlias: String?, modifier: Modifier = Modifier ) { - Column(modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { - Box(modifier = Modifier.size(70.dp)) { - Avatar( - avatarData = AvatarData(roomId, roomName, avatarUrl, AvatarSize.RoomHeader), - modifier = Modifier.fillMaxSize() - ) - } + Column( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Avatar( + avatarData = AvatarData(roomId, roomName, avatarUrl, AvatarSize.RoomHeader), + modifier = Modifier.size(70.dp) + ) Spacer(modifier = Modifier.height(24.dp)) - Text(roomName, style = ElementTheme.typography.fontHeadingLgBold) + Text( + text = roomName, + style = ElementTheme.typography.fontHeadingLgBold, + textAlign = TextAlign.Center, + ) if (roomAlias != null) { Spacer(modifier = Modifier.height(6.dp)) Text( text = roomAlias, style = ElementTheme.typography.fontBodyLgRegular, color = MaterialTheme.colorScheme.secondary, - modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), textAlign = TextAlign.Center, ) } From 8c363602c04b337c3ae7355441f6edad9c9e3aaf Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 27 Jul 2023 14:33:34 +0000 Subject: [PATCH 181/251] Update screenshots --- ...oadingRoomNodeViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...oadingRoomNodeViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...adingRoomNodeViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...adingRoomNodeViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...Group_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...roup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...ewDarkConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...wLightConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...Group_RoomListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...roup_RoomListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 7870560dd4..500f0a57f7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da4188dc606f0735fd4093acad34897c866d8c4d20b3e0ec0618685f7302cbf5 -size 9288 +oid sha256:3f18e74bfbebd69109f36a7154b20d3ee61071bff0d8ebf28f4b3b35c58e2938 +size 9256 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 7edd6f9ee7..17ed1eee99 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e27b1ea7bfa4eb97af3c2d433fbe3f5ca21458e4458d91b39c9c2bb3d7b02abc -size 11306 +oid sha256:eea203b9527f7c3dc3692e7db3e9d5cee20ca08fddf04977c948933dc2784022 +size 11274 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index c87156e8c6..3bbf79781a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2bbeb21faf320226f42aad179b955952ad8e4b5d5645c45bfbbc02166baf0662 -size 9641 +oid sha256:b5c69ae7d27eb7024e9a2f9c7100b308562ef2fc702e28604098180cb02a3813 +size 9645 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 6391f1dec0..1b135effbf 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.room_null_DefaultGroup_LoadingRoomNodeViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:499e17b52d57f8b0d8984e32457b3922c0647be5b835c02a01ed927fe004ec4d -size 11610 +oid sha256:62149a4d9b0dd62ddb56e1cdb24fe401f189fda05d17dd16f8c0bc511eab6561 +size 11616 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 482ab3b775..3dc03be3ae 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d368f7cc84b5a8850577fac658aaeb89a1c83a2890b7fd393577c9cde919069 -size 53689 +oid sha256:613d55031bc30060bdd84596fb78de043ead483ac59fd42e48b15f0e56947a36 +size 53660 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index ad548c2fbe..26d1cd82c3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89520fd2999582229ace8fd9304644fc74ce01230a4024b90b47ce1cd61eb564 -size 55678 +oid sha256:632dfa6edc34b7283654e43c3b7d9eb0334507644e50bc5fc7bdf8b5c45ed874 +size 55686 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewDarkConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewDarkConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png index fd00dfae33..41503c31be 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewDarkConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewDarkConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9e0c73b8d86c064ce46ade6477cc91803d543199657e597a15a7e21bdacab7be -size 6541 +oid sha256:774ece5436ab5a025a6a7134120069922276329bf87408a90263bb8c425a0568 +size 6510 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewLightConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewLightConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png index 820b688066..b41bc75694 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewLightConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.networkmonitor.api.ui_null_DefaultGroup_PreviewLightConnectivityIndicatorView_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3af4d7ce438da56851aa3041138558d8e57ac181775476b983646039a1cb62ae -size 6602 +oid sha256:55aff072541c752abd0a8fe48c73b38b42ea47ab4779a2a3ae37a449f88ffd24 +size 6606 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 6ef66d5ddf..a613701e1a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8aa48154bf2cf3a6ec6da96e24a86c6a3f1e6eb11865c2301a4f2638e471bd34 -size 37408 +oid sha256:9c6477d7476baccee037e491930d99f7ea00e313d1285fdd882be3de99f705b0 +size 37376 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index 75268923f9..de27672e6f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8f155381ccc3f8833b58e076e797034dfa528bd55bafd982c34fb997e4db491 -size 39830 +oid sha256:5b589817850bc30e336e9d5e443b2153133991aa5ea919838bddef26d2b1df9e +size 39840 From 337d4d05d68f062793f1f595313af0fcd8fa12f2 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 27 Jul 2023 16:49:49 +0200 Subject: [PATCH 182/251] Sync: move sync lifecycle to onStart/onStop instead of onResume/onPause --- .../main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 4130e5da23..effe67fd49 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -138,12 +138,12 @@ class LoggedInFlowNode @AssistedInject constructor( backstack.push(NavTarget.Ftue) } }, - onResume = { + onStart = { lifecycleScope.launch { syncService.startSync() } }, - onPause = { + onStop = { syncService.stopSync() }, onDestroy = { From d46a79316e5bc1f9e4fae9eb080fe0f093fbabaf Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 17:13:42 +0200 Subject: [PATCH 183/251] Auto capitalize first letter of sentences for messages, room topic and bug report description. (#948) --- .../createroom/impl/configureroom/ConfigureRoomView.kt | 5 +++++ .../features/rageshake/impl/bugreport/BugReportView.kt | 2 ++ .../features/roomdetails/impl/edit/RoomDetailsEditView.kt | 5 +++++ .../libraries/designsystem/components/LabelledTextField.kt | 3 +++ .../element/android/libraries/textcomposer/TextComposer.kt | 5 +++++ 5 files changed, 20 insertions(+) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index 9ac8f0fbde..37fcdbb7b1 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -29,6 +29,7 @@ import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ModalBottomSheetValue @@ -43,6 +44,7 @@ import androidx.compose.ui.focus.FocusManager import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp @@ -247,6 +249,9 @@ fun RoomTopic( placeholder = stringResource(CommonStrings.common_topic_placeholder), onValueChange = onTopicChanged, maxLines = 3, + keyboardOptions = KeyboardOptions( + capitalization = KeyboardCapitalization.Sentences, + ), ) } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt index 74f3a13d13..8b66d1db09 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -97,6 +98,7 @@ fun BugReportView( eventSink(BugReportEvents.SetDescription(it)) }, keyboardOptions = KeyboardOptions( + capitalization = KeyboardCapitalization.Sentences, keyboardType = KeyboardType.Text, imeAction = ImeAction.Next ), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt index 029f5ac5df..fb384dcdd5 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt @@ -34,6 +34,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ModalBottomSheetValue @@ -52,6 +53,7 @@ import androidx.compose.ui.focus.FocusManager import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp @@ -164,6 +166,9 @@ fun RoomDetailsEditView( placeholder = stringResource(CommonStrings.common_topic_placeholder), maxLines = 10, onValueChange = { state.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(it)) }, + keyboardOptions = KeyboardOptions( + capitalization = KeyboardCapitalization.Sentences, + ), ) } else { LabelledReadOnlyField( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt index cc1d92ccd8..19cefaed26 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt @@ -20,6 +20,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -39,6 +40,7 @@ fun LabelledTextField( placeholder: String? = null, singleLine: Boolean = false, maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, onValueChange: (String) -> Unit = {}, ) { Column( @@ -59,6 +61,7 @@ fun LabelledTextField( onValueChange = onValueChange, singleLine = singleLine, maxLines = maxLines, + keyboardOptions = keyboardOptions, ) } } diff --git a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index 8c4c361707..580b7670ea 100644 --- a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -38,6 +38,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.material.ripple.rememberRipple @@ -65,6 +66,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow @@ -155,6 +157,9 @@ fun TextComposer( onTextLayout = { lineCount = it.lineCount }, + keyboardOptions = KeyboardOptions( + capitalization = KeyboardCapitalization.Sentences, + ), textStyle = defaultTypography.copy(color = MaterialTheme.colorScheme.primary), cursorBrush = SolidColor(ElementTheme.colors.iconAccentTertiary), decorationBox = { innerTextField -> From 09e30bcac67952610372b96d8b55ea4d097e75a9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 17:29:03 +0200 Subject: [PATCH 184/251] Prevent reaction on state events and deleted events (#971) --- .../impl/actionlist/ActionListPresenter.kt | 6 +++++- .../model/event/TimelineItemEventContent.kt | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt index 2d18018746..7c7e43e02b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt @@ -28,6 +28,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent import io.element.android.features.messages.impl.timeline.model.event.canBeCopied +import io.element.android.features.messages.impl.timeline.model.event.canReact import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.meta.BuildMeta import kotlinx.collections.immutable.toImmutableList @@ -48,7 +49,10 @@ class ActionListPresenter @Inject constructor( } val displayEmojiReactions by remember { - derivedStateOf { (target.value as? ActionListState.Target.Success)?.event?.isRemote == true } + derivedStateOf { + val event = (target.value as? ActionListState.Target.Success)?.event + event?.isRemote == true && event.content.canReact() + } } fun handleEvents(event: ActionListEvents) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt index 0ff67e481f..e0de57c5a5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt @@ -33,3 +33,20 @@ fun TimelineItemEventContent.canBeCopied(): Boolean = is TimelineItemRedactedContent -> true else -> false } + +/** + * Return true if user can react (i.e. send a reaction) on the event content. + */ +fun TimelineItemEventContent.canReact(): Boolean = + when (this) { + is TimelineItemTextBasedContent, + is TimelineItemAudioContent, + is TimelineItemEncryptedContent, + is TimelineItemFileContent, + is TimelineItemImageContent, + is TimelineItemLocationContent, + is TimelineItemVideoContent -> true + is TimelineItemStateContent, + is TimelineItemRedactedContent, + TimelineItemUnknownContent -> false + } From b2d3368f638f8381f63c725abefebf096e07e083 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 17:53:28 +0200 Subject: [PATCH 185/251] Allow user with enough power level to redact other's messages (#969) --- .../messages/impl/MessagesPresenter.kt | 3 ++ .../features/messages/impl/MessagesState.kt | 1 + .../messages/impl/MessagesStateProvider.kt | 1 + .../features/messages/impl/MessagesView.kt | 2 +- .../impl/actionlist/ActionListEvents.kt | 2 +- .../impl/actionlist/ActionListPresenter.kt | 14 +++-- .../messages/MessagesPresenterTest.kt | 26 ++++++++- .../actionlist/ActionListPresenterTest.kt | 53 +++++++++++++++---- .../libraries/matrix/api/room/MatrixRoom.kt | 2 + .../room/powerlevels/MatrixRoomPowerLevels.kt | 6 +++ .../matrix/impl/room/RustMatrixRoom.kt | 6 +++ .../matrix/test/room/FakeMatrixRoom.kt | 6 +++ .../matrix/ui/room/MatrixRoomState.kt | 8 +++ 13 files changed, 112 insertions(+), 18 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index acaaf54c9e..8818cc6476 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -71,6 +71,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType +import io.element.android.libraries.matrix.ui.room.canRedactAsState import io.element.android.libraries.matrix.ui.room.canSendMessageAsState import io.element.android.libraries.textcomposer.MessageComposerMode import kotlinx.coroutines.CoroutineScope @@ -109,6 +110,7 @@ class MessagesPresenter @AssistedInject constructor( val syncUpdateFlow = room.syncUpdateFlow.collectAsState() val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value) + val userHasPermissionToRedact by room.canRedactAsState(updateKey = syncUpdateFlow.value) var roomName: Async by remember { mutableStateOf(Async.Uninitialized) } var roomAvatar: Async by remember { mutableStateOf(Async.Uninitialized) } LaunchedEffect(syncUpdateFlow.value) { @@ -165,6 +167,7 @@ class MessagesPresenter @AssistedInject constructor( roomName = roomName, roomAvatar = roomAvatar, userHasPermissionToSendMessage = userHasPermissionToSendMessage, + userHasPermissionToRedact = userHasPermissionToRedact, composerState = composerState, timelineState = timelineState, actionListState = actionListState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt index a042ec1ac4..50e952f237 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt @@ -33,6 +33,7 @@ data class MessagesState( val roomName: Async, val roomAvatar: Async, val userHasPermissionToSendMessage: Boolean, + val userHasPermissionToRedact: Boolean, val composerState: MessageComposerState, val timelineState: TimelineState, val actionListState: ActionListState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index ce50cc138b..bb0b61f620 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -50,6 +50,7 @@ fun aMessagesState() = MessagesState( roomName = Async.Success("Room name"), roomAvatar = Async.Success(AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom)), userHasPermissionToSendMessage = true, + userHasPermissionToRedact = false, composerState = aMessageComposerState().copy( text = "Hello", isFullScreen = false, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index f909ac7f1a..facd7ceb24 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -115,7 +115,7 @@ fun MessagesView( fun onMessageLongClicked(event: TimelineItem.Event) { Timber.v("OnMessageLongClicked= ${event.id}") localView.hideKeyboard() - state.actionListState.eventSink(ActionListEvents.ComputeForMessage(event)) + state.actionListState.eventSink(ActionListEvents.ComputeForMessage(event, state.userHasPermissionToRedact)) } fun onActionSelected(action: TimelineItemAction, event: TimelineItem.Event) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListEvents.kt index a6244a72e3..3c796036e7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListEvents.kt @@ -20,5 +20,5 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem sealed interface ActionListEvents { object Clear : ActionListEvents - data class ComputeForMessage(val event: TimelineItem.Event) : ActionListEvents + data class ComputeForMessage(val event: TimelineItem.Event, val canRedact: Boolean) : ActionListEvents } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt index 2d18018746..1aadcc2094 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt @@ -54,7 +54,11 @@ class ActionListPresenter @Inject constructor( fun handleEvents(event: ActionListEvents) { when (event) { ActionListEvents.Clear -> target.value = ActionListState.Target.None - is ActionListEvents.ComputeForMessage -> localCoroutineScope.computeForMessage(event.event, target) + is ActionListEvents.ComputeForMessage -> localCoroutineScope.computeForMessage( + timelineItem = event.event, + userCanRedact = event.canRedact, + target = target, + ) } } @@ -65,7 +69,11 @@ class ActionListPresenter @Inject constructor( ) } - private fun CoroutineScope.computeForMessage(timelineItem: TimelineItem.Event, target: MutableState) = launch { + private fun CoroutineScope.computeForMessage( + timelineItem: TimelineItem.Event, + userCanRedact: Boolean, + target: MutableState + ) = launch { target.value = ActionListState.Target.Loading(timelineItem) val actions = when (timelineItem.content) { @@ -102,7 +110,7 @@ class ActionListPresenter @Inject constructor( if (!timelineItem.isMine) { add(TimelineItemAction.ReportContent) } - if (timelineItem.isMine) { + if (timelineItem.isMine || userCanRedact) { add(TimelineItemAction.Redact) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index b47ac55d35..6ff003b735 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -44,8 +44,11 @@ import io.element.android.features.messages.media.FakeLocalMediaFactory import io.element.android.features.messages.utils.messagesummary.FakeMessageSummaryFormatter import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper +import io.element.android.libraries.architecture.Async import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.media.MediaSource @@ -83,9 +86,16 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = consumeItemsUntilTimeout().last() assertThat(initialState.roomId).isEqualTo(A_ROOM_ID) + assertThat(initialState.roomName).isEqualTo(Async.Success("")) + assertThat(initialState.roomAvatar).isEqualTo(Async.Success(AvatarData(id = A_ROOM_ID.value, name = "", size = AvatarSize.TimelineRoom))) + assertThat(initialState.userHasPermissionToSendMessage).isTrue() + assertThat(initialState.userHasPermissionToRedact).isFalse() + assertThat(initialState.hasNetworkConnection).isTrue() + assertThat(initialState.snackbarMessage).isNull() + assertThat(initialState.inviteProgress).isEqualTo(Async.Uninitialized) + assertThat(initialState.showReinvitePrompt).isFalse() } } @@ -531,6 +541,18 @@ class MessagesPresenterTest { } } + @Test + fun `present - permission to redact`() = runTest { + val matrixRoom = FakeMatrixRoom(canRedact = true) + val presenter = createMessagePresenter(matrixRoom = matrixRoom) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = consumeItemsUntilTimeout().last() + assertThat(initialState.userHasPermissionToRedact).isTrue() + } + } + private fun TestScope.createMessagePresenter( coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(), matrixRoom: MatrixRoom = FakeMatrixRoom(), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt index a2baa9dff7..afef9f6730 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/actionlist/ActionListPresenterTest.kt @@ -56,7 +56,7 @@ class ActionListPresenterTest { }.test { val initialState = awaitItem() val messageEvent = aMessageEvent(isMine = true, content = TimelineItemRedactedContent) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false)) // val loadingState = awaitItem() // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) val successState = awaitItem() @@ -81,7 +81,7 @@ class ActionListPresenterTest { }.test { val initialState = awaitItem() val messageEvent = aMessageEvent(isMine = false, content = TimelineItemRedactedContent) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false)) // val loadingState = awaitItem() // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) val successState = awaitItem() @@ -109,7 +109,7 @@ class ActionListPresenterTest { isMine = false, content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false) ) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false)) // val loadingState = awaitItem() // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) val successState = awaitItem() @@ -130,6 +130,37 @@ class ActionListPresenterTest { } } + @Test + fun `present - compute for others message and can redact`() = runTest { + val presenter = anActionListPresenter(isBuildDebuggable = true) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + val messageEvent = aMessageEvent( + isMine = false, + content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false) + ) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, true)) + val successState = awaitItem() + assertThat(successState.target).isEqualTo( + ActionListState.Target.Success( + messageEvent, + persistentListOf( + TimelineItemAction.Reply, + TimelineItemAction.Forward, + TimelineItemAction.Copy, + TimelineItemAction.Developer, + TimelineItemAction.ReportContent, + TimelineItemAction.Redact, + ) + ) + ) + initialState.eventSink.invoke(ActionListEvents.Clear) + assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None) + } + } + @Test fun `present - compute for my message`() = runTest { val presenter = anActionListPresenter(isBuildDebuggable = true) @@ -141,7 +172,7 @@ class ActionListPresenterTest { isMine = true, content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false) ) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false)) // val loadingState = awaitItem() // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) val successState = awaitItem() @@ -174,7 +205,7 @@ class ActionListPresenterTest { isMine = true, content = aTimelineItemImageContent(), ) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false)) // val loadingState = awaitItem() // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) val successState = awaitItem() @@ -205,7 +236,7 @@ class ActionListPresenterTest { isMine = true, content = aTimelineItemStateEventContent(), ) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(stateEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(stateEvent, false)) // val loadingState = awaitItem() // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) val successState = awaitItem() @@ -234,7 +265,7 @@ class ActionListPresenterTest { isMine = true, content = aTimelineItemStateEventContent(), ) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(stateEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(stateEvent, false)) // val loadingState = awaitItem() // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) val successState = awaitItem() @@ -262,7 +293,7 @@ class ActionListPresenterTest { isMine = true, content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false) ) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false)) // val loadingState = awaitItem() // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent)) val successState = awaitItem() @@ -299,10 +330,10 @@ class ActionListPresenterTest { content = TimelineItemRedactedContent, ) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false)) assertThat(awaitItem().target).isInstanceOf(ActionListState.Target.Success::class.java) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(redactedEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(redactedEvent, false)) awaitItem().run { assertThat(target).isEqualTo(ActionListState.Target.None) assertThat(displayEmojiReactions).isFalse() @@ -323,7 +354,7 @@ class ActionListPresenterTest { content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false), ) - initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent)) + initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, false)) val successState = awaitItem() assertThat(successState.target).isEqualTo( ActionListState.Target.Success( diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt index 1445f85228..f8b9e6c2c3 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt @@ -105,6 +105,8 @@ interface MatrixRoom : Closeable { suspend fun canUserInvite(userId: UserId): Result + suspend fun canUserRedact(userId: UserId): Result + suspend fun canUserSendState(userId: UserId, type: StateEventType): Result suspend fun canUserSendMessage(userId: UserId, type: MessageEventType): Result diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt index 852401bffc..e0ba452efe 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt @@ -34,3 +34,9 @@ suspend fun MatrixRoom.canSendState(type: StateEventType): Result = can * Shortcut for calling [MatrixRoom.canUserSendMessage] with our own user. */ suspend fun MatrixRoom.canSendMessage(type: MessageEventType): Result = canUserSendMessage(sessionId, type) + +/** + * Shortcut for calling [MatrixRoom.canUserRedact] with our own user. + */ +suspend fun MatrixRoom.canRedact(): Result = canUserRedact(sessionId) + diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 8c2eecce8a..c2d3dfeb39 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -250,6 +250,12 @@ class RustMatrixRoom( } } + override suspend fun canUserRedact(userId: UserId): Result { + return runCatching { + innerRoom.canUserRedact(userId.value) + } + } + override suspend fun canUserSendState(userId: UserId, type: StateEventType): Result { return runCatching { innerRoom.canUserSendState(userId.value, type.map()) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index 59f6ed57bd..ee735ae81b 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -56,6 +56,7 @@ class FakeMatrixRoom( override val joinedMemberCount: Long = 123L, override val activeMemberCount: Long = 234L, private val matrixTimeline: MatrixTimeline = FakeMatrixTimeline(), + canRedact: Boolean = false, ) : MatrixRoom { private var ignoreResult: Result = Result.success(Unit) @@ -66,6 +67,7 @@ class FakeMatrixRoom( private var joinRoomResult = Result.success(Unit) private var inviteUserResult = Result.success(Unit) private var canInviteResult = Result.success(true) + private var canRedactResult = Result.success(canRedact) private val canSendStateResults = mutableMapOf>() private val canSendEventResults = mutableMapOf>() private var sendMediaResult = Result.success(Unit) @@ -207,6 +209,10 @@ class FakeMatrixRoom( return canInviteResult } + override suspend fun canUserRedact(userId: UserId): Result { + return canRedactResult + } + override suspend fun canUserSendState(userId: UserId, type: StateEventType): Result { return canSendStateResults[type] ?: Result.failure(IllegalStateException("No fake answer")) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt index 005a0ac747..f2a73545bf 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt @@ -21,6 +21,7 @@ import androidx.compose.runtime.State import androidx.compose.runtime.produceState import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MessageEventType +import io.element.android.libraries.matrix.api.room.powerlevels.canRedact import io.element.android.libraries.matrix.api.room.powerlevels.canSendMessage @Composable @@ -30,3 +31,10 @@ fun MatrixRoom.canSendMessageAsState(type: MessageEventType, updateKey: Long): S } } +@Composable +fun MatrixRoom.canRedactAsState(updateKey: Long): State { + return produceState(initialValue = false, key1 = updateKey) { + value = canRedact().getOrElse { false } + } +} + From 5547b6278e170a01f6bcb88a180c7fa0f1dd88a5 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 27 Jul 2023 20:49:27 +0200 Subject: [PATCH 186/251] Update rust sdk to 0.1.37 --- gradle/libs.versions.toml | 2 +- .../matrix/impl/room/RoomListExtensions.kt | 6 +++--- .../matrix/impl/room/RoomSummaryListProcessor.kt | 8 +++++--- .../libraries/matrix/impl/room/RustMatrixRoom.kt | 15 +++++++++------ .../matrix/impl/room/RustRoomSummaryDataSource.kt | 2 +- .../item/event/TimelineEventContentMapper.kt | 1 + 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 04af1ba607..ff37e483d8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -145,7 +145,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" } timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.36" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.37" sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" } sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt index 84c4eeaefb..1baa4f23db 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt @@ -46,11 +46,11 @@ fun RoomList.loadingStateFlow(): Flow = result.stateStream }.buffer(Channel.UNLIMITED) -fun RoomList.entriesFlow(onInitialList: suspend (List) -> Unit): Flow = +fun RoomList.entriesFlow(onInitialList: suspend (List) -> Unit): Flow> = mxCallbackFlow { val listener = object : RoomListEntriesListener { - override fun onUpdate(roomEntriesUpdate: RoomListEntriesUpdate) { - trySendBlocking(roomEntriesUpdate) + override fun onUpdate(roomEntriesUpdates: List) { + trySendBlocking(roomEntriesUpdates) } } val result = entries(listener) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryListProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryListProcessor.kt index a8ab4cb807..ef54a0604d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryListProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryListProcessor.kt @@ -50,12 +50,14 @@ class RoomSummaryListProcessor( initLatch.complete(Unit) } - suspend fun postUpdate(update: RoomListEntriesUpdate) { + suspend fun postUpdate(updates: List) { // Makes sure to process first entries before update. initLatch.await() updateRoomSummaries { - Timber.v("Update rooms from postUpdate ($update) on ${Thread.currentThread()}") - applyUpdate(update) + Timber.v("Update rooms from postUpdates (with ${updates.size} items) on ${Thread.currentThread()}") + updates.forEach { update -> + applyUpdate(update) + } } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 8c2eecce8a..d2a528074c 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -364,13 +364,16 @@ class RustMatrixRoom( ) } } -} -//TODO handle cancellation, need refactoring of how we are catching errors -private suspend fun sendAttachment(handle: () -> SendAttachmentJoinHandle): Result { - return runCatching { - handle().use { - it.join() + //TODO handle cancellation, need refactoring of how we are catching errors + private suspend fun sendAttachment(handle: () -> SendAttachmentJoinHandle): Result = withContext(roomDispatcher) { + runCatching { + handle().use { + it.join() + } } } + } + + diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt index efdbcf34ad..3e65fa2e94 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt @@ -112,7 +112,7 @@ private fun RoomListLoadingState.toRoomSummaryDataSourceLoadingState(): RoomSumm } } -private fun RoomList.observeEntriesWithProcessor(processor: RoomSummaryListProcessor): Flow { +private fun RoomList.observeEntriesWithProcessor(processor: RoomSummaryListProcessor): Flow> { return entriesFlow { roomListEntries -> processor.postEntries(roomListEntries) }.onEach { update -> diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt index 33727c15d4..52d646e48e 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt @@ -96,6 +96,7 @@ class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMap data = kind.msg.map() ) } + else -> UnknownContent } } } From b75752e066eb8804b7a17890db50529e6bb907e7 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 27 Jul 2023 20:49:56 +0200 Subject: [PATCH 187/251] Timeline: fetch members only after timeline is ready --- .../android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt index 3997a492ac..796b48ebb3 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt @@ -127,6 +127,7 @@ class RustMatrixTimeline( } private suspend fun fetchMembers() = withContext(dispatcher) { + initLatch.await() runCatching { innerRoom.fetchMembers() } From 632c4ef168550c1651042630d3649fce6fe09223 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 27 Jul 2023 21:11:34 +0200 Subject: [PATCH 188/251] Fix warning --- .../android/libraries/matrix/impl/room/RoomListExtensions.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt index 1baa4f23db..fb22c47daa 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt @@ -49,8 +49,8 @@ fun RoomList.loadingStateFlow(): Flow = fun RoomList.entriesFlow(onInitialList: suspend (List) -> Unit): Flow> = mxCallbackFlow { val listener = object : RoomListEntriesListener { - override fun onUpdate(roomEntriesUpdates: List) { - trySendBlocking(roomEntriesUpdates) + override fun onUpdate(roomEntriesUpdate: List) { + trySendBlocking(roomEntriesUpdate) } } val result = entries(listener) From 271d0db587b8537073f7cb3be2fa1cf03f19097c Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 27 Jul 2023 21:12:07 +0200 Subject: [PATCH 189/251] Also catch Exception instead of RoomListException --- .../android/libraries/matrix/impl/room/RoomListExtensions.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt index fb22c47daa..b74ab8968e 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt @@ -25,7 +25,6 @@ import org.matrix.rustcomponents.sdk.RoomList import org.matrix.rustcomponents.sdk.RoomListEntriesListener import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate import org.matrix.rustcomponents.sdk.RoomListEntry -import org.matrix.rustcomponents.sdk.RoomListException import org.matrix.rustcomponents.sdk.RoomListItem import org.matrix.rustcomponents.sdk.RoomListLoadingState import org.matrix.rustcomponents.sdk.RoomListLoadingStateListener @@ -61,7 +60,7 @@ fun RoomList.entriesFlow(onInitialList: suspend (List) -> Unit): fun RoomListService.roomOrNull(roomId: String): RoomListItem? { return try { room(roomId) - } catch (exception: RoomListException) { + } catch (exception: Exception) { Timber.d(exception, "Failed finding room with id=$roomId.") return null } From 54e16eae0f9b3e8dd266badef285b18c7e26e615 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 23:03:40 +0200 Subject: [PATCH 190/251] No need to upload artifact in this job. This is done by the build job. --- .github/workflows/maestro.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/maestro.yml b/.github/workflows/maestro.yml index d3274d0b14..74fb1cfc83 100644 --- a/.github/workflows/maestro.yml +++ b/.github/workflows/maestro.yml @@ -40,12 +40,6 @@ jobs: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} - - name: Upload debug APKs - uses: actions/upload-artifact@v3 - with: - name: elementx-debug - path: | - app/build/outputs/apk/debug/*.apk - uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} From 438e0c598a6d931a99e2b3ef093eacff7c0ae3ac Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 23:10:26 +0200 Subject: [PATCH 191/251] Update the recipe about screenshots recording. --- docs/screenshot_testing.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/screenshot_testing.md b/docs/screenshot_testing.md index 37299af7fc..ca69acaf4a 100644 --- a/docs/screenshot_testing.md +++ b/docs/screenshot_testing.md @@ -30,6 +30,14 @@ If installed correctly, `git push` and `git pull` will now include LFS content. ## Recording +Recording of screenshots is done by triggering the GitHub action [Record screenshots](https://github.com/vector-im/element-x-android/actions/workflows/recordScreenshots.yml), to avoid differences of generated binary files (png images) depending on developers' environment. + +So basically, you will create a branch, do some commits with your work on it, then push your branch, trigger the GitHub action to record the screenshots (only if you think preview may have changed), and finally create a pull request. The GitHub action will record the screenshots and commit the changes to the branch. + +You can still record the screenshots locally, but please do not commit the changes. + +To record the screenshot locally, run the following command: + ```shell ./gradlew recordPaparazziDebug ``` From 32bf308b42162f51c1ffe8d89c243a67c7533ce1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 23:12:54 +0200 Subject: [PATCH 192/251] ElementX -> Element X --- .maestro/README.md | 4 ++-- docs/_developer_onboarding.md | 2 +- docs/nightly_build.md | 4 ++-- docs/screenshot_testing.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.maestro/README.md b/.maestro/README.md index cd1f0658a7..f35d37ec61 100644 --- a/.maestro/README.md +++ b/.maestro/README.md @@ -18,7 +18,7 @@ To setup, please refer at [https://maestro.mobile.dev](https://maestro.mobile.de From root dir of the project -*Note: Since ElementX does not allow account creation nor room creation, we have to use an existing account with an existing room to run maestro test suite. So to run locally, please replace `user` and `123` with your test matrix.org account credentials, and `my room` with one of a room this account has join. Note that the test will send messages to this room.* +*Note: Since Element X does not allow account creation nor room creation, we have to use an existing account with an existing room to run maestro test suite. So to run locally, please replace `user` and `123` with your test matrix.org account credentials, and `my room` with one of a room this account has join. Note that the test will send messages to this room.* ```shell maestro test \ @@ -39,7 +39,7 @@ Test result will be printed on the console, and screenshots will be generated at Tests are yaml files. Generally each yaml file should leave the app in the same screen than at the beginning. -Start the ElementX app and run this command to help writing test. +Start the Element X app and run this command to help writing test. ```shell maestro studio diff --git a/docs/_developer_onboarding.md b/docs/_developer_onboarding.md index ad5882abae..8a587b5a08 100644 --- a/docs/_developer_onboarding.md +++ b/docs/_developer_onboarding.md @@ -145,7 +145,7 @@ Then you can launch the build script from the matrix-rust-components-kotlin repo - `-m` Option to select the gradle module to build. Default is sdk. - `-t` Option to to select an android target to build against. Default will build for all targets. -So for example to build the sdk against aarch64-linux-android target and copy the generated aar to ElementX project: +So for example to build the sdk against aarch64-linux-android target and copy the generated aar to Element X project: ```shell ./scripts/build.sh -p [YOUR MATRIX RUST SDK PATH] -t aarch64-linux-android -o [YOUR element-x-android PATH]/libraries/rustsdk/matrix-rust-sdk.aar diff --git a/docs/nightly_build.md b/docs/nightly_build.md index 9abd59a67b..91ea10b530 100644 --- a/docs/nightly_build.md +++ b/docs/nightly_build.md @@ -10,11 +10,11 @@ ## Configuration -The nightly build will contain what's on develop, in release mode, for the main variant. It is signed using a dedicated signature, and has a dedicated appId (`io.element.android.x.nightly`), so it can be installed along with the production version of ElementX Android. The only other difference compared to ElementX Android is a different app name. We do not want to change the app name since it will also affect some strings in the app, and we do want to do that. (TODO today, the app name is changed.) +The nightly build will contain what's on develop, in release mode, for the main variant. It is signed using a dedicated signature, and has a dedicated appId (`io.element.android.x.nightly`), so it can be installed along with the production version of Element X Android. The only other difference compared to ElementX Android is a different app name. We do not want to change the app name since it will also affect some strings in the app, and we do want to do that. (TODO today, the app name is changed.) Nightly builds are built and released to Firebase every days, and automatically. -This is recommended to exclusively use this app, with your main account, instead of ElementX Android, and fallback to ElementX Android just in case of regression, to discover as soon as possible any regression, and report it to the team. To avoid double notification, you may want to disable the notification from the Element Android production version. Just open Element Android, navigate to `Settings/Notifications` and uncheck `Enable notifications for this session` (TODO Not supported yet). +This is recommended to exclusively use this app, with your main account, instead of Element X Android, and fallback to ElementX Android just in case of regression, to discover as soon as possible any regression, and report it to the team. To avoid double notification, you may want to disable the notification from the Element Android production version. Just open Element Android, navigate to `Settings/Notifications` and uncheck `Enable notifications for this session` (TODO Not supported yet). *Note:* Due to a limitation of Firebase, the nightly build is the universal build, which means that the size of the APK is a bit bigger, but this should not have any other side effect. diff --git a/docs/screenshot_testing.md b/docs/screenshot_testing.md index ca69acaf4a..79ecad20dd 100644 --- a/docs/screenshot_testing.md +++ b/docs/screenshot_testing.md @@ -13,7 +13,7 @@ ## Overview - Screenshot tests are tests which record the content of a rendered screen and verify subsequent runs to check if the screen renders differently. -- ElementX uses [Paparazzi](https://github.com/cashapp/paparazzi) to render, record and verify Composable. All Composable Preview will be use to make screenshot test, thanks to the usage of [Showkase](https://github.com/airbnb/Showkase). +- Element X uses [Paparazzi](https://github.com/cashapp/paparazzi) to render, record and verify Composable. All Composable Preview will be use to make screenshot test, thanks to the usage of [Showkase](https://github.com/airbnb/Showkase). - The screenshot verification occurs on every pull request as part of the `tests.yml` workflow. ## Setup From e07b66410811f8a9af4f4ad4417f1aed7da9ab3b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jul 2023 23:14:32 +0200 Subject: [PATCH 193/251] [doc] Element X can now create room. --- .maestro/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.maestro/README.md b/.maestro/README.md index f35d37ec61..9d9090744c 100644 --- a/.maestro/README.md +++ b/.maestro/README.md @@ -18,7 +18,7 @@ To setup, please refer at [https://maestro.mobile.dev](https://maestro.mobile.de From root dir of the project -*Note: Since Element X does not allow account creation nor room creation, we have to use an existing account with an existing room to run maestro test suite. So to run locally, please replace `user` and `123` with your test matrix.org account credentials, and `my room` with one of a room this account has join. Note that the test will send messages to this room.* +*Note: Since Element X does not allow account creation, we have to use an existing account to run maestro test suite. So to run locally, please replace `user` and `123` with your test matrix.org account credentials, and `my room` with one of a room this account has joined. Note that the test will send messages to this room.* ```shell maestro test \ From bfb27db58bb8af5a901c1468b1f19508fa0a5548 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 28 Jul 2023 10:01:22 +0200 Subject: [PATCH 194/251] Move extension to dedicated file. --- .../libraries/designsystem/text/FontSize.kt | 37 +++++++++++++++++++ .../designsystem/text/UnitConverters.kt | 16 -------- 2 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/FontSize.kt diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/FontSize.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/FontSize.kt new file mode 100644 index 0000000000..4dcc05ee29 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/FontSize.kt @@ -0,0 +1,37 @@ +/* + * 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.libraries.designsystem.text + +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.Dp + +/** + * Return the maximum value between the receiver value and the value with fonScale applied. + */ +@Composable +fun Dp.scaleMax(): Dp = with(LocalDensity.current) { + return this@scaleMax * fontScale.coerceAtMost(1f) +} + +/** + * Return the minimum value between the receiver value and the value with fonScale applied. + */ +@Composable +fun Dp.scaleMin(): Dp = with(LocalDensity.current) { + return this@scaleMin * fontScale.coerceAtLeast(1f) +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/UnitConverters.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/UnitConverters.kt index e3a96c2db3..1ee7d0d603 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/UnitConverters.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/UnitConverters.kt @@ -52,19 +52,3 @@ fun Dp.toPx(): Float = with(LocalDensity.current) { toPx() } */ @Composable fun Dp.roundToPx(): Int = with(LocalDensity.current) { roundToPx() } - -/** - * Return the maximum value between the receiver value and the value with fonScale applied. - */ -@Composable -fun Dp.scaleMax(): Dp = with(LocalDensity.current) { - return this@scaleMax * fontScale.coerceAtMost(1f) -} - -/** - * Return the minimum value between the receiver value and the value with fonScale applied. - */ -@Composable -fun Dp.scaleMin(): Dp = with(LocalDensity.current) { - return this@scaleMin * fontScale.coerceAtLeast(1f) -} From 0b5a3c56d69b266b74e54a06c2ed34da44463f8d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 28 Jul 2023 10:29:13 +0200 Subject: [PATCH 195/251] Update the API and add UI screenshots demoing it. --- .../impl/components/RoomListTopBar.kt | 6 +- .../libraries/designsystem/text/DpScale.kt | 119 ++++++++++++++++++ .../libraries/designsystem/text/FontSize.kt | 37 ------ .../libraries/textcomposer/TextComposer.kt | 28 ++--- 4 files changed, 136 insertions(+), 54 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt delete mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/FontSize.kt diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt index 0c8522e7be..c144ef8671 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt @@ -43,7 +43,7 @@ import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.text.scaleMax +import io.element.android.libraries.designsystem.text.applyScaleDown import io.element.android.libraries.designsystem.text.toSp import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.DropdownMenu @@ -118,8 +118,8 @@ private fun DefaultRoomListTopBar( else ElementTheme.typography.fontHeadingLgBold.copy( // Due to a limitation of MediumTopAppBar, and to avoid the text to be truncated, - // limit the size to 28.dp instead of 28.sp - fontSize = 28.dp.scaleMax().toSp() + // ensure that the font size will never be bigger than 28.dp. + fontSize = 28.dp.applyScaleDown().toSp() ) Text( style = fontStyle, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt new file mode 100644 index 0000000000..c4c524e804 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt @@ -0,0 +1,119 @@ +/* + * 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.libraries.designsystem.text + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.theme.ElementTheme + +/** + * Return the maximum value between the receiver value and the value with fonScale applied. + * So if fontScale is >= 1f, the same value is returned, and if fontScale is < 1f, so returned value + * will be smaller. + */ +@Composable +fun Dp.applyScaleDown(): Dp = with(LocalDensity.current) { + return this@applyScaleDown * fontScale.coerceAtMost(1f) +} + +/** + * Return the minimum value between the receiver value and the value with fonScale applied. + * So if fontScale is <= 1f, the same value is returned, and if fontScale is > 1f, so returned value + * will be bigger. + */ +@Composable +fun Dp.applyScaleUp(): Dp = with(LocalDensity.current) { + return this@applyScaleUp * fontScale.coerceAtLeast(1f) +} + +@Preview(fontScale = 0.75f) +@Composable +fun DpScalePreview_0_75f() = ElementPreviewLight { + val fontSizeInDp = 16.dp + Column( + modifier = Modifier.padding(4.dp), + verticalArrangement = Arrangement.spacedBy(6.dp) + ) { + Text( + text = "A text should have a size of 16.sp", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.toSp()) + ) + Text( + text = "A text should have the same size (applyScaleUp)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleUp().toSp()) + ) + Text( + text = "A text should be smaller (applyScaleDown)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleDown().toSp()) + ) + } +} + +@Preview(fontScale = 1.0f) +@Composable +fun DpScalePreview_1_0f() = ElementPreviewLight { + val fontSizeInDp = 16.dp + Column( + modifier = Modifier.padding(4.dp), + verticalArrangement = Arrangement.spacedBy(6.dp) + ) { + Text( + text = "A text with a size of 16.sp", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.toSp()) + ) + Text( + text = "A text with the same size (applyScaleUp)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleUp().toSp()) + ) + Text( + text = "A text with the same size (applyScaleDown)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleDown().toSp()) + ) + } +} + +@Preview(fontScale = 1.5f) +@Composable +fun DpScalePreview_1_5f() = ElementPreviewLight { + val fontSizeInDp = 16.dp + Column( + modifier = Modifier.padding(4.dp), + verticalArrangement = Arrangement.spacedBy(6.dp) + ) { + Text( + text = "A text with a size of 16.sp", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.toSp()) + ) + Text( + text = "A text with a bigger size (applyScaleUp)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleUp().toSp()) + ) + Text( + text = "A text with the same size (applyScaleDown)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleDown().toSp()) + ) + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/FontSize.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/FontSize.kt deleted file mode 100644 index 4dcc05ee29..0000000000 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/FontSize.kt +++ /dev/null @@ -1,37 +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.libraries.designsystem.text - -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.Dp - -/** - * Return the maximum value between the receiver value and the value with fonScale applied. - */ -@Composable -fun Dp.scaleMax(): Dp = with(LocalDensity.current) { - return this@scaleMax * fontScale.coerceAtMost(1f) -} - -/** - * Return the minimum value between the receiver value and the value with fonScale applied. - */ -@Composable -fun Dp.scaleMin(): Dp = with(LocalDensity.current) { - return this@scaleMin * fontScale.coerceAtLeast(1f) -} diff --git a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index ecf721ac1d..a802298624 100644 --- a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -73,7 +73,7 @@ import io.element.android.libraries.designsystem.VectorIcons import io.element.android.libraries.designsystem.modifiers.applyIf import io.element.android.libraries.designsystem.preview.DayNightPreviews import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.text.scaleMin +import io.element.android.libraries.designsystem.text.applyScaleUp import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text @@ -110,8 +110,8 @@ fun TextComposer( ) { AttachmentButton(onClick = onAddAttachment, modifier = Modifier.padding(vertical = 6.dp)) Spacer(modifier = Modifier.width(12.dp)) - val roundCornerSmall = 20.dp.scaleMin() - val roundCornerLarge = 28.dp.scaleMin() + val roundCornerSmall = 20.dp.applyScaleUp() + val roundCornerLarge = 28.dp.applyScaleUp() var lineCount by remember { mutableStateOf(0) } val roundedCornerSize = remember(lineCount, composerMode) { @@ -128,7 +128,7 @@ fun TextComposer( ) ) val roundedCorners = RoundedCornerShape(roundedCornerSizeState.value) - val minHeight = 42.dp.scaleMin() + val minHeight = 42.dp.applyScaleUp() val bgColor = ElementTheme.colors.bgSubtleSecondary // Change border color depending on focus var hasFocus by remember { mutableStateOf(false) } @@ -169,7 +169,7 @@ fun TextComposer( singleLine = false, visualTransformation = VisualTransformation.None, shape = roundedCorners, - contentPadding = PaddingValues(top = 10.dp.scaleMin(), bottom = 10.dp.scaleMin(), start = 12.dp.scaleMin(), end = 42.dp.scaleMin()), + contentPadding = PaddingValues(top = 10.dp.applyScaleUp(), bottom = 10.dp.applyScaleUp(), start = 12.dp.applyScaleUp(), end = 42.dp.applyScaleUp()), interactionSource = remember { MutableInteractionSource() }, placeholder = { Text(stringResource(CommonStrings.common_message), style = defaultTypography) @@ -197,7 +197,7 @@ fun TextComposer( canSendMessage = composerCanSendMessage, onSendMessage = onSendMessage, composerMode = composerMode, - modifier = Modifier.padding(end = 6.dp.scaleMin(), bottom = 6.dp.scaleMin()) + modifier = Modifier.padding(end = 6.dp.applyScaleUp(), bottom = 6.dp.applyScaleUp()) ) } } @@ -257,7 +257,7 @@ private fun EditingModeView( tint = ElementTheme.materialColors.secondary, modifier = Modifier .padding(vertical = 8.dp) - .size(16.dp.scaleMin()), + .size(16.dp.applyScaleUp()), ) Text( stringResource(CommonStrings.common_editing), @@ -274,7 +274,7 @@ private fun EditingModeView( tint = ElementTheme.materialColors.secondary, modifier = Modifier .padding(top = 8.dp, bottom = 8.dp, start = 16.dp, end = 12.dp) - .size(16.dp.scaleMin()) + .size(16.dp.applyScaleUp()) .clickable( enabled = true, onClick = onResetComposerMode, @@ -337,7 +337,7 @@ private fun ReplyToModeView( tint = MaterialTheme.colorScheme.secondary, modifier = Modifier .padding(end = 4.dp, top = 4.dp, start = 16.dp, bottom = 16.dp) - .size(16.dp.scaleMin()) + .size(16.dp.applyScaleUp()) .clickable( enabled = true, onClick = onResetComposerMode, @@ -355,13 +355,13 @@ private fun AttachmentButton( ) { Surface( modifier - .size(30.dp.scaleMin()) + .size(30.dp.applyScaleUp()) .clickable(onClick = onClick), shape = CircleShape, color = ElementTheme.colors.iconPrimary ) { Image( - modifier = Modifier.size(12.5f.dp.scaleMin()), + modifier = Modifier.size(12.5f.dp.applyScaleUp()), painter = painterResource(R.drawable.ic_add_attachment), contentDescription = stringResource(R.string.rich_text_editor_a11y_add_attachment), contentScale = ContentScale.Inside, @@ -385,10 +385,10 @@ private fun BoxScope.SendButton( modifier = modifier .clip(CircleShape) .background(if (canSendMessage) ElementTheme.colors.iconAccentTertiary else Color.Transparent) - .size(30.dp.scaleMin()) + .size(30.dp.applyScaleUp()) .align(Alignment.BottomEnd) .applyIf(composerMode !is MessageComposerMode.Edit, ifTrue = { - padding(start = 1.dp.scaleMin()) // Center the arrow in the circle + padding(start = 1.dp.applyScaleUp()) // Center the arrow in the circle }) .clickable( enabled = canSendMessage, @@ -408,7 +408,7 @@ private fun BoxScope.SendButton( else -> stringResource(CommonStrings.action_send) } Icon( - modifier = Modifier.size(16.dp.scaleMin()), + modifier = Modifier.size(16.dp.applyScaleUp()), resourceId = iconId, contentDescription = contentDescription, // Exception here, we use Color.White instead of ElementTheme.colors.iconOnSolidPrimary From ca4bbbc050464c92dcc4e6a0ff89ae2c9d17e870 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 28 Jul 2023 13:40:18 +0200 Subject: [PATCH 196/251] No crash when room is already destroyed... --- .../matrix/impl/room/RoomListExtensions.kt | 47 ++++++++++++------- .../matrix/impl/sync/SyncServiceExtension.kt | 5 +- .../impl/timeline/RoomTimelineExtensions.kt | 13 +++-- .../matrix/impl/util/CallbackFlow.kt | 7 ++- .../util/{TaskHandleBag.kt => TaskHandle.kt} | 8 +++- 5 files changed, 52 insertions(+), 28 deletions(-) rename libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/{TaskHandleBag.kt => TaskHandle.kt} (91%) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt index b74ab8968e..5d92bfde0b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.matrix.impl.room +import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.matrix.impl.util.mxCallbackFlow import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.trySendBlocking @@ -40,9 +41,15 @@ fun RoomList.loadingStateFlow(): Flow = trySendBlocking(state) } } - val result = loadingState(listener) - send(result.state) - result.stateStream + tryOrNull { + val result = loadingState(listener) + try { + send(result.state) + } catch (exception: Exception) { + Timber.d("loadingStateFlow() initialState failed.") + } + result.stateStream + } }.buffer(Channel.UNLIMITED) fun RoomList.entriesFlow(onInitialList: suspend (List) -> Unit): Flow> = @@ -52,9 +59,27 @@ fun RoomList.entriesFlow(onInitialList: suspend (List) -> Unit): trySendBlocking(roomEntriesUpdate) } } - val result = entries(listener) - onInitialList(result.entries) - result.entriesStream + tryOrNull { + val result = entries(listener) + try { + onInitialList(result.entries) + } catch (exception: Exception) { + Timber.d(exception, "entriesFlow() onInitialList failed.") + } + result.entriesStream + } + }.buffer(Channel.UNLIMITED) + +fun RoomListService.stateFlow(): Flow = + mxCallbackFlow { + val listener = object : RoomListServiceStateListener { + override fun onUpdate(state: RoomListServiceState) { + trySendBlocking(state) + } + } + tryOrNull { + state(listener) + } }.buffer(Channel.UNLIMITED) fun RoomListService.roomOrNull(roomId: String): RoomListItem? { @@ -65,13 +90,3 @@ fun RoomListService.roomOrNull(roomId: String): RoomListItem? { return null } } - -fun RoomListService.stateFlow(): Flow = - mxCallbackFlow { - val listener = object : RoomListServiceStateListener { - override fun onUpdate(state: RoomListServiceState) { - trySendBlocking(state) - } - } - state(listener) - }.buffer(Channel.UNLIMITED) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SyncServiceExtension.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SyncServiceExtension.kt index 36dabb71f3..a8e366ff7b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SyncServiceExtension.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SyncServiceExtension.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.matrix.impl.sync +import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.matrix.impl.util.mxCallbackFlow import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.trySendBlocking @@ -32,5 +33,7 @@ fun SyncService.stateFlow(): Flow = trySendBlocking(state) } } - state(listener) + tryOrNull { + state(listener) + } }.buffer(Channel.UNLIMITED) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt index 29b75a1dca..3c5bb26e30 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt @@ -16,6 +16,8 @@ package io.element.android.libraries.matrix.impl.timeline +import io.element.android.libraries.core.data.tryOrNull +import io.element.android.libraries.matrix.impl.util.cancelAndDestroy import io.element.android.libraries.matrix.impl.util.destroyAll import io.element.android.libraries.matrix.impl.util.mxCallbackFlow import kotlinx.coroutines.channels.Channel @@ -35,14 +37,14 @@ import timber.log.Timber internal fun Room.timelineDiffFlow(onInitialList: suspend (List) -> Unit): Flow = callbackFlow { - val roomId = id() - Timber.d("Open timelineDiffFlow for room $roomId") val listener = object : TimelineListener { override fun onUpdate(diff: TimelineDiff) { trySendBlocking(diff) } } var result: RoomTimelineListenerResult? = null + val roomId = tryOrNull { id() } + Timber.d("Open timelineDiffFlow for room $roomId") try { result = addTimelineListener(listener) onInitialList(result.items) @@ -51,8 +53,7 @@ internal fun Room.timelineDiffFlow(onInitialList: suspend (List) - } awaitClose { Timber.d("Close timelineDiffFlow for room $roomId") - result?.itemsStream?.cancel() - result?.itemsStream?.destroy() + result?.itemsStream?.cancelAndDestroy() result?.items?.destroyAll() } }.buffer(Channel.UNLIMITED) @@ -64,5 +65,7 @@ internal fun Room.backPaginationStatusFlow(): Flow = trySendBlocking(status) } } - subscribeToBackPaginationStatus(listener) + tryOrNull { + subscribeToBackPaginationStatus(listener) + } }.buffer(Channel.UNLIMITED) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/CallbackFlow.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/CallbackFlow.kt index a347973e89..283b5f7076 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/CallbackFlow.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/CallbackFlow.kt @@ -21,11 +21,10 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow import org.matrix.rustcomponents.sdk.TaskHandle -internal fun mxCallbackFlow(block: suspend ProducerScope.() -> TaskHandle) = +internal fun mxCallbackFlow(block: suspend ProducerScope.() -> TaskHandle?) = callbackFlow { - val token: TaskHandle = block(this) + val token: TaskHandle? = block(this) awaitClose { - token.cancel() - token.destroy() + token?.cancelAndDestroy() } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandleBag.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandle.kt similarity index 91% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandleBag.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandle.kt index 9a21645351..937e73e72e 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandleBag.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandle.kt @@ -28,9 +28,13 @@ class TaskHandleBag(private val tokens: MutableSet = CopyOnWriteArra fun dispose() { tokens.forEach { - it.cancel() - it.destroy() + it.cancelAndDestroy() } tokens.clear() } } + +fun TaskHandle.cancelAndDestroy() { + cancel() + destroy() +} From 157e761000a7ca4d7cabce0f0afbc155b17ea531 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 28 Jul 2023 15:33:35 +0200 Subject: [PATCH 197/251] Rewrite the test, maybe the CI will be happier. --- .../element/android/features/messages/MessagesPresenterTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index 6ff003b735..c19dbe7cdc 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -548,8 +548,9 @@ class MessagesPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = consumeItemsUntilTimeout().last() + val initialState = consumeItemsUntilPredicate { it.userHasPermissionToRedact }.last() assertThat(initialState.userHasPermissionToRedact).isTrue() + cancelAndIgnoreRemainingEvents() } } From aabb455ea4c5f85a5b741e7ed758b28f94735e7b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 28 Jul 2023 11:59:49 +0200 Subject: [PATCH 198/251] Split long line. --- .../element/android/libraries/textcomposer/TextComposer.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index a802298624..7b54b02c16 100644 --- a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -169,7 +169,12 @@ fun TextComposer( singleLine = false, visualTransformation = VisualTransformation.None, shape = roundedCorners, - contentPadding = PaddingValues(top = 10.dp.applyScaleUp(), bottom = 10.dp.applyScaleUp(), start = 12.dp.applyScaleUp(), end = 42.dp.applyScaleUp()), + contentPadding = PaddingValues( + top = 10.dp.applyScaleUp(), + bottom = 10.dp.applyScaleUp(), + start = 12.dp.applyScaleUp(), + end = 42.dp.applyScaleUp(), + ), interactionSource = remember { MutableInteractionSource() }, placeholder = { Text(stringResource(CommonStrings.common_message), style = defaultTypography) From 4d7307bae29461b34942e03463489ae360b387dc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 28 Jul 2023 16:04:30 +0200 Subject: [PATCH 199/251] Showkase does not take into account the `fontScale` parameter of the Preview annotation, so alter the LocalDensity in the CompositionLocalProvider. --- .../designsystem/preview/WithFontScale.kt | 38 ++++++ .../libraries/designsystem/text/DpScale.kt | 121 +++++++++--------- 2 files changed, 102 insertions(+), 57 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/WithFontScale.kt diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/WithFontScale.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/WithFontScale.kt new file mode 100644 index 0000000000..6d3ecfc82b --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/WithFontScale.kt @@ -0,0 +1,38 @@ +/* + * 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.libraries.designsystem.preview + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.Density + +/** + * Showkase does not take into account the `fontScale` parameter of the Preview annotation, so alter the + * LocalDensity in the CompositionLocalProvider. + */ +@Composable +fun WithFontScale(fontScale: Float, content: @Composable () -> Unit) { + CompositionLocalProvider( + LocalDensity provides Density( + density = LocalDensity.current.density, + fontScale = fontScale + ) + ) { + content() + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt index c4c524e804..8576214351 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.preview.WithFontScale import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.theme.ElementTheme @@ -49,71 +50,77 @@ fun Dp.applyScaleUp(): Dp = with(LocalDensity.current) { return this@applyScaleUp * fontScale.coerceAtLeast(1f) } -@Preview(fontScale = 0.75f) +@Preview @Composable -fun DpScalePreview_0_75f() = ElementPreviewLight { - val fontSizeInDp = 16.dp - Column( - modifier = Modifier.padding(4.dp), - verticalArrangement = Arrangement.spacedBy(6.dp) - ) { - Text( - text = "A text should have a size of 16.sp", - style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.toSp()) - ) - Text( - text = "A text should have the same size (applyScaleUp)", - style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleUp().toSp()) - ) - Text( - text = "A text should be smaller (applyScaleDown)", - style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleDown().toSp()) - ) +fun DpScalePreview_0_75f() = WithFontScale(0.75f) { + ElementPreviewLight { + val fontSizeInDp = 16.dp + Column( + modifier = Modifier.padding(4.dp), + verticalArrangement = Arrangement.spacedBy(6.dp) + ) { + Text( + text = "Text with size of 16.sp", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.toSp()) + ) + Text( + text = "Text with the same size (applyScaleUp)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleUp().toSp()) + ) + Text( + text = "Text with a smaller size (applyScaleDown)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleDown().toSp()) + ) + } } } -@Preview(fontScale = 1.0f) +@Preview @Composable -fun DpScalePreview_1_0f() = ElementPreviewLight { - val fontSizeInDp = 16.dp - Column( - modifier = Modifier.padding(4.dp), - verticalArrangement = Arrangement.spacedBy(6.dp) - ) { - Text( - text = "A text with a size of 16.sp", - style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.toSp()) - ) - Text( - text = "A text with the same size (applyScaleUp)", - style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleUp().toSp()) - ) - Text( - text = "A text with the same size (applyScaleDown)", - style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleDown().toSp()) - ) +fun DpScalePreview_1_0f() = WithFontScale(1f) { + ElementPreviewLight { + val fontSizeInDp = 16.dp + Column( + modifier = Modifier.padding(4.dp), + verticalArrangement = Arrangement.spacedBy(6.dp) + ) { + Text( + text = "Text with size of 16.sp", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.toSp()) + ) + Text( + text = "Text with the same size (applyScaleUp)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleUp().toSp()) + ) + Text( + text = "Text with the same size (applyScaleDown)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleDown().toSp()) + ) + } } } -@Preview(fontScale = 1.5f) +@Preview @Composable -fun DpScalePreview_1_5f() = ElementPreviewLight { - val fontSizeInDp = 16.dp - Column( - modifier = Modifier.padding(4.dp), - verticalArrangement = Arrangement.spacedBy(6.dp) - ) { - Text( - text = "A text with a size of 16.sp", - style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.toSp()) - ) - Text( - text = "A text with a bigger size (applyScaleUp)", - style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleUp().toSp()) - ) - Text( - text = "A text with the same size (applyScaleDown)", - style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleDown().toSp()) - ) +fun DpScalePreview_1_5f() = WithFontScale(1.5f) { + ElementPreviewLight { + val fontSizeInDp = 16.dp + Column( + modifier = Modifier.padding(4.dp), + verticalArrangement = Arrangement.spacedBy(6.dp) + ) { + Text( + text = "Text with size of 16.sp", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.toSp()) + ) + Text( + text = "Text with a bigger size (applyScaleUp)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleUp().toSp()) + ) + Text( + text = "Text with the same size (applyScaleDown)", + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = fontSizeInDp.applyScaleDown().toSp()) + ) + } } } From a2c59d4469b46233bfc9b4ee63ef2ac72bb264c4 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 28 Jul 2023 16:22:30 +0200 Subject: [PATCH 200/251] Timeline: move TimelineEncryptedHistoryPostProcessor off the main thread --- libraries/matrix/impl/build.gradle.kts | 1 + .../impl/timeline/RustMatrixTimeline.kt | 1 + .../TimelineEncryptedHistoryPostProcessor.kt | 11 +++++++--- ...melineEncryptedHistoryPostProcessorTest.kt | 20 +++++++++++-------- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts index 112cc0777c..499e36988c 100644 --- a/libraries/matrix/impl/build.gradle.kts +++ b/libraries/matrix/impl/build.gradle.kts @@ -45,4 +45,5 @@ dependencies { testImplementation(libs.test.junit) testImplementation(libs.test.truth) testImplementation(projects.libraries.matrix.test) + testImplementation(libs.coroutines.test) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt index 796b48ebb3..1a5bbf2e2a 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt @@ -81,6 +81,7 @@ class RustMatrixTimeline( lastLoginTimestamp = lastLoginTimestamp, isRoomEncrypted = matrixRoom.isEncrypted, paginationStateFlow = _paginationState, + dispatcher = dispatcher, ) private val timelineItemFactory = MatrixTimelineItemMapper( diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt index 0fe12e6391..b273bef21b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt @@ -19,18 +19,23 @@ package io.element.android.libraries.matrix.impl.timeline.postprocessor import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.getAndUpdate +import kotlinx.coroutines.withContext +import timber.log.Timber import java.util.Date class TimelineEncryptedHistoryPostProcessor( + private val dispatcher: CoroutineDispatcher, private val lastLoginTimestamp: Date?, private val isRoomEncrypted: Boolean, private val paginationStateFlow: MutableStateFlow, ) { - fun process(items: List): List { - if (!isRoomEncrypted || lastLoginTimestamp == null) return items + suspend fun process(items: List): List = withContext(dispatcher) { + Timber.d("Process on Thread=${Thread.currentThread()}") + if (!isRoomEncrypted || lastLoginTimestamp == null) return@withContext items val filteredItems = replaceWithEncryptionHistoryBannerIfNeeded(items) // Disable back pagination @@ -43,7 +48,7 @@ class TimelineEncryptedHistoryPostProcessor( ) } } - return filteredItems + filteredItems } private fun replaceWithEncryptionHistoryBannerIfNeeded(list: List): List { diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt index 91f0bc1883..3b04895bfb 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt @@ -22,6 +22,9 @@ import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem import io.element.android.libraries.matrix.test.room.anEventTimelineItem import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest import org.junit.Test import java.util.Date @@ -30,7 +33,7 @@ class TimelineEncryptedHistoryPostProcessorTest { private val defaultLastLoginTimestamp = Date(1689061264L) @Test - fun `given an unencrypted room, nothing is done`() { + fun `given an unencrypted room, nothing is done`() = runTest { val processor = createPostProcessor(isRoomEncrypted = false) val items = listOf( MatrixTimelineItem.Event(0L, anEventTimelineItem()) @@ -39,7 +42,7 @@ class TimelineEncryptedHistoryPostProcessorTest { } @Test - fun `given a null lastLoginTimestamp, nothing is done`() { + fun `given a null lastLoginTimestamp, nothing is done`() = runTest { val processor = createPostProcessor(lastLoginTimestamp = null) val items = listOf( MatrixTimelineItem.Event(0L, anEventTimelineItem()) @@ -48,14 +51,14 @@ class TimelineEncryptedHistoryPostProcessorTest { } @Test - fun `given an empty list, nothing is done`() { + fun `given an empty list, nothing is done`() = runTest { val processor = createPostProcessor() val items = emptyList() assertThat(processor.process(items)).isSameInstanceAs(items) } @Test - fun `given a list with no items before lastLoginTimestamp, nothing is done`() { + fun `given a list with no items before lastLoginTimestamp, nothing is done`() = runTest { val processor = createPostProcessor() val items = listOf( MatrixTimelineItem.Event(0L, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time + 1)) @@ -64,7 +67,7 @@ class TimelineEncryptedHistoryPostProcessorTest { } @Test - fun `given a list with an item with equal timestamp as lastLoginTimestamp, it's replaced`() { + fun `given a list with an item with equal timestamp as lastLoginTimestamp, it's replaced`() = runTest { val processor = createPostProcessor() val items = listOf( MatrixTimelineItem.Event(0L, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time)) @@ -74,7 +77,7 @@ class TimelineEncryptedHistoryPostProcessorTest { } @Test - fun `given a list with an item with a lower timestamp than lastLoginTimestamp, it's replaced`() { + fun `given a list with an item with a lower timestamp than lastLoginTimestamp, it's replaced`() = runTest { val processor = createPostProcessor() val items = listOf( MatrixTimelineItem.Event(0L, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time - 1)) @@ -85,7 +88,7 @@ class TimelineEncryptedHistoryPostProcessorTest { } @Test - fun `given a list with several with lower or equal timestamps than lastLoginTimestamp, they're replaced and the user can't back paginate`() { + fun `given a list with several with lower or equal timestamps than lastLoginTimestamp, they're replaced and the user can't back paginate`() = runTest { val paginationStateFlow = MutableStateFlow(MatrixTimeline.PaginationState(hasMoreToLoadBackwards = true, isBackPaginating = false)) val processor = createPostProcessor(paginationStateFlow = paginationStateFlow) val items = listOf( @@ -102,7 +105,7 @@ class TimelineEncryptedHistoryPostProcessorTest { assertThat(paginationStateFlow.value).isEqualTo(MatrixTimeline.PaginationState(hasMoreToLoadBackwards = false, isBackPaginating = false)) } - private fun createPostProcessor( + private fun TestScope.createPostProcessor( lastLoginTimestamp: Date? = defaultLastLoginTimestamp, isRoomEncrypted: Boolean = true, paginationStateFlow: MutableStateFlow = @@ -111,5 +114,6 @@ class TimelineEncryptedHistoryPostProcessorTest { lastLoginTimestamp = lastLoginTimestamp, isRoomEncrypted = isRoomEncrypted, paginationStateFlow = paginationStateFlow, + dispatcher = StandardTestDispatcher(testScheduler) ) } From cac4aa6f42e09a7e822f694505ad144e13c6f0c5 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 28 Jul 2023 14:24:03 +0000 Subject: [PATCH 201/251] Update screenshots --- ...efaultGroup_DpScalePreview_0_75f_0_null,NEXUS_5,1.0,en].png | 3 +++ ...DefaultGroup_DpScalePreview_1_0f_0_null,NEXUS_5,1.0,en].png | 3 +++ ...DefaultGroup_DpScalePreview_1_5f_0_null,NEXUS_5,1.0,en].png | 3 +++ 3 files changed, 9 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_0_75f_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_0f_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_5f_0_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_0_75f_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_0_75f_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..60a3b8f39e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_0_75f_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:373fcfdea163cd59d1b929adb1abf7163c0e1aaa203033292931fd22cd5c60d6 +size 22378 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_0f_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_0f_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9a02d72493 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_0f_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1e171c6295cd93af0fc0533b8c3099c75d5343132477709e231848c0c51b35f +size 24366 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_5f_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_5f_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9600daf12b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.text_null_DefaultGroup_DpScalePreview_1_5f_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:45485288d1cd0552c371aa868600e1fd8c30232ea2d9cb26420ec36c539cdb6f +size 28228 From 8dd1aeabff062ac0ba023b8f50718f82d59e3e52 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 28 Jul 2023 16:15:34 +0200 Subject: [PATCH 202/251] fix typo --- .../io/element/android/libraries/designsystem/text/DpScale.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt index 8576214351..7a4f5dd172 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt @@ -31,7 +31,7 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.theme.ElementTheme /** - * Return the maximum value between the receiver value and the value with fonScale applied. + * Return the maximum value between the receiver value and the value with fontScale applied. * So if fontScale is >= 1f, the same value is returned, and if fontScale is < 1f, so returned value * will be smaller. */ @@ -41,7 +41,7 @@ fun Dp.applyScaleDown(): Dp = with(LocalDensity.current) { } /** - * Return the minimum value between the receiver value and the value with fonScale applied. + * Return the minimum value between the receiver value and the value with fontScale applied. * So if fontScale is <= 1f, the same value is returned, and if fontScale is > 1f, so returned value * will be bigger. */ From 42f0474dd2ffd9ede5b02704fa21737cb344b059 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 28 Jul 2023 17:55:43 +0200 Subject: [PATCH 203/251] Messages: remove some blocking code from main thread --- .../features/messages/impl/MessagesPresenter.kt | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index acaaf54c9e..bdb5f2c2ba 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -122,17 +122,12 @@ class MessagesPresenter @AssistedInject constructor( } val inviteProgress = remember { mutableStateOf>(Async.Uninitialized) } - - val showReinvitePrompt by remember( - hasDismissedInviteDialog, - composerState.hasFocus, - syncUpdateFlow, - ) { - derivedStateOf { - !hasDismissedInviteDialog && composerState.hasFocus && room.isDirect && room.activeMemberCount == 1L + var showReinvitePrompt by remember { mutableStateOf(false) } + LaunchedEffect(hasDismissedInviteDialog, composerState.hasFocus, syncUpdateFlow) { + withContext(dispatchers.io) { + showReinvitePrompt = !hasDismissedInviteDialog && composerState.hasFocus && room.isDirect && room.activeMemberCount == 1L } } - val networkConnectionStatus by networkMonitor.connectivity.collectAsState() val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() From a60364d3042cd9094df843deaa5a897ae054123d Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 28 Jul 2023 17:56:17 +0200 Subject: [PATCH 204/251] Makes sure NotificationService is suspendable --- .../matrix/api/notification/NotificationService.kt | 2 +- .../android/libraries/matrix/impl/RustMatrixClient.kt | 2 +- .../matrix/impl/notification/RustNotificationService.kt | 9 ++++++--- .../matrix/test/notification/FakeNotificationService.kt | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationService.kt index 2046252930..4113953203 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationService.kt @@ -21,5 +21,5 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId interface NotificationService { - fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId, filterByPushRules: Boolean): Result + suspend fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId, filterByPushRules: Boolean): Result } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 305bcaaba3..061810e3cd 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -103,7 +103,7 @@ class RustMatrixClient constructor( builder.finish() } - private val notificationService = RustNotificationService(sessionId, notificationClient, clock) + private val notificationService = RustNotificationService(sessionId, notificationClient, dispatchers, clock) private val isLoggingOut = AtomicBoolean(false) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt index 2c4258da11..bb281ba4d7 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt @@ -16,29 +16,32 @@ package io.element.android.libraries.matrix.impl.notification +import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.notification.NotificationData import io.element.android.libraries.matrix.api.notification.NotificationService import io.element.android.services.toolbox.api.systemclock.SystemClock +import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.NotificationClient import org.matrix.rustcomponents.sdk.use class RustNotificationService( sessionId: SessionId, private val notificationClient: NotificationClient, + private val dispatchers: CoroutineDispatchers, clock: SystemClock, ) : NotificationService { private val notificationMapper: NotificationMapper = NotificationMapper(sessionId, clock) - override fun getNotification( + override suspend fun getNotification( userId: SessionId, roomId: RoomId, eventId: EventId, filterByPushRules: Boolean, - ): Result { - return runCatching { + ): Result = withContext(dispatchers.io) { + runCatching { val item = notificationClient.getNotificationWithSlidingSync(roomId.value, eventId.value) item?.use { notificationMapper.map(eventId, roomId, it) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/FakeNotificationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/FakeNotificationService.kt index 9eb5a20ba4..5b863e65f8 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/FakeNotificationService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/FakeNotificationService.kt @@ -23,7 +23,7 @@ import io.element.android.libraries.matrix.api.notification.NotificationData import io.element.android.libraries.matrix.api.notification.NotificationService class FakeNotificationService : NotificationService { - override fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId, filterByPushRules: Boolean): Result { + override suspend fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId, filterByPushRules: Boolean): Result { return Result.success(null) } } From 82d15422413801dfc8f1a39f5c30d14d7e457190 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 29 Jul 2023 04:17:49 +0000 Subject: [PATCH 205/251] Update anvil to v2.4.7-1-8 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ff37e483d8..98767036ee 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -49,7 +49,7 @@ telephoto = "0.4.0" # DI dagger = "2.47" -anvil = "2.4.6" +anvil = "2.4.7-1-8" # Auto service autoservice = "1.1.1" From 1571ba9135d685e033c5ffb713ec593aee25d1e6 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 31 Jul 2023 11:36:28 +0200 Subject: [PATCH 206/251] Small changes after reviews --- .../matrix/impl/room/RoomListExtensions.kt | 33 ++++++++++--------- .../impl/timeline/RoomTimelineExtensions.kt | 13 ++++---- .../matrix/impl/util/CallbackFlow.kt | 4 +-- .../libraries/matrix/impl/util/TaskHandle.kt | 30 ++++++++--------- 4 files changed, 41 insertions(+), 39 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt index 5d92bfde0b..8e7047aaa4 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt @@ -22,6 +22,7 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.trySendBlocking import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.buffer +import kotlinx.coroutines.flow.catch import org.matrix.rustcomponents.sdk.RoomList import org.matrix.rustcomponents.sdk.RoomListEntriesListener import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate @@ -41,15 +42,15 @@ fun RoomList.loadingStateFlow(): Flow = trySendBlocking(state) } } - tryOrNull { - val result = loadingState(listener) - try { - send(result.state) - } catch (exception: Exception) { - Timber.d("loadingStateFlow() initialState failed.") - } - result.stateStream + val result = loadingState(listener) + try { + send(result.state) + } catch (exception: Exception) { + Timber.d("loadingStateFlow() initialState failed.") } + result.stateStream + }.catch { + Timber.d(it, "loadingStateFlow() failed") }.buffer(Channel.UNLIMITED) fun RoomList.entriesFlow(onInitialList: suspend (List) -> Unit): Flow> = @@ -59,15 +60,15 @@ fun RoomList.entriesFlow(onInitialList: suspend (List) -> Unit): trySendBlocking(roomEntriesUpdate) } } - tryOrNull { - val result = entries(listener) - try { - onInitialList(result.entries) - } catch (exception: Exception) { - Timber.d(exception, "entriesFlow() onInitialList failed.") - } - result.entriesStream + val result = entries(listener) + try { + onInitialList(result.entries) + } catch (exception: Exception) { + Timber.d("entriesFlow() onInitialList failed.") } + result.entriesStream + }.catch { + Timber.d(it, "entriesFlow() failed") }.buffer(Channel.UNLIMITED) fun RoomListService.stateFlow(): Flow = diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt index 3c5bb26e30..9f8bccb1d0 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt @@ -26,10 +26,10 @@ import kotlinx.coroutines.channels.trySendBlocking import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.catch import org.matrix.rustcomponents.sdk.BackPaginationStatus import org.matrix.rustcomponents.sdk.BackPaginationStatusListener import org.matrix.rustcomponents.sdk.Room -import org.matrix.rustcomponents.sdk.RoomTimelineListenerResult import org.matrix.rustcomponents.sdk.TimelineDiff import org.matrix.rustcomponents.sdk.TimelineItem import org.matrix.rustcomponents.sdk.TimelineListener @@ -42,20 +42,21 @@ internal fun Room.timelineDiffFlow(onInitialList: suspend (List) - trySendBlocking(diff) } } - var result: RoomTimelineListenerResult? = null - val roomId = tryOrNull { id() } + val roomId = id() Timber.d("Open timelineDiffFlow for room $roomId") + val result = addTimelineListener(listener) try { - result = addTimelineListener(listener) onInitialList(result.items) } catch (exception: Exception) { Timber.d(exception, "Catch failure in timelineDiffFlow of room $roomId") } awaitClose { Timber.d("Close timelineDiffFlow for room $roomId") - result?.itemsStream?.cancelAndDestroy() - result?.items?.destroyAll() + result.itemsStream.cancelAndDestroy() + result.items.destroyAll() } + }.catch { + Timber.d(it, "timelineDiffFlow() failed") }.buffer(Channel.UNLIMITED) internal fun Room.backPaginationStatusFlow(): Flow = diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/CallbackFlow.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/CallbackFlow.kt index 283b5f7076..fbf393e587 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/CallbackFlow.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/CallbackFlow.kt @@ -23,8 +23,8 @@ import org.matrix.rustcomponents.sdk.TaskHandle internal fun mxCallbackFlow(block: suspend ProducerScope.() -> TaskHandle?) = callbackFlow { - val token: TaskHandle? = block(this) + val taskHandle: TaskHandle? = block(this) awaitClose { - token?.cancelAndDestroy() + taskHandle?.cancelAndDestroy() } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandle.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandle.kt index 937e73e72e..5842ba1546 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandle.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/util/TaskHandle.kt @@ -19,22 +19,22 @@ package io.element.android.libraries.matrix.impl.util import org.matrix.rustcomponents.sdk.TaskHandle import java.util.concurrent.CopyOnWriteArraySet -class TaskHandleBag(private val tokens: MutableSet = CopyOnWriteArraySet()) : Set by tokens { - - operator fun plusAssign(taskHandle: TaskHandle?) { - if (taskHandle == null) return - tokens += taskHandle - } - - fun dispose() { - tokens.forEach { - it.cancelAndDestroy() - } - tokens.clear() - } -} - fun TaskHandle.cancelAndDestroy() { cancel() destroy() } + +class TaskHandleBag(private val taskHandles: MutableSet = CopyOnWriteArraySet()) : Set by taskHandles { + + operator fun plusAssign(taskHandle: TaskHandle?) { + if (taskHandle == null) return + taskHandles += taskHandle + } + + fun dispose() { + taskHandles.forEach { + it.cancelAndDestroy() + } + taskHandles.clear() + } +} From 4290ec7e705f48ffa1342a4de83f028db5e1e976 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 31 Jul 2023 11:58:49 +0200 Subject: [PATCH 207/251] Fix CI --- .../android/features/messages/impl/MessagesPresenter.kt | 1 - .../android/features/messages/MessagesPresenterTest.kt | 8 ++++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index bdb5f2c2ba..67499b81a1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -21,7 +21,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index b47ac55d35..7ebfecfae6 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -361,11 +361,15 @@ class MessagesPresenterTest { assertThat(initialState.showReinvitePrompt).isFalse() // When the input field is focused we show the alert initialState.composerState.eventSink(MessageComposerEvents.FocusChanged(true)) - val focusedState = awaitItem() + val focusedState = consumeItemsUntilPredicate { state -> + state.showReinvitePrompt + }.last() assertThat(focusedState.showReinvitePrompt).isTrue() // If it's dismissed then we stop showing the alert initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Cancel)) - val dismissedState = awaitItem() + val dismissedState = consumeItemsUntilPredicate { state -> + !state.showReinvitePrompt + }.last() assertThat(dismissedState.showReinvitePrompt).isFalse() } } From 75137c1469c54e31fb5d4a155f5bd07f9e13ad2a Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 31 Jul 2023 14:01:08 +0200 Subject: [PATCH 208/251] Fix detekt issues in develop (#1004) Co-authored-by: Marco Romano --- .../element/android/libraries/designsystem/text/DpScale.kt | 6 +++--- .../libraries/designsystem/theme/components/Checkbox.kt | 4 +--- .../io/element/android/tests/testutils/ReceiveTurbine.kt | 7 ++++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt index 7a4f5dd172..c6408b662e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/text/DpScale.kt @@ -52,7 +52,7 @@ fun Dp.applyScaleUp(): Dp = with(LocalDensity.current) { @Preview @Composable -fun DpScalePreview_0_75f() = WithFontScale(0.75f) { +internal fun DpScalePreview_0_75f() = WithFontScale(0.75f) { ElementPreviewLight { val fontSizeInDp = 16.dp Column( @@ -77,7 +77,7 @@ fun DpScalePreview_0_75f() = WithFontScale(0.75f) { @Preview @Composable -fun DpScalePreview_1_0f() = WithFontScale(1f) { +internal fun DpScalePreview_1_0f() = WithFontScale(1f) { ElementPreviewLight { val fontSizeInDp = 16.dp Column( @@ -102,7 +102,7 @@ fun DpScalePreview_1_0f() = WithFontScale(1f) { @Preview @Composable -fun DpScalePreview_1_5f() = WithFontScale(1.5f) { +internal fun DpScalePreview_1_5f() = WithFontScale(1.5f) { ElementPreviewLight { val fontSizeInDp = 16.dp Column( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Checkbox.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Checkbox.kt index a1a854ede1..31c6cc3647 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Checkbox.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Checkbox.kt @@ -51,13 +51,11 @@ fun Checkbox( var indeterminateState by remember { mutableStateOf(indeterminate) } androidx.compose.material3.TriStateCheckbox( state = if (!checked && indeterminateState) ToggleableState.Indeterminate else ToggleableState(checked), - onClick = if (onCheckedChange != null) { + onClick = onCheckedChange?.let { { indeterminateState = false onCheckedChange(!checked) } - } else { - null }, modifier = modifier, enabled = enabled, diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt index 57aaa7dbfc..06b6b3d3ea 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/ReceiveTurbine.kt @@ -43,15 +43,16 @@ suspend fun ReceiveTurbine.consumeItemsUntilPredicate( ): List { val items = ArrayList() tryOrNull { - while (true) { + var foundItemOrFinished = false + while (!foundItemOrFinished) { when (val event = withTurbineTimeout(timeout) { awaitEvent() }) { is Event.Item -> { items.add(event.value) if (predicate(event.value)) { - break + foundItemOrFinished = true } } - else -> break + Event.Complete, is Event.Error -> foundItemOrFinished = true } } } From a1dd52447121eaca10004567c847164b4df874a9 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 31 Jul 2023 15:35:38 +0200 Subject: [PATCH 209/251] Try to fix using the merge queue (#1009) --- .github/workflows/build.yml | 3 ++- .github/workflows/danger.yml | 2 +- .github/workflows/gradle-wrapper-validation.yml | 3 ++- .github/workflows/quality.yml | 3 ++- .github/workflows/tests.yml | 3 ++- .github/workflows/validate-lfs.yml | 2 +- 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 657dc7c772..b96e17a306 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,7 +2,8 @@ name: APK Build on: workflow_dispatch: - pull_request: { } + pull_request: + merge_group: push: branches: [ develop ] diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml index 3fd17939e0..cbc57274db 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -1,6 +1,6 @@ name: Danger CI -on: [pull_request] +on: [pull_request, merge_group] jobs: build: diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index 7b68c0077d..271c5399f6 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -1,6 +1,7 @@ name: "Validate Gradle Wrapper" on: - pull_request: { } + pull_request: + merge_group: push: branches: [ main, develop ] diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 6c21ddc55a..b4258627f0 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -2,7 +2,8 @@ name: Code Quality Checks on: workflow_dispatch: - pull_request: { } + pull_request: + merge_group: push: branches: [ main, develop ] diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e4f2300b4c..5eece16f26 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,7 +2,8 @@ name: Test on: workflow_dispatch: - pull_request: { } + pull_request: + merge_group: push: branches: [ main, develop ] diff --git a/.github/workflows/validate-lfs.yml b/.github/workflows/validate-lfs.yml index 25fe50359c..b1d6d8af6f 100644 --- a/.github/workflows/validate-lfs.yml +++ b/.github/workflows/validate-lfs.yml @@ -1,6 +1,6 @@ name: Validate Git LFS -on: [pull_request] +on: [pull_request, merge_group] jobs: build: From dadc0495b24bc484d8491c28b26716282d96fa20 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 31 Jul 2023 15:48:34 +0200 Subject: [PATCH 210/251] Update dependency com.freeletics.flowredux:compose to v1.2.0 (#996) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 98767036ee..a415dac53b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -156,7 +156,7 @@ otaliastudios_transcoder = "com.otaliastudios:transcoder:0.10.5" vanniktech_blurhash = "com.vanniktech:blurhash:0.1.0" vanniktech_emoji = "com.vanniktech:emoji-google:0.16.0" telephoto_zoomableimage = { module = "me.saket.telephoto:zoomable-image-coil", version.ref = "telephoto" } -statemachine = "com.freeletics.flowredux:compose:1.1.0" +statemachine = "com.freeletics.flowredux:compose:1.2.0" maplibre = "org.maplibre.gl:android-sdk:10.2.0" maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:2.0.0" maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:2.0.0" From e88e8069aa3bd9aab3a8efa72df08d853fbf67a0 Mon Sep 17 00:00:00 2001 From: ElementBot <110224175+ElementBot@users.noreply.github.com> Date: Mon, 31 Jul 2023 15:49:52 +0200 Subject: [PATCH 211/251] Sync Strings from Localazy (#1002) Co-authored-by: bmarty --- .../ui-strings/src/main/res/values/localazy.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 2ea156d327..f8ad46d447 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -158,6 +158,10 @@ "%1$d member" "%1$d members" + + "1 vote" + "%d votes" + "Rageshake to report bug" "You seem to be shaking the phone in frustration. Would you like to open the bug report screen?" "This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages." @@ -171,7 +175,16 @@ "Failed uploading media, please try again." "This is a one time process, thanks for waiting." "Setting up your account." + "Additional settings" + "Audio and video calls" + "Direct chats" "Enable notifications on this device" + "Group chats" + "Mentions" + "All" + "Mentions" + "Notify me for" + "Notify me on @room" "To receive notifications, please change your %1$s." "system settings" "System notifications turned off" From fb1299c955b9af9f3aeba226191a0e6a334dc78c Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Mon, 31 Jul 2023 15:49:57 +0200 Subject: [PATCH 212/251] Properly inline `tryOrNull()` (#1003) Nullable params can't be inlined, default empty lambdas are therefore preferred. --- .../main/kotlin/io/element/android/libraries/core/data/Try.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/data/Try.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/data/Try.kt index b91d249547..fe801e71f7 100644 --- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/data/Try.kt +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/data/Try.kt @@ -16,11 +16,11 @@ package io.element.android.libraries.core.data -inline fun tryOrNull(noinline onError: ((Throwable) -> Unit)? = null, operation: () -> A): A? { +inline fun tryOrNull(onError: ((Throwable) -> Unit) = { }, operation: () -> A): A? { return try { operation() } catch (any: Throwable) { - onError?.invoke(any) + onError.invoke(any) null } } From ffdf005507623e5ff53b40281dc1b9fbce973ecd Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 31 Jul 2023 17:19:51 +0200 Subject: [PATCH 213/251] Try to skip extra checks in the CI for up to date PRs in merge queue. (#1010) * Try to skip extra checks in the CI for up to date PRs in merge queue. * Try to simplify check * Remove expression syntax from `if`s --- .github/workflows/build.yml | 3 ++- .github/workflows/danger.yml | 1 + .github/workflows/gradle-wrapper-validation.yml | 1 + .github/workflows/quality.yml | 2 ++ .github/workflows/tests.yml | 1 + .github/workflows/validate-lfs.yml | 1 + 6 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b96e17a306..b06e3f1622 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: debug: name: Build debug APKs runs-on: ubuntu-latest - if: github.ref != 'refs/heads/main' + if: github.ref != 'refs/heads/main' && github.event.merge_group.base_ref != 'refs/heads/develop' strategy: fail-fast: false # Allow all jobs on develop. Just one per PR. @@ -24,6 +24,7 @@ jobs: group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-{0}', github.sha) || format('build-debug-{0}', github.ref) }} cancel-in-progress: true steps: + - run: echo ${{ github.event.merge_group.base_ref }} - uses: actions/checkout@v3 with: # Ensure we are building the branch and not the branch after being merged on develop diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml index cbc57274db..96e9d945b7 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -5,6 +5,7 @@ on: [pull_request, merge_group] jobs: build: runs-on: ubuntu-latest + if: github.event.merge_group.base_ref != 'refs/heads/develop' name: Danger main check steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index 271c5399f6..55d9d45d6d 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -8,6 +8,7 @@ on: jobs: validation: name: "Validation" + if: github.event.merge_group.base_ref != 'refs/heads/develop' runs-on: ubuntu-latest # No concurrency required, this is a prerequisite to other actions and should run every time. steps: diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index b4258627f0..f62b393e45 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -16,6 +16,7 @@ jobs: checkScript: name: Search for forbidden patterns runs-on: ubuntu-latest + if: github.event.merge_group.base_ref != 'refs/heads/develop' steps: - uses: actions/checkout@v3 - name: Run code quality check suite @@ -24,6 +25,7 @@ jobs: check: name: Project Check Suite runs-on: ubuntu-latest + if: github.event.merge_group.base_ref != 'refs/heads/develop' # Allow all jobs on main and develop. Just one per PR. concurrency: group: ${{ github.ref == 'refs/heads/main' && format('check-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-develop-{0}', github.sha) || format('check-{0}', github.ref) }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5eece16f26..82e0708e64 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,6 +16,7 @@ jobs: tests: name: Runs unit tests runs-on: ubuntu-latest + if: github.event.merge_group.base_ref != 'refs/heads/develop' # Allow all jobs on main and develop. Just one per PR. concurrency: diff --git a/.github/workflows/validate-lfs.yml b/.github/workflows/validate-lfs.yml index b1d6d8af6f..e7f5c04d9e 100644 --- a/.github/workflows/validate-lfs.yml +++ b/.github/workflows/validate-lfs.yml @@ -5,6 +5,7 @@ on: [pull_request, merge_group] jobs: build: runs-on: ubuntu-latest + if: github.event.merge_group.base_ref != 'refs/heads/develop' name: Validate steps: - uses: nschloe/action-cached-lfs-checkout@v1.2.1 From 8fd71b41268a17e50a4a4f33a0015c5577a5a039 Mon Sep 17 00:00:00 2001 From: David Langley Date: Mon, 31 Jul 2023 18:39:20 +0100 Subject: [PATCH 214/251] Reaction summary view and sorting reactions by count and then timestamp (#942) * Sort reactions by count and then timestamp - Sort reactions within an aggregation by timestamp descending. This puts the most recent at the top, useful in cases like the reaction summary view or getting the most recent reaction. - Sort aggregated reactions by count and then timestamp ascending, using the most recent reaction in the aggregation. This appends new aggregations on the end of the reaction layout. * Add reaction summary view * fix warnings * Fix test unit tests and add sorting tests - Fix broken build in test code - Add a test for reaction sorting * Remove default closure, move logic to presenter and add tests * Update screenshots * Fix imports * Revert Screenshots I didn't update * Fix imports remove screetshots * Update screenshots * Update screenshots * Address comments. * Update screenshots * Remove unnecessary snapshotFlow * Fix code quality checks --------- Co-authored-by: ElementBot --- .../messages/impl/MessagesPresenter.kt | 4 + .../features/messages/impl/MessagesState.kt | 2 + .../messages/impl/MessagesStateProvider.kt | 5 + .../features/messages/impl/MessagesView.kt | 11 + .../impl/timeline/TimelineStateProvider.kt | 8 +- .../messages/impl/timeline/TimelineView.kt | 6 + .../components/MessagesReactionButton.kt | 22 +- .../components/TimelineItemEventRow.kt | 11 + .../components/TimelineItemReactionsLayout.kt | 9 +- .../components/TimelineItemReactionsView.kt | 11 +- .../reactionsummary/ReactionSummaryEvents.kt | 25 ++ .../ReactionSummaryPresenter.kt | 87 ++++++ .../reactionsummary/ReactionSummaryState.kt | 32 +++ .../ReactionSummaryStateProvider.kt | 37 +++ .../reactionsummary/ReactionSummaryView.kt | 271 ++++++++++++++++++ .../event/TimelineItemEventFactory.kt | 31 +- .../impl/timeline/model/AggregatedReaction.kt | 24 +- .../model/AggregatedReactionProvider.kt | 29 +- .../model/AggregatedReactionSender.kt | 28 ++ .../messages/MessagesPresenterTest.kt | 3 + .../timeline/TimelinePresenterTest.kt | 60 ++++ .../ReactionSummaryPresenterTests.kt | 80 ++++++ .../timeline/model/AggregatedReactionTest.kt | 21 +- .../api/timeline/item/event/EventReaction.kt | 5 +- .../api/timeline/item/event/ReactionSender.kt | 31 ++ .../item/event/EventTimelineItemMapper.kt | 9 +- ...Preview-D-12_13_null_0,NEXUS_5,1.0,en].png | 3 + ...Preview-N-12_14_null_0,NEXUS_5,1.0,en].png | 3 + ...wPreview-D-13_14_null,NEXUS_5,1.0,en].png} | 0 ...wPreview-N-13_15_null,NEXUS_5,1.0,en].png} | 0 30 files changed, 829 insertions(+), 39 deletions(-) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryEvents.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryState.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryStateProvider.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionSender.kt create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ReactionSender.kt create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-12_13_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-12_14_null_0,NEXUS_5,1.0,en].png rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-13_14_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-13_15_null,NEXUS_5,1.0,en].png} (100%) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index 2ea112c1cc..811e087b02 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -39,6 +39,7 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.TimelinePresenter 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.retrysendmenu.RetrySendMenuPresenter import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent @@ -84,6 +85,7 @@ class MessagesPresenter @AssistedInject constructor( private val timelinePresenter: TimelinePresenter, private val actionListPresenter: ActionListPresenter, private val customReactionPresenter: CustomReactionPresenter, + private val reactionSummaryPresenter: ReactionSummaryPresenter, private val retrySendMenuPresenter: RetrySendMenuPresenter, private val networkMonitor: NetworkMonitor, private val snackbarDispatcher: SnackbarDispatcher, @@ -105,6 +107,7 @@ class MessagesPresenter @AssistedInject constructor( val timelineState = timelinePresenter.present() val actionListState = actionListPresenter.present() val customReactionState = customReactionPresenter.present() + val reactionSummaryState = reactionSummaryPresenter.present() val retryState = retrySendMenuPresenter.present() val syncUpdateFlow = room.syncUpdateFlow.collectAsState() @@ -166,6 +169,7 @@ class MessagesPresenter @AssistedInject constructor( timelineState = timelineState, actionListState = actionListState, customReactionState = customReactionState, + reactionSummaryState = reactionSummaryState, retrySendMenuState = retryState, hasNetworkConnection = networkConnectionStatus == NetworkStatus.Online, snackbarMessage = snackbarMessage, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt index 50e952f237..d22d54e7f3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt @@ -21,6 +21,7 @@ import io.element.android.features.messages.impl.actionlist.ActionListState import io.element.android.features.messages.impl.messagecomposer.MessageComposerState 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.reactionsummary.ReactionSummaryState import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -38,6 +39,7 @@ data class MessagesState( val timelineState: TimelineState, val actionListState: ActionListState, val customReactionState: CustomReactionState, + val reactionSummaryState: ReactionSummaryState, val retrySendMenuState: RetrySendMenuState, val hasNetworkConnection: Boolean, val snackbarMessage: SnackbarMessage?, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index bb0b61f620..7da67d468c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -22,6 +22,7 @@ import io.element.android.features.messages.impl.messagecomposer.aMessageCompose import io.element.android.features.messages.impl.timeline.aTimelineItemList import io.element.android.features.messages.impl.timeline.aTimelineState 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.retrysendmenu.RetrySendMenuState import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent import io.element.android.libraries.architecture.Async @@ -68,6 +69,10 @@ fun aMessagesState() = MessagesState( selectedEventId = null, eventSink = {}, ), + reactionSummaryState = ReactionSummaryState( + target = null, + eventSink = {}, + ), hasNetworkConnection = true, snackbarMessage = null, inviteProgress = Async.Uninitialized, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 4d93810e20..b68a889384 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -57,6 +57,8 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer import io.element.android.features.messages.impl.timeline.TimelineView import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionBottomSheet import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents +import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents +import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryView 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 @@ -127,6 +129,11 @@ fun MessagesView( state.eventSink(MessagesEvents.ToggleReaction(emoji, event.eventId)) } + fun onEmojiReactionLongClicked(emoji: String, event: TimelineItem.Event) { + if (event.eventId == null) return + state.reactionSummaryState.eventSink(ReactionSummaryEvents.ShowReactionSummary(event.eventId, event.reactionsState.reactions, emoji)) + } + fun onMoreReactionsClicked(event: TimelineItem.Event) { state.customReactionState.eventSink(CustomReactionEvents.UpdateSelectedEvent(event.eventId)) } @@ -160,6 +167,7 @@ fun MessagesView( } }, onReactionClicked = ::onEmojiReactionClicked, + onReactionLongClicked = ::onEmojiReactionLongClicked, onMoreReactionsClicked = ::onMoreReactionsClicked, onSendLocationClicked = onSendLocationClicked, onSwipeToReply = { targetEvent -> @@ -194,6 +202,7 @@ fun MessagesView( } ) + ReactionSummaryView(state = state.reactionSummaryState) RetrySendMessageMenu( state = state.retrySendMenuState ) @@ -246,6 +255,7 @@ private fun MessagesViewContent( onMessageClicked: (TimelineItem.Event) -> Unit, onUserDataClicked: (UserId) -> Unit, onReactionClicked: (key: String, TimelineItem.Event) -> Unit, + onReactionLongClicked: (key: String, TimelineItem.Event) -> Unit, onMoreReactionsClicked: (TimelineItem.Event) -> Unit, onMessageLongClicked: (TimelineItem.Event) -> Unit, onTimestampClicked: (TimelineItem.Event) -> Unit, @@ -269,6 +279,7 @@ private fun MessagesViewContent( onUserDataClicked = onUserDataClicked, onTimestampClicked = onTimestampClicked, onReactionClicked = onReactionClicked, + onReactionLongClicked = onReactionLongClicked, onMoreReactionsClicked = onMoreReactionsClicked, onSwipeToReply = onSwipeToReply, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index e939b9ff68..b47ded8b3a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt @@ -16,10 +16,10 @@ package io.element.android.features.messages.impl.timeline -import io.element.android.features.messages.impl.timeline.model.AggregatedReaction import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions +import io.element.android.features.messages.impl.timeline.model.anAggregatedReaction import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent @@ -141,7 +141,11 @@ fun aTimelineItemReactions( reactions = buildList { repeat(count) { index -> val key = emojis[index % emojis.size] - add(AggregatedReaction(key = key, count = 1 + index, isHighlighted = isHighlighted)) + add(anAggregatedReaction( + key = key, + count = index + 1, + isHighlighted = isHighlighted + )) } }.toPersistentList() ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index cecc2d7a63..9f81e612ba 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -81,6 +81,7 @@ fun TimelineView( onTimestampClicked: (TimelineItem.Event) -> Unit, onSwipeToReply: (TimelineItem.Event) -> Unit, onReactionClicked: (emoji: String, TimelineItem.Event) -> Unit, + onReactionLongClicked: (emoji: String, TimelineItem.Event) -> Unit, onMoreReactionsClicked: (TimelineItem.Event) -> Unit, modifier: Modifier = Modifier, ) { @@ -121,6 +122,7 @@ fun TimelineView( onUserDataClick = onUserDataClicked, inReplyToClick = ::inReplyToClicked, onReactionClick = onReactionClicked, + onReactionLongClick = onReactionLongClicked, onMoreReactionsClick = onMoreReactionsClicked, onTimestampClicked = onTimestampClicked, onSwipeToReply = onSwipeToReply, @@ -155,6 +157,7 @@ fun TimelineItemRow( onLongClick: (TimelineItem.Event) -> Unit, inReplyToClick: (EventId) -> Unit, onReactionClick: (key: String, TimelineItem.Event) -> Unit, + onReactionLongClick: (key: String, TimelineItem.Event) -> Unit, onMoreReactionsClick: (TimelineItem.Event) -> Unit, onTimestampClicked: (TimelineItem.Event) -> Unit, onSwipeToReply: (TimelineItem.Event) -> Unit, @@ -186,6 +189,7 @@ fun TimelineItemRow( onUserDataClick = onUserDataClick, inReplyToClick = inReplyToClick, onReactionClick = onReactionClick, + onReactionLongClick = onReactionLongClick, onMoreReactionsClick = onMoreReactionsClick, onTimestampClicked = onTimestampClicked, onSwipeToReply = { onSwipeToReply(timelineItem) }, @@ -224,6 +228,7 @@ fun TimelineItemRow( onUserDataClick = onUserDataClick, onTimestampClicked = onTimestampClicked, onReactionClick = onReactionClick, + onReactionLongClick = onReactionLongClick, onMoreReactionsClick = onMoreReactionsClick, onSwipeToReply = {}, ) @@ -321,6 +326,7 @@ internal fun TimelineViewPreview( onUserDataClicked = {}, onMessageLongClicked = {}, onReactionClicked = { _, _ -> }, + onReactionLongClicked = { _, _ -> }, onMoreReactionsClicked = {}, onSwipeToReply = {}, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt index 20658c798e..568bfefd18 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt @@ -17,9 +17,10 @@ package io.element.android.features.messages.impl.timeline.components import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.border -import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -54,8 +55,10 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.theme.ElementTheme @Composable +@OptIn(ExperimentalFoundationApi::class) fun MessagesReactionButton( onClick: () -> Unit, + onLongClick: () -> Unit, content: MessagesReactionsButtonContent, modifier: Modifier = Modifier, ) { @@ -82,7 +85,10 @@ fun MessagesReactionButton( .padding(vertical = 2.dp, horizontal = 2.dp) // Clip click indicator inside the outer border .clip(RoundedCornerShape(corner = CornerSize(12.dp))) - .clickable(onClick = onClick) + .combinedClickable( + onClick = onClick, + onLongClick = onLongClick + ) // Inner border, to highlight when selected .border(BorderStroke(1.dp, borderColor), RoundedCornerShape(corner = CornerSize(12.dp))) .background(buttonColor, RoundedCornerShape(corner = CornerSize(12.dp))) @@ -162,7 +168,8 @@ private fun ReactionContent( internal fun MessagesReactionButtonPreview(@PreviewParameter(AggregatedReactionProvider::class) reaction: AggregatedReaction) = ElementPreview { MessagesReactionButton( content = MessagesReactionsButtonContent.Reaction(reaction), - onClick = {} + onClick = {}, + onLongClick = {} ) } @@ -172,11 +179,13 @@ internal fun MessagesReactionExtraButtonsPreview() = ElementPreview { Row { MessagesReactionButton( content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction), - onClick = {} + onClick = {}, + onLongClick = {} ) MessagesReactionButton( content = MessagesReactionsButtonContent.Text("12 more"), - onClick = {} + onClick = {}, + onLongClick = {} ) MessagesReactionButton( content = MessagesReactionsButtonContent.Reaction( @@ -184,7 +193,8 @@ internal fun MessagesReactionExtraButtonsPreview() = ElementPreview { key = "A very long reaction with many characters that should be truncated" ) ), - onClick = {} + onClick = {}, + onLongClick = {} ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt index 0099c958ca..90d3e6cd8c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt @@ -112,6 +112,7 @@ fun TimelineItemEventRow( inReplyToClick: (EventId) -> Unit, onTimestampClicked: (TimelineItem.Event) -> Unit, onReactionClick: (emoji: String, eventId: TimelineItem.Event) -> Unit, + onReactionLongClick: (emoji: String, eventId: TimelineItem.Event) -> Unit, onMoreReactionsClick: (eventId: TimelineItem.Event) -> Unit, onSwipeToReply: () -> Unit, modifier: Modifier = Modifier @@ -169,6 +170,7 @@ fun TimelineItemEventRow( inReplyToClicked = ::inReplyToClicked, onUserDataClicked = ::onUserDataClicked, onReactionClicked = { emoji -> onReactionClick(emoji, event) }, + onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) }, onMoreReactionsClicked = { onMoreReactionsClick(event) }, ) } @@ -184,6 +186,7 @@ fun TimelineItemEventRow( inReplyToClicked = ::inReplyToClicked, onUserDataClicked = ::onUserDataClicked, onReactionClicked = { emoji -> onReactionClick(emoji, event) }, + onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) }, onMoreReactionsClicked = { onMoreReactionsClick(event) }, ) } @@ -224,6 +227,7 @@ private fun TimelineItemEventRowContent( inReplyToClicked: () -> Unit, onUserDataClicked: () -> Unit, onReactionClicked: (emoji: String) -> Unit, + onReactionLongClicked: (emoji: String) -> Unit, onMoreReactionsClicked: (event: TimelineItem.Event) -> Unit, modifier: Modifier = Modifier, ) { @@ -292,6 +296,7 @@ private fun TimelineItemEventRowContent( reactionsState = event.reactionsState, isOutgoing = event.isMine, onReactionClicked = onReactionClicked, + onReactionLongClicked = onReactionLongClicked, onMoreReactionsClicked = { onMoreReactionsClicked(event) }, modifier = Modifier .constrainAs(reactions) { @@ -588,6 +593,7 @@ private fun ContentToPreview() { onUserDataClick = {}, inReplyToClick = {}, onReactionClick = { _, _ -> }, + onReactionLongClick = { _, _ -> }, onMoreReactionsClick = {}, onTimestampClicked = {}, onSwipeToReply = {}, @@ -607,6 +613,7 @@ private fun ContentToPreview() { onUserDataClick = {}, inReplyToClick = {}, onReactionClick = { _, _ -> }, + onReactionLongClick = { _, _ -> }, onMoreReactionsClick = {}, onTimestampClicked = {}, onSwipeToReply = {}, @@ -653,6 +660,7 @@ private fun ContentToPreviewWithReply() { onUserDataClick = {}, inReplyToClick = {}, onReactionClick = { _, _ -> }, + onReactionLongClick = { _, _ -> }, onMoreReactionsClick = {}, onTimestampClicked = {}, onSwipeToReply = {}, @@ -673,6 +681,7 @@ private fun ContentToPreviewWithReply() { onUserDataClick = {}, inReplyToClick = {}, onReactionClick = { _, _ -> }, + onReactionLongClick = { _, _ -> }, onMoreReactionsClick = {}, onTimestampClicked = {}, onSwipeToReply = {}, @@ -729,6 +738,7 @@ private fun ContentTimestampToPreview(event: TimelineItem.Event) { onUserDataClick = {}, inReplyToClick = {}, onReactionClick = { _, _ -> }, + onReactionLongClick = { _, _ -> }, onMoreReactionsClick = {}, onTimestampClicked = {}, onSwipeToReply = {}, @@ -768,6 +778,7 @@ private fun ContentWithManyReactionsToPreview() { onUserDataClick = {}, inReplyToClick = {}, onReactionClick = { _, _ -> }, + onReactionLongClick = { _, _ -> }, onMoreReactionsClick = {}, onSwipeToReply = {}, onTimestampClicked = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt index 851389c6bd..1bd5cc2c1c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt @@ -185,13 +185,15 @@ internal fun TimelineItemReactionsLayoutPreview() = ElementPreview { content = MessagesReactionsButtonContent.Text( text = stringResource(id = R.string.screen_room_timeline_less_reactions) ), - onClick = { }, + onClick = {}, + onLongClick = {} ) }, addMoreButton = { MessagesReactionButton( content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction), - onClick = {} + onClick = {}, + onLongClick = {} ) }, reactions = { @@ -200,7 +202,8 @@ internal fun TimelineItemReactionsLayoutPreview() = ElementPreview { content = MessagesReactionsButtonContent.Reaction( it ), - onClick = {} + onClick = {}, + onLongClick = {} ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt index 6682a302d2..39281dc26e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt @@ -42,6 +42,7 @@ fun TimelineItemReactions( reactionsState: TimelineItemReactions, isOutgoing: Boolean, onReactionClicked: (emoji: String) -> Unit, + onReactionLongClicked: (emoji: String) -> Unit, onMoreReactionsClicked: () -> Unit, modifier: Modifier = Modifier, ) { @@ -61,6 +62,7 @@ fun TimelineItemReactions( reactions = reactionsState.reactions, expanded = expanded, onReactionClick = onReactionClicked, + onReactionLongClick = onReactionLongClicked, onMoreReactionsClick = onMoreReactionsClicked, onToggleExpandClick = { expanded = !expanded }, ) @@ -72,6 +74,7 @@ private fun TimelineItemReactionsView( reactions: ImmutableList, expanded: Boolean, onReactionClick: (emoji: String) -> Unit, + onReactionLongClick: (emoji: String) -> Unit, onMoreReactionsClick: () -> Unit, onToggleExpandClick: () -> Unit, modifier: Modifier = Modifier @@ -86,19 +89,22 @@ private fun TimelineItemReactionsView( text = stringResource(id = if (expanded) R.string.screen_room_reactions_show_less else R.string.screen_room_reactions_show_more) ), onClick = onToggleExpandClick, + onLongClick = {} ) }, addMoreButton = { MessagesReactionButton( content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction), - onClick = onMoreReactionsClick + onClick = onMoreReactionsClick, + onLongClick = {} ) }, reactions = { reactions.forEach { reaction -> MessagesReactionButton( content = MessagesReactionsButtonContent.Reaction(reaction = reaction), - onClick = { onReactionClick(reaction.key) } + onClick = { onReactionClick(reaction.key) }, + onLongClick = { onReactionLongClick(reaction.key) } ) } } @@ -148,6 +154,7 @@ private fun ContentToPreview( ), isOutgoing = isOutgoing, onReactionClicked = {}, + onReactionLongClicked = {}, onMoreReactionsClicked = {}, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryEvents.kt new file mode 100644 index 0000000000..fdf94f52ce --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryEvents.kt @@ -0,0 +1,25 @@ +/* + * 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.reactionsummary + +import io.element.android.features.messages.impl.timeline.model.AggregatedReaction +import io.element.android.libraries.matrix.api.core.EventId + +sealed interface ReactionSummaryEvents { + object Clear : ReactionSummaryEvents + data class ShowReactionSummary(val eventId: EventId, val reactions: List, val selectedKey: String) : ReactionSummaryEvents +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt new file mode 100644 index 0000000000..456ac5f548 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt @@ -0,0 +1,87 @@ +/* + * 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.reactionsummary + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.RoomMember +import io.element.android.libraries.matrix.api.room.roomMembers +import io.element.android.libraries.matrix.api.user.MatrixUser +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList +import javax.inject.Inject + +class ReactionSummaryPresenter @Inject constructor( + private val room: MatrixRoom, +) : Presenter { + @Composable + override fun present(): ReactionSummaryState { + LaunchedEffect(Unit) { + room.updateMembers() + } + + val membersState by room.membersStateFlow.collectAsState() + + val target: MutableState = remember { + mutableStateOf(null) + } + val targetWithAvatars = populateSenderAvatars(members = membersState.roomMembers().orEmpty().toImmutableList(), summary = target.value) + + fun handleEvents(event: ReactionSummaryEvents) { + when (event) { + is ReactionSummaryEvents.ShowReactionSummary -> target.value = ReactionSummaryState.Summary( + reactions = event.reactions, + selectedKey = event.selectedKey, + selectedEventId = event.eventId + ) + ReactionSummaryEvents.Clear -> target.value = null + } + } + return ReactionSummaryState( + target = targetWithAvatars.value, + eventSink = ::handleEvents + ) + } + + @Composable + private fun populateSenderAvatars(members: ImmutableList, summary: ReactionSummaryState.Summary?) = remember(summary) { + derivedStateOf { + summary?.let { summary -> + summary.copy(reactions = summary.reactions.map { reaction -> + reaction.copy(senders = reaction.senders.map { sender -> + val member = members.firstOrNull { it.userId == sender.senderId } + val user = MatrixUser( + userId = sender.senderId, + displayName = member?.displayName, + avatarUrl = member?.avatarUrl + ) + sender.copy(user = user) + }) + }) + } + } + } + +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryState.kt new file mode 100644 index 0000000000..37e150320b --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryState.kt @@ -0,0 +1,32 @@ +/* + * 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.reactionsummary + +import io.element.android.features.messages.impl.timeline.model.AggregatedReaction +import io.element.android.libraries.matrix.api.core.EventId + +data class ReactionSummaryState( + val target: Summary?, + val eventSink: (ReactionSummaryEvents) -> Unit +){ + data class Summary( + val reactions: List, + val selectedKey: String, + val selectedEventId: EventId + ) +} + diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryStateProvider.kt new file mode 100644 index 0000000000..d6642922bb --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryStateProvider.kt @@ -0,0 +1,37 @@ +/* + * 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.reactionsummary + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.messages.impl.timeline.aTimelineItemReactions +import io.element.android.libraries.matrix.api.core.EventId + +open class ReactionSummaryStateProvider : PreviewParameterProvider { + override val values = sequenceOf(anActionListState()) +} + +fun anActionListState(): ReactionSummaryState { + val reactions = aTimelineItemReactions(8, true).reactions + return ReactionSummaryState( + target = ReactionSummaryState.Summary( + reactions = reactions, + selectedKey = reactions[0].key, + selectedEventId = EventId("$1234"), + ), + eventSink = {} + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt new file mode 100644 index 0000000000..f794d60c81 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt @@ -0,0 +1,271 @@ +/* + * 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.reactionsummary + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.CornerSize +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.element.android.features.messages.impl.timeline.model.AggregatedReaction +import io.element.android.libraries.designsystem.components.avatar.Avatar +import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.preview.DayNightPreviews +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet +import io.element.android.libraries.designsystem.theme.components.Surface +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.ui.model.getAvatarData +import io.element.android.libraries.theme.ElementTheme +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ReactionSummaryView( + state: ReactionSummaryState, + modifier: Modifier = Modifier, +) { + val sheetState = rememberModalBottomSheetState() + + fun onDismiss() { + state.eventSink(ReactionSummaryEvents.Clear) + } + + if (state.target != null) { + ModalBottomSheet( + onDismissRequest = ::onDismiss, + sheetState = sheetState, + modifier = modifier + ) { + SheetContent(summary = state.target) + } + } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +private fun SheetContent( + summary: ReactionSummaryState.Summary, + modifier: Modifier = Modifier, +) { + val animationScope = rememberCoroutineScope() + var selectedReactionKey: String by rememberSaveable { mutableStateOf(summary.selectedKey) } + val selectedReactionIndex: Int by remember { + derivedStateOf { + summary.reactions.indexOfFirst { it.key == selectedReactionKey } + } + } + val pagerState = rememberPagerState(initialPage = selectedReactionIndex) + val reactionListState = rememberLazyListState() + + LaunchedEffect(pagerState.currentPage) { + selectedReactionKey = summary.reactions[pagerState.currentPage].key + val visibleInfo = reactionListState.layoutInfo.visibleItemsInfo + if (selectedReactionIndex <= visibleInfo.first().index || selectedReactionIndex >= visibleInfo.last().index) { + reactionListState.animateScrollToItem(selectedReactionIndex) + } + } + + Column( + modifier = modifier + .fillMaxWidth() + .fillMaxHeight() + ) { + LazyRow(state = reactionListState, + horizontalArrangement = Arrangement.spacedBy(8.dp), + contentPadding = PaddingValues(start = 12.dp, end = 12.dp, bottom = 12.dp) + ) { + items(summary.reactions) { reaction -> + AggregatedReactionButton( + reaction = reaction, + isHighlighted = selectedReactionKey == reaction.key, + onClick = { + selectedReactionKey = reaction.key + animationScope.launch { + pagerState.animateScrollToPage(selectedReactionIndex) + } + } + ) + } + } + HorizontalPager(state = pagerState, pageCount = summary.reactions.size) { page -> + LazyColumn(modifier = Modifier.fillMaxHeight()) { + items(summary.reactions[page].senders) { sender -> + + val user = sender.user ?: MatrixUser(userId = sender.senderId) + + SenderRow( + avatarData = user.getAvatarData(AvatarSize.UserListItem), + name = user.displayName ?: user.userId.value, + userId = user.userId.value, + sentTime = sender.sentTime + ) + } + } + } + } +} + +@Composable +fun AggregatedReactionButton( + reaction: AggregatedReaction, + isHighlighted: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + + val buttonColor = if (isHighlighted) { + ElementTheme.colors.bgActionPrimaryRest + } else { + Color.Transparent + } + + val textColor = if (isHighlighted) { + MaterialTheme.colorScheme.inversePrimary + } else { + MaterialTheme.colorScheme.primary + } + + Surface( + modifier = modifier + .clickable(onClick = onClick) + .background(buttonColor, RoundedCornerShape(corner = CornerSize(percent = 50))) + .padding(vertical = 8.dp, horizontal = 12.dp), + color = buttonColor + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier, + ) { + Text( + text = reaction.displayKey, + style = ElementTheme.typography.fontBodyMdRegular.copy( + fontSize = 20.sp, + lineHeight = 25.sp + ), + ) + if (reaction.count > 1) { + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = reaction.count.toString(), + color = textColor, + style = ElementTheme.typography.fontBodyMdRegular.copy( + fontSize = 20.sp, + lineHeight = 25.sp + ) + ) + } + } + } +} + +@Composable +fun SenderRow( + avatarData: AvatarData, + name: String, + userId: String, + sentTime: String, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .fillMaxWidth() + .heightIn(min = 56.dp) + .padding(start = 16.dp, top = 4.dp, end = 16.dp, bottom = 4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Avatar(avatarData) + Column( + modifier = Modifier.padding(start = 12.dp), + ) { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.Bottom + ) { + Text( + modifier = Modifier + .padding(end = 4.dp) + .weight(1f), + text = name, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = MaterialTheme.colorScheme.primary, + style = ElementTheme.typography.fontBodyMdRegular, + ) + Text( + text = sentTime, + color = MaterialTheme.colorScheme.secondary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = ElementTheme.typography.fontBodySmRegular, + ) + } + Text( + text = userId, + color = MaterialTheme.colorScheme.secondary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = ElementTheme.typography.fontBodySmRegular, + ) + } + } +} + +@DayNightPreviews +@Composable +internal fun SheetContentPreview( + @PreviewParameter(ReactionSummaryStateProvider::class) state: ReactionSummaryState +) = ElementPreview { + SheetContent(summary = state.target as ReactionSummaryState.Summary) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt index 6bc5df1e79..683b7515b9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt @@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline.factories.event import io.element.android.features.messages.impl.timeline.groups.canBeDisplayedInBubbleBlock import io.element.android.features.messages.impl.timeline.model.AggregatedReaction +import io.element.android.features.messages.impl.timeline.model.AggregatedReactionSender import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions @@ -90,14 +91,34 @@ class TimelineItemEventFactory @Inject constructor( } private fun MatrixTimelineItem.Event.computeReactionsState(): TimelineItemReactions { - val aggregatedReactions = event.reactions.map { + val timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT) + var aggregatedReactions = event.reactions.map { reaction -> + // Sort reactions within an aggregation by timestamp descending. + // This puts the most recent at the top, useful in cases like the + // reaction summary view or getting the most recent reaction. AggregatedReaction( - key = it.key, - count = it.count.toInt(), - isHighlighted = it.senderIds.contains(matrixClient.sessionId), + key = reaction.key, + currentUserId = matrixClient.sessionId, + senders = reaction.senders + .sortedByDescending{ it.timestamp } + .map { + val date = Date(it.timestamp) + AggregatedReactionSender( + senderId = it.senderId, + timestamp = date, + sentTime = timeFormatter.format(date), + ) + } ) } - aggregatedReactions.sortedByDescending { it.count } + // Sort aggregated reactions by count and then timestamp ascending, using + // the most recent reaction in the aggregation(hence index 0). + // This appends new aggregations on the end of the reaction layout. + aggregatedReactions = aggregatedReactions + .sortedWith( + compareByDescending { it.count } + .thenBy { it.senders[0].timestamp } + ) return TimelineItemReactions(aggregatedReactions.toImmutableList()) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReaction.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReaction.kt index ba13896c06..59c52ed8cf 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReaction.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReaction.kt @@ -17,6 +17,7 @@ package io.element.android.features.messages.impl.timeline.model import io.element.android.libraries.core.extensions.ellipsize +import io.element.android.libraries.matrix.api.core.UserId /** * Length at which we ellipsize a reaction key for display @@ -27,16 +28,15 @@ import io.element.android.libraries.core.extensions.ellipsize private const val MAX_DISPLAY_CHARS = 16 /** + * @property currentUserId the ID of the currently logged in user * @property key the full reaction key (e.g. "👍", "YES!") - * @property count the number of users who reacted with this key - * @property isHighlighted true if the reaction has (also) been sent by the current user. + * @property senders the list of users who sent the reactions */ data class AggregatedReaction( + val currentUserId: UserId, val key: String, - val count: Int, - val isHighlighted: Boolean = false + val senders: List ) { - /** * The key to be displayed on screen. * @@ -45,4 +45,18 @@ data class AggregatedReaction( val displayKey: String by lazy { key.ellipsize(MAX_DISPLAY_CHARS) } + + /** + * The number of users who reacted with this key. + */ + val count: Int by lazy { + senders.count() + } + + /** + * True if the reaction has (also) been sent by the current user. + */ + val isHighlighted: Boolean by lazy { + senders.any { it.senderId.value == currentUserId.value } + } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionProvider.kt index 148f565911..dcd6bb105c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionProvider.kt @@ -17,6 +17,9 @@ package io.element.android.features.messages.impl.timeline.model import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.matrix.api.core.UserId +import java.text.DateFormat +import java.util.Date open class AggregatedReactionProvider : PreviewParameterProvider { override val values: Sequence @@ -29,11 +32,27 @@ open class AggregatedReactionProvider : PreviewParameterProvider + val timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT) + val date = Date(1_689_061_264L) + add( + AggregatedReactionSender( + senderId = if (isHighlighted && index == 0) userId else UserId("@user$index:server.org"), + timestamp = date, + sentTime = timeFormatter.format(date), + ) + ) + } + } + return AggregatedReaction( + currentUserId = userId, + key = key, + senders = senders + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionSender.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionSender.kt new file mode 100644 index 0000000000..276ee0b266 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/AggregatedReactionSender.kt @@ -0,0 +1,28 @@ +/* + * 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.model + +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.user.MatrixUser +import java.util.Date + +data class AggregatedReactionSender( + val senderId: UserId, + val timestamp: Date, + val sentTime: String, + val user: MatrixUser? = null +) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index 692d4a4e35..5fcd06a980 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -35,6 +35,7 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter import io.element.android.features.messages.impl.timeline.TimelinePresenter 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.retrysendmenu.RetrySendMenuPresenter import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent @@ -584,6 +585,7 @@ class MessagesPresenterTest { val buildMeta = aBuildMeta() val actionListPresenter = ActionListPresenter(buildMeta = buildMeta) val customReactionPresenter = CustomReactionPresenter() + val reactionSummaryPresenter = ReactionSummaryPresenter(room = matrixRoom) val retrySendMenuPresenter = RetrySendMenuPresenter(room = matrixRoom) return MessagesPresenter( room = matrixRoom, @@ -591,6 +593,7 @@ class MessagesPresenterTest { timelinePresenter = timelinePresenter, actionListPresenter = actionListPresenter, customReactionPresenter = customReactionPresenter, + reactionSummaryPresenter = reactionSummaryPresenter, retrySendMenuPresenter = retrySendMenuPresenter, networkMonitor = FakeNetworkMonitor(), snackbarDispatcher = SnackbarDispatcher(), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt index 0dfe6fd53c..345884f7bf 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt @@ -24,8 +24,12 @@ import io.element.android.features.messages.fixtures.aTimelineItemsFactory import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.TimelinePresenter import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory +import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.libraries.matrix.ui.components.aMatrixUserList import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction +import io.element.android.libraries.matrix.api.timeline.item.event.ReactionSender import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.room.FakeMatrixRoom @@ -37,6 +41,7 @@ import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test +import java.util.Date class TimelinePresenterTest { @Test @@ -188,6 +193,61 @@ class TimelinePresenterTest { } } + @Test + fun `present - reaction ordering`() = runTest { + val timeline = FakeMatrixTimeline() + val presenter = createTimelinePresenter(timeline) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.hasNewItems).isFalse() + assertThat(initialState.timelineItems.size).isEqualTo(0) + val now = Date().time + val minuteInMilis = 60 * 1000 + // Use index as a convenient value for timestamp + val (alice, bob, charlie) = aMatrixUserList().take(3).mapIndexed { i, user -> + ReactionSender(senderId = user.userId, timestamp = now + i * minuteInMilis) + } + val oneReaction = listOf( + EventReaction( + key = "❤️", + senders = listOf(alice, charlie) + ), + EventReaction( + key = "👍", + senders = listOf(alice, bob) + ), + EventReaction( + key = "🐶", + senders = listOf(charlie) + ), + ) + timeline.updateTimelineItems { + listOf(MatrixTimelineItem.Event(0, anEventTimelineItem(reactions = oneReaction))) + } + skipItems(1) + val item = awaitItem().timelineItems.first() + assertThat(item).isInstanceOf(TimelineItem.Event::class.java) + val event = item as TimelineItem.Event + val reactions = event.reactionsState.reactions + assertThat(reactions.size).isEqualTo(3) + + // Aggregated reactions are sorted by count first and then timestamp ascending(new ones tagged on the end) + assertThat(reactions[0].count).isEqualTo(2) + assertThat(reactions[0].key).isEqualTo("👍") + assertThat(reactions[0].senders[0].senderId).isEqualTo(bob.senderId) + + assertThat(reactions[1].count).isEqualTo(2) + assertThat(reactions[1].key).isEqualTo("❤️") + assertThat(reactions[1].senders[0].senderId).isEqualTo(charlie.senderId) + + assertThat(reactions[2].count).isEqualTo(1) + assertThat(reactions[2].key).isEqualTo("🐶") + assertThat(reactions[2].senders[0].senderId).isEqualTo(charlie.senderId) + } + } + private fun TestScope.createTimelinePresenter( timeline: MatrixTimeline = FakeMatrixTimeline(), timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt new file mode 100644 index 0000000000..0170878cb5 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/reactionsummary/ReactionSummaryPresenterTests.kt @@ -0,0 +1,80 @@ +/* + * 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.timeline.components.reactionsummary + +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.components.reactionsummary.ReactionSummaryEvents +import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter +import io.element.android.features.messages.impl.timeline.model.anAggregatedReaction +import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState +import io.element.android.libraries.matrix.test.AN_AVATAR_URL +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.A_USER_NAME +import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomMember +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class ReactionSummaryPresenterTests { + private val aggregatedReaction = anAggregatedReaction(userId = A_USER_ID, key = "👍", isHighlighted = true) + private val roomMember = aRoomMember(userId = A_USER_ID, avatarUrl = AN_AVATAR_URL, displayName = A_USER_NAME) + private val summaryEvent = ReactionSummaryEvents.ShowReactionSummary(AN_EVENT_ID, listOf(aggregatedReaction), aggregatedReaction.key) + private val room = FakeMatrixRoom().apply { + givenRoomMembersState(MatrixRoomMembersState.Ready(listOf(roomMember))) + } + private val presenter = ReactionSummaryPresenter(room) + + @Test + fun `present - handle showing and hiding the reaction summary`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.target).isEqualTo(null) + + initialState.eventSink(summaryEvent) + assertThat(awaitItem().target).isNotNull() + + initialState.eventSink(ReactionSummaryEvents.Clear) + assertThat(awaitItem().target).isNull() + } + } + + @Test + fun `present - handle reaction summary content and avatars populated`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.target).isEqualTo(null) + + initialState.eventSink(summaryEvent) + val reactions = awaitItem().target?.reactions + assertThat(reactions?.count()).isEqualTo(1) + assertThat(reactions?.first()?.key).isEqualTo("👍") + assertThat(reactions?.first()?.senders?.first()?.senderId).isEqualTo(A_USER_ID) + assertThat(reactions?.first()?.senders?.first()?.user?.userId).isEqualTo(A_USER_ID) + assertThat(reactions?.first()?.senders?.first()?.user?.avatarUrl).isEqualTo(AN_AVATAR_URL) + assertThat(reactions?.first()?.senders?.first()?.user?.displayName).isEqualTo(A_USER_NAME) + } + } + +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/model/AggregatedReactionTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/model/AggregatedReactionTest.kt index 0e1ccbd003..ce107f76aa 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/model/AggregatedReactionTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/model/AggregatedReactionTest.kt @@ -16,19 +16,30 @@ package io.element.android.features.messages.timeline.model -import io.element.android.features.messages.impl.timeline.model.AggregatedReaction +import io.element.android.features.messages.impl.timeline.model.anAggregatedReaction import org.junit.Assert.assertEquals import org.junit.Test class AggregatedReactionTest { @Test fun `reaction display key is shortened`() { - val reaction = AggregatedReaction( - key = "1234567890123456790", - count = 1, - isHighlighted = false + val reaction = anAggregatedReaction( + key = "1234567890123456790", + count = 1 ) assertEquals("1234567890123456…", reaction.displayKey) } + + @Test + fun `reaction count and isHighlighted are computed correctly`() { + val reaction = anAggregatedReaction( + key = "1234567890123456790", + count = 3, + isHighlighted = true + ) + + assertEquals(3, reaction.count) + assertEquals(true, reaction.isHighlighted) + } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventReaction.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventReaction.kt index 8bea4b5330..a2e68d17d2 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventReaction.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventReaction.kt @@ -16,10 +16,7 @@ package io.element.android.libraries.matrix.api.timeline.item.event -import io.element.android.libraries.matrix.api.core.UserId - data class EventReaction( val key: String, - val count: Long, - val senderIds: List + val senders: List ) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ReactionSender.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ReactionSender.kt new file mode 100644 index 0000000000..60398cffd5 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/ReactionSender.kt @@ -0,0 +1,31 @@ +/* + * 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.libraries.matrix.api.timeline.item.event + +import io.element.android.libraries.matrix.api.core.UserId + +/** + * The sender of a reaction. + * + * @property senderId the ID of the user who sent the reaction + * @property timestamp the timestamp the reaction was received on the origin homeserver + */ +data class ReactionSender( + val senderId: UserId, + val timestamp: Long +) + diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt index 359b9ecdef..21e7d51638 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt @@ -25,6 +25,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails +import io.element.android.libraries.matrix.api.timeline.item.event.ReactionSender import org.matrix.rustcomponents.sdk.Reaction import org.matrix.rustcomponents.sdk.EventItemOrigin as RustEventItemOrigin import org.matrix.rustcomponents.sdk.EventSendState as RustEventSendState @@ -81,8 +82,12 @@ private fun List?.map(): List { return this?.map { EventReaction( key = it.key, - count = it.count.toLong(), - senderIds = it.senders.map { sender -> UserId(sender.senderId) } + senders = it.senders.map { sender -> + ReactionSender( + senderId = UserId(sender.senderId), + timestamp = sender.timestamp.toLong() + ) + } ) } ?: emptyList() } diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-12_13_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-12_13_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c63cf7083a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-12_13_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e63f683ef130d77b55966e954a7cbc144dceb5960056bcd01d7c0c9583b3b03 +size 25358 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-12_14_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-12_14_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..495f0b09a0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-12_14_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5fd8aeb2e875a1280a70ba913dd913759af66fb4300ed69c7e109ec8e8378a09 +size 25051 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-13_14_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-12_13_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-13_14_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-13_15_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-12_14_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-13_15_null,NEXUS_5,1.0,en].png From 89b1bba96e7b74bc165e10dd4e4947b713eb3654 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 31 Jul 2023 21:05:28 +0200 Subject: [PATCH 215/251] CI improvements (#1012) --- .github/workflows/build.yml | 19 ++++--- .github/workflows/danger.yml | 1 + .../workflows/gradle-wrapper-validation.yml | 1 + .github/workflows/quality.yml | 2 + .github/workflows/sonar.yml | 51 +++++++++++++++++++ .github/workflows/tests.yml | 1 + .github/workflows/validate-lfs.yml | 1 + 7 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/sonar.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b06e3f1622..74eea2e7d3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,17 +14,19 @@ env: jobs: debug: - name: Build debug APKs + name: Build APKs runs-on: ubuntu-latest + # Skip for `main` and the merge queue if the branch is up to date with `develop` if: github.ref != 'refs/heads/main' && github.event.merge_group.base_ref != 'refs/heads/develop' strategy: + matrix: + variant: [debug, release, nightly, samples] fail-fast: false # Allow all jobs on develop. Just one per PR. concurrency: - group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-{0}', github.sha) || format('build-debug-{0}', github.ref) }} + group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-{1}', github.sha) || format('build-{0}-{1}', matrix.variant, github.ref) }} cancel-in-progress: true steps: - - run: echo ${{ github.event.merge_group.base_ref }} - uses: actions/checkout@v3 with: # Ensure we are building the branch and not the branch after being merged on develop @@ -40,12 +42,14 @@ jobs: with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Assemble debug APK + if: ${{ matrix.variant == 'debug' }} env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} run: ./gradlew assembleDebug -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES - - name: Upload debug APKs + - name: Upload APK APKs + if: ${{ matrix.variant == 'debug' }} uses: actions/upload-artifact@v3 with: name: elementx-debug @@ -57,12 +61,12 @@ jobs: continue-on-error: true env: token: ${{ secrets.DIAWI_TOKEN }} - if: ${{ github.event_name == 'pull_request' && env.token != '' }} + if: ${{ matrix.variant == 'debug' && github.event_name == 'pull_request' && env.token != '' }} with: token: ${{ env.token }} file: app/build/outputs/apk/debug/app-arm64-v8a-debug.apk - name: Add or update PR comment with QR Code to download APK. - if: ${{ github.event_name == 'pull_request' && steps.diawi.conclusion == 'success' }} + if: ${{ matrix.variant == 'debug' && github.event_name == 'pull_request' && steps.diawi.conclusion == 'success' }} uses: NejcZdovc/comment-pr@v2 with: message: | @@ -74,8 +78,11 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Compile release sources + if: ${{ matrix.variant == 'release' }} run: ./gradlew compileReleaseSources -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES - name: Compile nightly sources + if: ${{ matrix.variant == 'nightly' }} run: ./gradlew compileNightlySources -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES - name: Compile samples minimal + if: ${{ matrix.variant == 'samples' }} run: ./gradlew :samples:minimal:assemble $CI_GRADLE_ARG_PROPERTIES diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml index 96e9d945b7..8186f0370c 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -5,6 +5,7 @@ on: [pull_request, merge_group] jobs: build: runs-on: ubuntu-latest + # Don't run in the merge queue again if the branch is up to date with `develop` if: github.event.merge_group.base_ref != 'refs/heads/develop' name: Danger main check steps: diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index 55d9d45d6d..c1e478b15c 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -8,6 +8,7 @@ on: jobs: validation: name: "Validation" + # Don't run in the merge queue again if the branch is up to date with `develop` if: github.event.merge_group.base_ref != 'refs/heads/develop' runs-on: ubuntu-latest # No concurrency required, this is a prerequisite to other actions and should run every time. diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index f62b393e45..1cc49d5503 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -16,6 +16,7 @@ jobs: checkScript: name: Search for forbidden patterns runs-on: ubuntu-latest + # Don't run in the merge queue again if the branch is up to date with `develop` if: github.event.merge_group.base_ref != 'refs/heads/develop' steps: - uses: actions/checkout@v3 @@ -25,6 +26,7 @@ jobs: check: name: Project Check Suite runs-on: ubuntu-latest + # Don't run in the merge queue again if the branch is up to date with `develop` if: github.event.merge_group.base_ref != 'refs/heads/develop' # Allow all jobs on main and develop. Just one per PR. concurrency: diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml new file mode 100644 index 0000000000..24e5b5ad9b --- /dev/null +++ b/.github/workflows/sonar.yml @@ -0,0 +1,51 @@ +name: Code Quality Checks + +on: + workflow_dispatch: + pull_request: + merge_group: + push: + branches: [ main, develop ] + +# Enrich gradle.properties for CI/CD +env: + GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx3072m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -XX:MaxMetaspaceSize=512m -Dkotlin.daemon.jvm.options="-Xmx2g" -Dkotlin.incremental=false + CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 2 --no-daemon --warn + +jobs: + sonar: + name: Project Check Suite + runs-on: ubuntu-latest + # Don't run in the merge queue again if the branch is up to date with `develop` + if: github.event.merge_group.base_ref != 'refs/heads/develop' + # Allow all jobs on main and develop. Just one per PR. + concurrency: + group: ${{ github.ref == 'refs/heads/main' && format('sonar-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('sonar-develop-{0}', github.sha) || format('sonar-{0}', github.ref) }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v3 + with: + # Ensure we are building the branch and not the branch after being merged on develop + # https://github.com/actions/checkout/issues/881 + ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} + - name: Use JDK 17 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' # See 'Supported distributions' for available options + java-version: '17' + - name: Configure gradle + uses: gradle/gradle-build-action@v2.7.0 + with: + cache-read-only: ${{ github.ref != 'refs/heads/develop' }} + - name: 🔊 Publish results to Sonar + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + ORG_GRADLE_PROJECT_SONAR_LOGIN: ${{ secrets.SONAR_TOKEN }} + if: ${{ always() && env.SONAR_TOKEN != '' && env.ORG_GRADLE_PROJECT_SONAR_LOGIN != '' }} + run: ./gradlew sonar $CI_GRADLE_ARG_PROPERTIES + - name: Prepare Danger + if: always() + run: | + npm install --save-dev @babel/core + npm install --save-dev @babel/plugin-transform-flow-strip-types + yarn add danger-plugin-lint-report --dev diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 82e0708e64..97a739f747 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,6 +16,7 @@ jobs: tests: name: Runs unit tests runs-on: ubuntu-latest + # Don't run in the merge queue again if the branch is up to date with `develop` if: github.event.merge_group.base_ref != 'refs/heads/develop' # Allow all jobs on main and develop. Just one per PR. diff --git a/.github/workflows/validate-lfs.yml b/.github/workflows/validate-lfs.yml index e7f5c04d9e..63ded8f4e1 100644 --- a/.github/workflows/validate-lfs.yml +++ b/.github/workflows/validate-lfs.yml @@ -5,6 +5,7 @@ on: [pull_request, merge_group] jobs: build: runs-on: ubuntu-latest + # Don't run in the merge queue again if the branch is up to date with `develop` if: github.event.merge_group.base_ref != 'refs/heads/develop' name: Validate steps: From e453b984ef64835cf833e08c353b6caec27893a8 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 1 Aug 2023 10:53:41 +0200 Subject: [PATCH 216/251] RoomList: use same logic than Timeline for caching built items. (#1013) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * RoomList: use same logic than Timeline for caching built items. Extract into reusable components. * RoomList: fix tests * Fix `DiffCacheUpdater` docs --------- Co-authored-by: ganfra Co-authored-by: Jorge Martín --- features/messages/impl/build.gradle.kts | 1 - .../impl/timeline/diff/CacheInvalidator.kt | 56 ---------- .../diff/TimelineItemsCacheInvalidator.kt | 63 +++++++++++ .../factories/TimelineItemsFactory.kt | 47 ++++---- .../impl/datasource/RoomListDataSource.kt | 100 ++++++++++++------ .../roomlist/impl/RoomListPresenterTests.kt | 26 +++-- libraries/androidutils/build.gradle.kts | 1 + .../androidutils/diff/DefaultDiffCallback.kt | 21 ++-- .../libraries/androidutils/diff/DiffCache.kt | 67 ++++++++++++ .../androidutils/diff/DiffCacheInvalidator.kt | 63 +++++++++++ .../androidutils/diff/DiffCacheUpdater.kt | 70 ++++++++++++ 11 files changed, 373 insertions(+), 142 deletions(-) delete mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/CacheInvalidator.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/TimelineItemsCacheInvalidator.kt rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/MatrixTimelineItemsDiffCallback.kt => libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DefaultDiffCallback.kt (70%) create mode 100644 libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCache.kt create mode 100644 libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheInvalidator.kt create mode 100644 libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheUpdater.kt diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index b5edaec78e..948bac4c57 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -52,7 +52,6 @@ dependencies { implementation(libs.coil.compose) implementation(libs.datetime) implementation(libs.accompanist.flowlayout) - implementation(libs.androidx.recyclerview) implementation(libs.jsoup) implementation(libs.androidx.constraintlayout) implementation(libs.androidx.constraintlayout.compose) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/CacheInvalidator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/CacheInvalidator.kt deleted file mode 100644 index 9aa3ab5e02..0000000000 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/CacheInvalidator.kt +++ /dev/null @@ -1,56 +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.diff - -import androidx.recyclerview.widget.ListUpdateCallback -import io.element.android.features.messages.impl.timeline.model.TimelineItem -import io.element.android.features.messages.impl.timeline.util.invalidateLast -import timber.log.Timber - -internal class CacheInvalidator(private val itemStatesCache: MutableList) : - ListUpdateCallback { - - override fun onChanged(position: Int, count: Int, payload: Any?) { - Timber.d("onChanged(position= $position, count= $count)") - (position until position + count).forEach { - // Invalidate cache - itemStatesCache[it] = null - } - } - - override fun onMoved(fromPosition: Int, toPosition: Int) { - Timber.d("onMoved(fromPosition= $fromPosition, toPosition= $toPosition)") - val model = itemStatesCache.removeAt(fromPosition) - itemStatesCache.add(toPosition, model) - } - - override fun onInserted(position: Int, count: Int) { - Timber.d("onInserted(position= $position, count= $count)") - itemStatesCache.invalidateLast() - repeat(count) { - itemStatesCache.add(position, null) - } - } - - override fun onRemoved(position: Int, count: Int) { - Timber.d("onRemoved(position= $position, count= $count)") - itemStatesCache.invalidateLast() - repeat(count) { - itemStatesCache.removeAt(position) - } - } -} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/TimelineItemsCacheInvalidator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/TimelineItemsCacheInvalidator.kt new file mode 100644 index 0000000000..a7a3bea00e --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/TimelineItemsCacheInvalidator.kt @@ -0,0 +1,63 @@ +/* + * 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.diff + +import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.libraries.androidutils.diff.DefaultDiffCacheInvalidator +import io.element.android.libraries.androidutils.diff.DiffCacheInvalidator +import io.element.android.libraries.androidutils.diff.MutableDiffCache + +/** + * [DiffCacheInvalidator] implementation for [TimelineItem]. + * It uses [DefaultDiffCacheInvalidator] and invalidate the cache around the updated item so that those items are computed again. + * This is needed because a timeline item is computed based on the previous and next items. + */ +internal class TimelineItemsCacheInvalidator : DiffCacheInvalidator { + + private val delegate = DefaultDiffCacheInvalidator() + + override fun onChanged(position: Int, count: Int, cache: MutableDiffCache) { + delegate.onChanged(position, count, cache) + } + + override fun onMoved(fromPosition: Int, toPosition: Int, cache: MutableDiffCache) { + delegate.onMoved(fromPosition, toPosition, cache) + } + + override fun onInserted(position: Int, count: Int, cache: MutableDiffCache) { + cache.invalidateAround(position) + delegate.onInserted(position, count, cache) + } + + override fun onRemoved(position: Int, count: Int, cache: MutableDiffCache) { + cache.invalidateAround(position) + delegate.onRemoved(position, count, cache) + } +} + +/** + * Invalidate the cache around the given position. + * It invalidates the previous and next items. + */ +private fun MutableDiffCache<*>.invalidateAround(position: Int) { + if (position > 0) { + set(position - 1, null) + } + if (position < indices().last) { + set(position + 1, null) + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt index aa9786c945..d664d3f26c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt @@ -19,13 +19,13 @@ package io.element.android.features.messages.impl.timeline.factories import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState -import androidx.recyclerview.widget.DiffUtil -import io.element.android.features.messages.impl.timeline.diff.CacheInvalidator -import io.element.android.features.messages.impl.timeline.diff.MatrixTimelineItemsDiffCallback +import io.element.android.features.messages.impl.timeline.diff.TimelineItemsCacheInvalidator import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemEventFactory import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.libraries.androidutils.diff.DiffCacheUpdater +import io.element.android.libraries.androidutils.diff.MutableListDiffCache import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import kotlinx.collections.immutable.ImmutableList @@ -35,9 +35,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext -import timber.log.Timber import javax.inject.Inject -import kotlin.system.measureTimeMillis class TimelineItemsFactory @Inject constructor( private val dispatchers: CoroutineDispatchers, @@ -46,13 +44,20 @@ class TimelineItemsFactory @Inject constructor( private val timelineItemGrouper: TimelineItemGrouper, ) { private val timelineItems = MutableStateFlow(persistentListOf()) - private val timelineItemsCache = arrayListOf() - - // Items from rust sdk, used for diffing - private var matrixTimelineItems: List = emptyList() private val lock = Mutex() - private val cacheInvalidator = CacheInvalidator(timelineItemsCache) + private val diffCache = MutableListDiffCache() + private val diffCacheUpdater = DiffCacheUpdater( + diffCache = diffCache, + detectMoves = false, + cacheInvalidator = TimelineItemsCacheInvalidator() + ) { old, new -> + if (old is MatrixTimelineItem.Event && new is MatrixTimelineItem.Event) { + old.uniqueId == new.uniqueId + } else { + false + } + } @Composable fun collectItemsAsState(): State> { @@ -63,15 +68,15 @@ class TimelineItemsFactory @Inject constructor( timelineItems: List, ) = withContext(dispatchers.computation) { lock.withLock { - calculateAndApplyDiff(timelineItems) + diffCacheUpdater.updateWith(timelineItems) buildAndEmitTimelineItemStates(timelineItems) } } private suspend fun buildAndEmitTimelineItemStates(timelineItems: List) { val newTimelineItemStates = ArrayList() - for (index in timelineItemsCache.indices.reversed()) { - val cacheItem = timelineItemsCache[index] + for (index in diffCache.indices().reversed()) { + val cacheItem = diffCache.get(index) if (cacheItem == null) { buildAndCacheItem(timelineItems, index)?.also { timelineItemState -> newTimelineItemStates.add(timelineItemState) @@ -84,20 +89,6 @@ class TimelineItemsFactory @Inject constructor( this.timelineItems.emit(result) } - private fun calculateAndApplyDiff(newTimelineItems: List) { - val timeToDiff = measureTimeMillis { - val diffCallback = - MatrixTimelineItemsDiffCallback( - oldList = matrixTimelineItems, - newList = newTimelineItems - ) - val diffResult = DiffUtil.calculateDiff(diffCallback, false) - matrixTimelineItems = newTimelineItems - diffResult.dispatchUpdatesTo(cacheInvalidator) - } - Timber.v("Time to apply diff on new list of ${newTimelineItems.size} items: $timeToDiff ms") - } - private fun buildAndCacheItem( timelineItems: List, index: Int @@ -108,7 +99,7 @@ class TimelineItemsFactory @Inject constructor( is MatrixTimelineItem.Virtual -> virtualItemFactory.create(currentTimelineItem) MatrixTimelineItem.Other -> null } - timelineItemsCache[index] = timelineItemState + diffCache[index] = timelineItemState return timelineItemState } } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt index 4eb7bc9d72..714f6e2e11 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt @@ -18,6 +18,8 @@ package io.element.android.features.roomlist.impl.datasource import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.features.roomlist.impl.model.RoomListRoomSummaryPlaceholders +import io.element.android.libraries.androidutils.diff.DiffCacheUpdater +import io.element.android.libraries.androidutils.diff.MutableListDiffCache import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.extensions.orEmpty import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter @@ -36,6 +38,8 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import javax.inject.Inject @@ -50,15 +54,17 @@ class RoomListDataSource @Inject constructor( private val _allRooms = MutableStateFlow>(persistentListOf()) private val _filteredRooms = MutableStateFlow>(persistentListOf()) + private val lock = Mutex() + private val diffCache = MutableListDiffCache() + private val diffCacheUpdater = DiffCacheUpdater(diffCache = diffCache, detectMoves = true) { old, new -> + old?.identifier() == new?.identifier() + } + fun launchIn(coroutineScope: CoroutineScope) { roomSummaryDataSource .allRooms() .onEach { roomSummaries -> - _allRooms.value = if (roomSummaries.isEmpty()) { - RoomListRoomSummaryPlaceholders.createFakeList(16) - } else { - mapRoomSummaries(roomSummaries) - }.toImmutableList() + replaceWith(roomSummaries) } .launchIn(coroutineScope) @@ -85,33 +91,63 @@ class RoomListDataSource @Inject constructor( val allRooms: StateFlow> = _allRooms val filteredRooms: StateFlow> = _filteredRooms - private suspend fun mapRoomSummaries( - roomSummaries: List - ): List = withContext(coroutineDispatchers.computation) { - roomSummaries.map { roomSummary -> - when (roomSummary) { - is RoomSummary.Empty -> RoomListRoomSummaryPlaceholders.create(roomSummary.identifier) - is RoomSummary.Filled -> { - val avatarData = AvatarData( - id = roomSummary.identifier(), - name = roomSummary.details.name, - url = roomSummary.details.avatarURLString, - size = AvatarSize.RoomListItem, - ) - val roomIdentifier = roomSummary.identifier() - RoomListRoomSummary( - id = roomSummary.identifier(), - roomId = RoomId(roomIdentifier), - name = roomSummary.details.name, - hasUnread = roomSummary.details.unreadNotificationCount > 0, - timestamp = lastMessageTimestampFormatter.format(roomSummary.details.lastMessageTimestamp), - lastMessage = roomSummary.details.lastMessage?.let { message -> - roomLastMessageFormatter.format(message.event, roomSummary.details.isDirect) - }.orEmpty(), - avatarData = avatarData, - ) - } - } + private suspend fun replaceWith(roomSummaries: List) = withContext(coroutineDispatchers.computation) { + lock.withLock { + diffCacheUpdater.updateWith(roomSummaries) + buildAndEmitAllRooms(roomSummaries) } } + + private suspend fun buildAndEmitAllRooms(roomSummaries: List) { + if (diffCache.isEmpty()) { + _allRooms.emit( + RoomListRoomSummaryPlaceholders.createFakeList(16).toImmutableList() + ) + } else { + val roomListRoomSummaries = ArrayList() + for (index in diffCache.indices()) { + val cacheItem = diffCache.get(index) + if (cacheItem == null) { + buildAndCacheItem(roomSummaries, index)?.also { timelineItemState -> + roomListRoomSummaries.add(timelineItemState) + } + } else { + roomListRoomSummaries.add(cacheItem) + } + } + _allRooms.emit(roomListRoomSummaries.toImmutableList()) + } + } + + private fun buildAndCacheItem( + roomSummaries: List, + index: Int + ): RoomListRoomSummary? { + val roomListRoomSummary = when (val roomSummary = roomSummaries.getOrNull(index)) { + is RoomSummary.Empty -> RoomListRoomSummaryPlaceholders.create(roomSummary.identifier) + is RoomSummary.Filled -> { + val avatarData = AvatarData( + id = roomSummary.identifier(), + name = roomSummary.details.name, + url = roomSummary.details.avatarURLString, + size = AvatarSize.RoomListItem, + ) + val roomIdentifier = roomSummary.identifier() + RoomListRoomSummary( + id = roomSummary.identifier(), + roomId = RoomId(roomIdentifier), + name = roomSummary.details.name, + hasUnread = roomSummary.details.unreadNotificationCount > 0, + timestamp = lastMessageTimestampFormatter.format(roomSummary.details.lastMessageTimestamp), + lastMessage = roomSummary.details.lastMessage?.let { message -> + roomLastMessageFormatter.format(message.event, roomSummary.details.isDirect) + }.orEmpty(), + avatarData = avatarData, + ) + } + null -> null + } + diffCache[index] = roomListRoomSummary + return roomListRoomSummary + } } diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index ce8e83c70d..d2d52aecc8 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -50,6 +50,7 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService +import io.element.android.tests.testutils.consumeItemsUntilPredicate import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.TestScope @@ -118,13 +119,12 @@ class RoomListPresenterTests { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - skipItems(1) - val initialState = awaitItem() + val initialState = consumeItemsUntilPredicate { state -> state.roomList.size == 16 }.last() // Room list is loaded with 16 placeholders Truth.assertThat(initialState.roomList.size).isEqualTo(16) Truth.assertThat(initialState.roomList.all { it.isPlaceholder }).isTrue() roomSummaryDataSource.postAllRooms(listOf(aRoomSummaryFilled())) - val withRoomState = awaitItem() + val withRoomState = consumeItemsUntilPredicate { state -> state.roomList.size == 1 }.last() Truth.assertThat(withRoomState.roomList.size).isEqualTo(1) Truth.assertThat(withRoomState.roomList.first()) .isEqualTo(aRoomListRoomSummary) @@ -142,21 +142,19 @@ class RoomListPresenterTests { presenter.present() }.test { roomSummaryDataSource.postAllRooms(listOf(aRoomSummaryFilled())) - skipItems(1) - val loadedState = awaitItem() + val loadedState = consumeItemsUntilPredicate { state -> state.roomList.size == 1 }.last() // Test filtering with result loadedState.eventSink.invoke(RoomListEvents.UpdateFilter(A_ROOM_NAME.substring(0, 3))) - skipItems(1) // Filter update - val withNotFilteredRoomState = awaitItem() - Truth.assertThat(withNotFilteredRoomState.filter).isEqualTo(A_ROOM_NAME.substring(0, 3)) - Truth.assertThat(withNotFilteredRoomState.filteredRoomList.size).isEqualTo(1) - Truth.assertThat(withNotFilteredRoomState.filteredRoomList.first()) + val withFilteredRoomState = consumeItemsUntilPredicate { state -> state.filteredRoomList.size == 1 }.last() + Truth.assertThat(withFilteredRoomState.filter).isEqualTo(A_ROOM_NAME.substring(0, 3)) + Truth.assertThat(withFilteredRoomState.filteredRoomList.size).isEqualTo(1) + Truth.assertThat(withFilteredRoomState.filteredRoomList.first()) .isEqualTo(aRoomListRoomSummary) // Test filtering without result - withNotFilteredRoomState.eventSink.invoke(RoomListEvents.UpdateFilter("tada")) - skipItems(1) // Filter update - Truth.assertThat(awaitItem().filter).isEqualTo("tada") - Truth.assertThat(awaitItem().filteredRoomList).isEmpty() + withFilteredRoomState.eventSink.invoke(RoomListEvents.UpdateFilter("tada")) + val withNotFilteredRoomState = consumeItemsUntilPredicate { state -> state.filteredRoomList.size == 0 }.last() + Truth.assertThat(withNotFilteredRoomState.filter).isEqualTo("tada") + Truth.assertThat(withNotFilteredRoomState.filteredRoomList).isEmpty() } } diff --git a/libraries/androidutils/build.gradle.kts b/libraries/androidutils/build.gradle.kts index 92e3c46126..57e4ca3569 100644 --- a/libraries/androidutils/build.gradle.kts +++ b/libraries/androidutils/build.gradle.kts @@ -37,6 +37,7 @@ dependencies { implementation(libs.timber) implementation(libs.androidx.corektx) implementation(libs.androidx.activity.activity) + implementation(libs.androidx.recyclerview) implementation(libs.androidx.exifinterface) implementation(libs.androidx.security.crypto) implementation(libs.androidx.browser) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/MatrixTimelineItemsDiffCallback.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DefaultDiffCallback.kt similarity index 70% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/MatrixTimelineItemsDiffCallback.kt rename to libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DefaultDiffCallback.kt index 4a78447bd7..219441d5e6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/diff/MatrixTimelineItemsDiffCallback.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DefaultDiffCallback.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * 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. @@ -14,14 +14,17 @@ * limitations under the License. */ -package io.element.android.features.messages.impl.timeline.diff +package io.element.android.libraries.androidutils.diff import androidx.recyclerview.widget.DiffUtil -import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem -internal class MatrixTimelineItemsDiffCallback( - private val oldList: List, - private val newList: List +/** + * Default implementation of [DiffUtil.Callback] that uses [areItemsTheSame] to compare items. + */ +internal class DefaultDiffCallback( + private val oldList: List, + private val newList: List, + private val areItemsTheSame: (oldItem: T?, newItem: T?) -> Boolean, ) : DiffUtil.Callback() { override fun getOldListSize(): Int { @@ -35,11 +38,7 @@ internal class MatrixTimelineItemsDiffCallback( override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { val oldItem = oldList.getOrNull(oldItemPosition) val newItem = newList.getOrNull(newItemPosition) - return if (oldItem is MatrixTimelineItem.Event && newItem is MatrixTimelineItem.Event) { - oldItem.uniqueId == newItem.uniqueId - } else { - false - } + return areItemsTheSame(oldItem, newItem) } override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCache.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCache.kt new file mode 100644 index 0000000000..3d1161e2e0 --- /dev/null +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCache.kt @@ -0,0 +1,67 @@ +/* + * 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.libraries.androidutils.diff + +/** + * A cache that can be used to store some data that can be invalidated when a diff is applied. + * The cache is invalidated by the [DiffCacheInvalidator]. + */ +interface DiffCache { + fun get(index: Int): E? + fun indices(): IntRange + fun isEmpty(): Boolean +} + +/** + * A [DiffCache] that can be mutated by adding, removing or updating elements. + */ +interface MutableDiffCache : DiffCache { + fun removeAt(index: Int): E? + fun add(index: Int, element: E?) + operator fun set(index: Int, element: E?) +} + +/** + * A [MutableDiffCache] backed by a [MutableList]. + * + */ +class MutableListDiffCache(private val mutableList: MutableList = ArrayList()) : MutableDiffCache { + + override fun removeAt(index: Int): E? { + return mutableList.removeAt(index) + } + + override fun get(index: Int): E? { + return mutableList.getOrNull(index) + } + + override fun indices(): IntRange { + return mutableList.indices + } + + override fun isEmpty(): Boolean { + return mutableList.isEmpty() + } + + override operator fun set(index: Int, element: E?) { + mutableList[index] = element + } + + override fun add(index: Int, element: E?) { + mutableList.add(index, element) + } +} diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheInvalidator.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheInvalidator.kt new file mode 100644 index 0000000000..d9f378c8fa --- /dev/null +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheInvalidator.kt @@ -0,0 +1,63 @@ +/* + * 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.libraries.androidutils.diff + +/** + * [DiffCacheInvalidator] is used to invalidate the cache when the list is updated. + * It is used by [DiffCacheUpdater]. + * Check the default implementation [DefaultDiffCacheInvalidator]. + */ +interface DiffCacheInvalidator { + fun onChanged(position: Int, count: Int, cache: MutableDiffCache) + + fun onMoved(fromPosition: Int, toPosition: Int, cache: MutableDiffCache) + + fun onInserted(position: Int, count: Int, cache: MutableDiffCache) + + fun onRemoved(position: Int, count: Int, cache: MutableDiffCache) +} + +/** + * Default implementation of [DiffCacheInvalidator]. + * It invalidates the cache by setting values to null. + */ +class DefaultDiffCacheInvalidator : DiffCacheInvalidator { + + override fun onChanged(position: Int, count: Int, cache: MutableDiffCache) { + (position until position + count).forEach { + // Invalidate cache + cache[it] = null + } + } + + override fun onMoved(fromPosition: Int, toPosition: Int, cache: MutableDiffCache) { + val model = cache.removeAt(fromPosition) + cache.add(toPosition, model) + } + + override fun onInserted(position: Int, count: Int, cache: MutableDiffCache) { + repeat(count) { + cache.add(position, null) + } + } + + override fun onRemoved(position: Int, count: Int, cache: MutableDiffCache) { + repeat(count) { + cache.removeAt(position) + } + } +} diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheUpdater.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheUpdater.kt new file mode 100644 index 0000000000..500edcb135 --- /dev/null +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheUpdater.kt @@ -0,0 +1,70 @@ +/* + * 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.libraries.androidutils.diff + +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListUpdateCallback +import timber.log.Timber +import kotlin.system.measureTimeMillis + +/** + * Class in charge of updating a [MutableDiffCache] according to the cache invalidation rules provided by the [DiffCacheInvalidator]. + * @param ListItem the type of the items in the list + * @param CachedItem the type of the items in the cache + * @param diffCache the cache to update + * @param detectMoves true if DiffUtil should try to detect moved items, false otherwise + * @param cacheInvalidator the invalidator to use to update the cache + * @param areItemsTheSame the function to use to compare items + */ +class DiffCacheUpdater( + private val diffCache: MutableDiffCache, + private val detectMoves: Boolean = false, + private val cacheInvalidator: DiffCacheInvalidator = DefaultDiffCacheInvalidator(), + private val areItemsTheSame: (oldItem: ListItem?, newItem: ListItem?) -> Boolean, +) { + + private val lock = Object() + private var prevOriginalList: List = emptyList() + + private val listUpdateCallback = object : ListUpdateCallback { + override fun onInserted(position: Int, count: Int) { + cacheInvalidator.onInserted(position, count, diffCache) + } + + override fun onRemoved(position: Int, count: Int) { + cacheInvalidator.onRemoved(position, count, diffCache) + } + + override fun onMoved(fromPosition: Int, toPosition: Int) { + cacheInvalidator.onMoved(fromPosition, toPosition, diffCache) + } + + override fun onChanged(position: Int, count: Int, payload: Any?) { + cacheInvalidator.onChanged(position, count, diffCache) + } + } + + fun updateWith(newOriginalList: List) = synchronized(lock) { + val timeToDiff = measureTimeMillis { + val diffCallback = DefaultDiffCallback(prevOriginalList, newOriginalList, areItemsTheSame) + val diffResult = DiffUtil.calculateDiff(diffCallback, detectMoves) + prevOriginalList = newOriginalList + diffResult.dispatchUpdatesTo(listUpdateCallback) + } + Timber.v("Time to apply diff on new list of ${newOriginalList.size} items: $timeToDiff ms") + } +} From acd26235986982bf14bc131992438799fb9d9e7e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 1 Aug 2023 12:29:36 +0200 Subject: [PATCH 217/251] Update dependency org.matrix.rustcomponents:sdk-android to v0.1.38 (#1015) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependency org.matrix.rustcomponents:sdk-android to v0.1.38 * Fix API breaks * Use `roomListItem.avatarUrl` if possible --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jorge Martín --- gradle/libs.versions.toml | 2 +- .../impl/notification/NotificationMapper.kt | 17 +++++++++++------ .../notification/RustNotificationService.kt | 2 +- .../TimelineEventToNotificationContentMapper.kt | 7 +++---- .../matrix/impl/room/RustMatrixRoom.kt | 2 +- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a415dac53b..71584b9c5b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -145,7 +145,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" } timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.37" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.38" sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" } sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt index a5db095035..6f1c3b494b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt @@ -20,6 +20,7 @@ import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.notification.NotificationContent import io.element.android.libraries.matrix.api.notification.NotificationData import io.element.android.libraries.matrix.api.room.RoomMembershipState @@ -39,6 +40,8 @@ class NotificationMapper( roomId: RoomId, notificationItem: NotificationItem ): NotificationData { + notificationItem.event.use { (it as NotificationEvent.Timeline).event } + val senderId = UserId(notificationItem.senderInfo.senderId) return notificationItem.use { item -> NotificationData( eventId = eventId, @@ -50,8 +53,8 @@ class NotificationMapper( isDirect = item.roomInfo.isDirect, isEncrypted = item.roomInfo.isEncrypted.orFalse(), isNoisy = item.isNoisy.orFalse(), - timestamp = item.timestamp() ?: clock.epochMillis(), - content = item.event.use(notificationContentMapper::map), + timestamp = item.timestamp(clock), + content = item.event.use { notificationContentMapper.map(senderId, it) }, contentUrl = null, ) } @@ -63,9 +66,9 @@ class NotificationContentMapper( ) { private val timelineEventToNotificationContentMapper = TimelineEventToNotificationContentMapper() - fun map(notificationEvent: NotificationEvent): NotificationContent = + fun map(senderId: UserId, notificationEvent: NotificationEvent): NotificationContent = when (notificationEvent) { - is NotificationEvent.Timeline -> timelineEventToNotificationContentMapper.map(notificationEvent.event) + is NotificationEvent.Timeline -> timelineEventToNotificationContentMapper.map(senderId, notificationEvent.event) is NotificationEvent.Invite -> NotificationContent.StateEvent.RoomMemberContent( userId = sessionId.value, membershipState = RoomMembershipState.INVITE, @@ -73,6 +76,8 @@ class NotificationContentMapper( } } -private fun NotificationItem.timestamp(): Long? { - return (this.event as? NotificationEvent.Timeline)?.event?.timestamp()?.toLong() +private fun NotificationItem.timestamp(clock: SystemClock): Long { + // FIXME we can't get the timestamp from the notification item anymore, so we need to fake it +// return (this.event as? NotificationEvent.Timeline)?.event?.timestamp()?.toLong() + return clock.epochMillis() } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt index bb281ba4d7..3dc432ad27 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt @@ -42,7 +42,7 @@ class RustNotificationService( filterByPushRules: Boolean, ): Result = withContext(dispatchers.io) { runCatching { - val item = notificationClient.getNotificationWithSlidingSync(roomId.value, eventId.value) + val item = notificationClient.getNotification(roomId.value, eventId.value) item?.use { notificationMapper.map(eventId, roomId, it) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt index 3121c9240d..1bf11b8e93 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt @@ -22,16 +22,15 @@ import io.element.android.libraries.matrix.impl.room.RoomMemberMapper import io.element.android.libraries.matrix.impl.timeline.item.event.EventMessageMapper import org.matrix.rustcomponents.sdk.MessageLikeEventContent import org.matrix.rustcomponents.sdk.StateEventContent -import org.matrix.rustcomponents.sdk.TimelineEvent import org.matrix.rustcomponents.sdk.TimelineEventType import org.matrix.rustcomponents.sdk.use import javax.inject.Inject class TimelineEventToNotificationContentMapper @Inject constructor() { - fun map(timelineEvent: TimelineEvent): NotificationContent { - return timelineEvent.use { - it.eventType().toContent(senderId = UserId(timelineEvent.senderId())) + fun map(senderId: UserId, timelineEventType: TimelineEventType): NotificationContent { + return timelineEventType.use { + it.toContent(senderId = senderId) } } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 397aa1e5b8..5503c22b1d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -140,7 +140,7 @@ class RustMatrixRoom( override val avatarUrl: String? get() { - return innerRoom.avatarUrl() + return roomListItem.avatarUrl() ?: innerRoom.avatarUrl() } override val isEncrypted: Boolean From d79b05878abcb4ba6cc8271c70ae4da42e7bf9bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Wed, 2 Aug 2023 11:19:28 +0200 Subject: [PATCH 218/251] Fix concurrentcy group for building develop --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 74eea2e7d3..c29b1aa0e0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: fail-fast: false # Allow all jobs on develop. Just one per PR. concurrency: - group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-{1}', github.sha) || format('build-{0}-{1}', matrix.variant, github.ref) }} + group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-{0}', github.sha) || format('build-{0}-{1}', matrix.variant, github.ref) }} cancel-in-progress: true steps: - uses: actions/checkout@v3 From 414bbd2f2c506de61031fdf6f34fae673542db80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Wed, 2 Aug 2023 11:20:25 +0200 Subject: [PATCH 219/251] Fix concurrentcy group for building develop with a matrix of variants --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c29b1aa0e0..9579e81997 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: fail-fast: false # Allow all jobs on develop. Just one per PR. concurrency: - group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-{0}', github.sha) || format('build-{0}-{1}', matrix.variant, github.ref) }} + group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-{0}-{1}', matrix.variant, github.sha) || format('build-{0}-{1}', matrix.variant, github.ref) }} cancel-in-progress: true steps: - uses: actions/checkout@v3 From f8ebb5930dcbcc3946b25b78506785173538a389 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 2 Aug 2023 13:55:04 +0200 Subject: [PATCH 220/251] Fix sending read receipts when entering a room (#1016) * Fix sending read receipts when entering a room * Improve solution for sending a read receipt when opening a room * Roll back previous solution as it broke unit tests * Remove dead code --- .../features/messages/impl/timeline/TimelinePresenter.kt | 2 +- .../android/features/messages/impl/timeline/TimelineView.kt | 6 ++++-- .../features/messages/timeline/TimelinePresenterTest.kt | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 082f18e449..c53d8fc279 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -91,7 +91,7 @@ class TimelinePresenter @Inject constructor( } LaunchedEffect(timelineItems.size) { - computeHasNewItems(timelineItems, prevMostRecentItemId, hasNewItems) + computeHasNewItems(timelineItems, prevMostRecentItemId, hasNewItems) } LaunchedEffect(Unit) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index 9f81e612ba..6e16a3b92d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -140,6 +140,7 @@ fun TimelineView( } TimelineScrollHelper( + isTimelineEmpty = state.timelineItems.isEmpty(), lazyListState = lazyListState, hasNewItems = state.hasNewItems, onScrollFinishedAt = ::onScrollFinishedAt @@ -242,6 +243,7 @@ fun TimelineItemRow( @Composable private fun BoxScope.TimelineScrollHelper( + isTimelineEmpty: Boolean, lazyListState: LazyListState, hasNewItems: Boolean, onScrollFinishedAt: (Int) -> Unit, @@ -259,8 +261,8 @@ private fun BoxScope.TimelineScrollHelper( } } - LaunchedEffect(isScrollFinished) { - if (isScrollFinished) { + LaunchedEffect(isScrollFinished, isTimelineEmpty) { + if (isScrollFinished && !isTimelineEmpty) { // Notify the parent composable about the first visible item index when scrolling finishes onScrollFinishedAt(lazyListState.firstVisibleItemIndex) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt index 345884f7bf..b4bb14f672 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt @@ -119,7 +119,7 @@ class TimelinePresenterTest { } @Test - fun `present - on scroll finished will not send read receipt no event is before the index`() = runTest { + fun `present - on scroll finished will not send read receipt if no event is before the index`() = runTest { val timeline = FakeMatrixTimeline( initialTimelineItems = listOf( MatrixTimelineItem.Event(0, anEventTimelineItem()) From 22d4df1bafba1a64b6300c8d5c307de8e0592a2e Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 2 Aug 2023 13:55:13 +0200 Subject: [PATCH 221/251] Fix notification rendering by removing leftover test code (#1019) --- .../libraries/matrix/impl/notification/NotificationMapper.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt index 6f1c3b494b..3a18c7f19a 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt @@ -40,7 +40,6 @@ class NotificationMapper( roomId: RoomId, notificationItem: NotificationItem ): NotificationData { - notificationItem.event.use { (it as NotificationEvent.Timeline).event } val senderId = UserId(notificationItem.senderInfo.senderId) return notificationItem.use { item -> NotificationData( From 249b8aed02b0eab6f743a11a865f15c254bf6762 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 2 Aug 2023 20:43:47 +0200 Subject: [PATCH 222/251] Update dependency me.saket.telephoto:zoomable-image-coil to v0.5.0 (#1017) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 71584b9c5b..e908aba3a7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -45,7 +45,7 @@ dependencycheck = "8.3.1" dependencyanalysis = "1.20.0" stem = "2.3.0" sqldelight = "1.5.5" -telephoto = "0.4.0" +telephoto = "0.5.0" # DI dagger = "2.47" From ed6af5e49d8f25afd26b3f08983e0e8e93246b58 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 2 Aug 2023 20:56:26 +0200 Subject: [PATCH 223/251] Feature/fga/matrix client versions (#1022) * Extract a RustMatrixClientFactory * Use serverVersions api on client builder to speed up startup. * Clean up * Fix compilation of sample --------- Co-authored-by: ganfra --- .../matrix/impl/RustMatrixClientFactory.kt | 78 +++++++++++++++++++ .../auth/RustMatrixAuthenticationService.kt | 51 ++---------- .../android/samples/minimal/MainActivity.kt | 22 ++++-- 3 files changed, 100 insertions(+), 51 deletions(-) create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt new file mode 100644 index 0000000000..931133c266 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt @@ -0,0 +1,78 @@ +/* + * 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.libraries.matrix.impl + +import android.content.Context +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.di.ApplicationContext +import io.element.android.libraries.network.useragent.UserAgentProvider +import io.element.android.libraries.sessionstorage.api.SessionData +import io.element.android.libraries.sessionstorage.api.SessionStore +import io.element.android.services.toolbox.api.systemclock.SystemClock +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.withContext +import org.matrix.rustcomponents.sdk.ClientBuilder +import org.matrix.rustcomponents.sdk.Session +import org.matrix.rustcomponents.sdk.use +import java.io.File +import javax.inject.Inject + +class RustMatrixClientFactory @Inject constructor( + @ApplicationContext private val context: Context, + private val baseDirectory: File, + private val appCoroutineScope: CoroutineScope, + private val coroutineDispatchers: CoroutineDispatchers, + private val sessionStore: SessionStore, + private val userAgentProvider: UserAgentProvider, + private val clock: SystemClock, +) { + + suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) { + val client = ClientBuilder() + .basePath(baseDirectory.absolutePath) + .homeserverUrl(sessionData.homeserverUrl) + .username(sessionData.userId) + .userAgent(userAgentProvider.provide()) + // FIXME Quick and dirty fix for stopping version requests on startup https://github.com/matrix-org/matrix-rust-sdk/pull/1376 + .serverVersions(listOf("v1.0", "v1.1", "v1.2", "v1.3", "v1.4", "v1.5")) + .use { it.build() } + + client.restoreSession(sessionData.toSession()) + + val syncService = client.syncService().finish() + + RustMatrixClient( + client = client, + syncService = syncService, + sessionStore = sessionStore, + appCoroutineScope = appCoroutineScope, + dispatchers = coroutineDispatchers, + baseDirectory = baseDirectory, + baseCacheDirectory = context.cacheDir, + clock = clock, + ) + } +} + +private fun SessionData.toSession() = Session( + accessToken = accessToken, + refreshToken = refreshToken, + userId = userId, + deviceId = deviceId, + homeserverUrl = homeserverUrl, + slidingSyncProxy = slidingSyncProxy, +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt index 71e6e730e5..ba06891013 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt @@ -16,33 +16,27 @@ package io.element.android.libraries.matrix.impl.auth -import android.content.Context +// TODO Oidc +// import org.matrix.rustcomponents.sdk.OidcAuthenticationUrl import com.squareup.anvil.annotations.ContributesBinding import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.extensions.mapFailure import io.element.android.libraries.di.AppScope -import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.SingleIn import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.api.auth.OidcDetails import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.matrix.impl.RustMatrixClient +import io.element.android.libraries.matrix.impl.RustMatrixClientFactory import io.element.android.libraries.matrix.impl.exception.mapClientException import io.element.android.libraries.network.useragent.UserAgentProvider import io.element.android.libraries.sessionstorage.api.SessionData import io.element.android.libraries.sessionstorage.api.SessionStore -import io.element.android.services.toolbox.api.systemclock.SystemClock -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.withContext -import org.matrix.rustcomponents.sdk.Client -import org.matrix.rustcomponents.sdk.ClientBuilder -// TODO Oidc -// import org.matrix.rustcomponents.sdk.OidcAuthenticationUrl import org.matrix.rustcomponents.sdk.Session import org.matrix.rustcomponents.sdk.use import java.io.File @@ -53,13 +47,11 @@ import org.matrix.rustcomponents.sdk.AuthenticationService as RustAuthentication @ContributesBinding(AppScope::class) @SingleIn(AppScope::class) class RustMatrixAuthenticationService @Inject constructor( - @ApplicationContext private val context: Context, - private val baseDirectory: File, - private val appCoroutineScope: CoroutineScope, + baseDirectory: File, private val coroutineDispatchers: CoroutineDispatchers, private val sessionStore: SessionStore, - private val clock: SystemClock, - private val userAgentProvider: UserAgentProvider, + userAgentProvider: UserAgentProvider, + private val rustMatrixClientFactory: RustMatrixClientFactory, ) : MatrixAuthenticationService { private val authService: RustAuthenticationService = RustAuthenticationService( @@ -84,14 +76,7 @@ class RustMatrixAuthenticationService @Inject constructor( runCatching { val sessionData = sessionStore.getSession(sessionId.value) if (sessionData != null) { - val client = ClientBuilder() - .basePath(baseDirectory.absolutePath) - .homeserverUrl(sessionData.homeserverUrl) - .username(sessionData.userId) - .userAgent(userAgentProvider.provide()) - .use { it.build() } - client.restoreSession(sessionData.toSession()) - createMatrixClient(client) + rustMatrixClientFactory.create(sessionData) } else { error("No session to restore with id $sessionId") } @@ -181,30 +166,8 @@ class RustMatrixAuthenticationService @Inject constructor( */ } - private suspend fun createMatrixClient(client: Client): MatrixClient { - val syncService = client.syncService().finish() - return RustMatrixClient( - client = client, - syncService = syncService, - sessionStore = sessionStore, - appCoroutineScope = appCoroutineScope, - dispatchers = coroutineDispatchers, - baseDirectory = baseDirectory, - baseCacheDirectory = context.cacheDir, - clock = clock, - ) - } } -private fun SessionData.toSession() = Session( - accessToken = accessToken, - refreshToken = refreshToken, - userId = userId, - deviceId = deviceId, - homeserverUrl = homeserverUrl, - slidingSyncProxy = slidingSyncProxy, -) - private fun Session.toSessionData() = SessionData( userId = userId, deviceId = deviceId, diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt index 21d6648a41..a915e70046 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt @@ -26,11 +26,12 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.core.view.WindowCompat -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService +import io.element.android.libraries.matrix.impl.RustMatrixClientFactory import io.element.android.libraries.matrix.impl.auth.RustMatrixAuthenticationService import io.element.android.libraries.network.useragent.SimpleUserAgentProvider import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore +import io.element.android.libraries.theme.ElementTheme import io.element.android.services.toolbox.impl.systemclock.DefaultSystemClock import kotlinx.coroutines.runBlocking import java.io.File @@ -39,15 +40,22 @@ class MainActivity : ComponentActivity() { private val matrixAuthenticationService: MatrixAuthenticationService by lazy { val baseDirectory = File(applicationContext.filesDir, "sessions") - + val userAgentProvider = SimpleUserAgentProvider("MinimalSample") + val sessionStore = InMemorySessionStore() RustMatrixAuthenticationService( - context = applicationContext, baseDirectory = baseDirectory, - appCoroutineScope = Singleton.appScope, coroutineDispatchers = Singleton.coroutineDispatchers, - sessionStore = InMemorySessionStore(), - clock = DefaultSystemClock(), - userAgentProvider = SimpleUserAgentProvider("MinimalSample") + sessionStore = sessionStore, + userAgentProvider = userAgentProvider, + rustMatrixClientFactory = RustMatrixClientFactory( + context = applicationContext, + baseDirectory = baseDirectory, + appCoroutineScope = Singleton.appScope, + coroutineDispatchers = Singleton.coroutineDispatchers, + sessionStore = sessionStore, + userAgentProvider = userAgentProvider, + clock = DefaultSystemClock() + ) ) } From 30c513b1b5cda6174143cf97585f7b2ce7230650 Mon Sep 17 00:00:00 2001 From: David Langley Date: Wed, 2 Aug 2023 20:18:16 +0100 Subject: [PATCH 224/251] Reactions ux updates (#1020) * Fix ordering of reaction count/key label on outgoing messages and fix reaction button height - Fix ordering of reaction count/key label on outgoing messages - Fix reaction button height * Fix emojis circles on action list * Fix shape of reaction summary button when pressed * Update screenshots --------- Co-authored-by: ElementBot --- .../impl/actionlist/ActionListView.kt | 40 +++++---- .../components/MessagesReactionButton.kt | 19 ++-- .../components/TimelineItemReactionsLayout.kt | 16 ++-- .../components/TimelineItemReactionsView.kt | 86 ++++++++++--------- .../reactionsummary/ReactionSummaryView.kt | 5 +- ...ntPreview-D-0_1_null_2,NEXUS_5,1.0,en].png | 4 +- ...ntPreview-D-0_1_null_3,NEXUS_5,1.0,en].png | 4 +- ...ntPreview-D-0_1_null_4,NEXUS_5,1.0,en].png | 4 +- ...ntPreview-D-0_1_null_5,NEXUS_5,1.0,en].png | 4 +- ...ntPreview-D-0_1_null_6,NEXUS_5,1.0,en].png | 4 +- ...ntPreview-N-0_2_null_2,NEXUS_5,1.0,en].png | 4 +- ...ntPreview-N-0_2_null_3,NEXUS_5,1.0,en].png | 4 +- ...ntPreview-N-0_2_null_4,NEXUS_5,1.0,en].png | 4 +- ...ntPreview-N-0_2_null_5,NEXUS_5,1.0,en].png | 4 +- ...ntPreview-N-0_2_null_6,NEXUS_5,1.0,en].png | 4 +- ...review-D-11_12_null_0,NEXUS_5,1.0,en].png} | 0 ...review-D-11_12_null_1,NEXUS_5,1.0,en].png} | 0 ...review-D-11_12_null_2,NEXUS_5,1.0,en].png} | 0 ...review-N-11_13_null_0,NEXUS_5,1.0,en].png} | 0 ...review-N-11_13_null_1,NEXUS_5,1.0,en].png} | 0 ...review-N-11_13_null_2,NEXUS_5,1.0,en].png} | 0 ...review-D-12_13_null_0,NEXUS_5,1.0,en].png} | 0 ...review-D-12_13_null_1,NEXUS_5,1.0,en].png} | 0 ...review-N-12_14_null_0,NEXUS_5,1.0,en].png} | 0 ...review-N-12_14_null_1,NEXUS_5,1.0,en].png} | 0 ...review-D-13_14_null_0,NEXUS_5,1.0,en].png} | 0 ...review-N-13_15_null_0,NEXUS_5,1.0,en].png} | 0 ...wPreview-D-14_15_null,NEXUS_5,1.0,en].png} | 0 ...wPreview-N-14_16_null,NEXUS_5,1.0,en].png} | 0 ...ttonPreview-D-4_5_null,NEXUS_5,1.0,en].png | 3 + ...ttonPreview-N-4_6_null,NEXUS_5,1.0,en].png | 3 + ...tonsPreview-D-4_5_null,NEXUS_5,1.0,en].png | 3 - ...tonsPreview-D-5_6_null,NEXUS_5,1.0,en].png | 3 + ...tonsPreview-N-4_6_null,NEXUS_5,1.0,en].png | 3 - ...tonsPreview-N-5_7_null,NEXUS_5,1.0,en].png | 3 + ...tRowDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...RowLightPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...ionsDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...onsLightPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...eplyDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...plyLightPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...youtPreview-D-5_6_null,NEXUS_5,1.0,en].png | 3 - ...youtPreview-D-6_7_null,NEXUS_5,1.0,en].png | 3 + ...youtPreview-N-5_7_null,NEXUS_5,1.0,en].png | 3 - ...youtPreview-N-6_8_null,NEXUS_5,1.0,en].png | 3 + ...wFewPreview-D-7_8_null,NEXUS_5,1.0,en].png | 3 - ...wFewPreview-D-8_9_null,NEXUS_5,1.0,en].png | 3 + ...wFewPreview-N-7_9_null,NEXUS_5,1.0,en].png | 3 - ...FewPreview-N-8_10_null,NEXUS_5,1.0,en].png | 3 + ...mingPreview-D-8_9_null,NEXUS_5,1.0,en].png | 3 - ...ingPreview-D-9_10_null,NEXUS_5,1.0,en].png | 3 + ...ingPreview-N-8_10_null,NEXUS_5,1.0,en].png | 3 - ...ingPreview-N-9_11_null,NEXUS_5,1.0,en].png | 3 + ...ngPreview-D-10_11_null,NEXUS_5,1.0,en].png | 3 + ...ingPreview-D-9_10_null,NEXUS_5,1.0,en].png | 3 - ...ngPreview-N-10_12_null,NEXUS_5,1.0,en].png | 3 + ...ingPreview-N-9_11_null,NEXUS_5,1.0,en].png | 3 - ...ViewPreview-D-6_7_null,NEXUS_5,1.0,en].png | 3 - ...ViewPreview-D-7_8_null,NEXUS_5,1.0,en].png | 3 + ...ViewPreview-N-6_8_null,NEXUS_5,1.0,en].png | 3 - ...ViewPreview-N-7_9_null,NEXUS_5,1.0,en].png | 3 + ...ewPreview-D-2_3_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-D-2_3_null_1,NEXUS_5,1.0,en].png | 4 +- ...wPreview-D-2_3_null_10,NEXUS_5,1.0,en].png | 4 +- ...wPreview-D-2_3_null_11,NEXUS_5,1.0,en].png | 4 +- ...wPreview-D-2_3_null_12,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-D-2_3_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-D-2_3_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-D-2_3_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-D-2_3_null_5,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-D-2_3_null_6,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-D-2_3_null_7,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-D-2_3_null_8,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-D-2_3_null_9,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-N-2_4_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-N-2_4_null_1,NEXUS_5,1.0,en].png | 4 +- ...wPreview-N-2_4_null_10,NEXUS_5,1.0,en].png | 4 +- ...wPreview-N-2_4_null_11,NEXUS_5,1.0,en].png | 4 +- ...wPreview-N-2_4_null_12,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-N-2_4_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-N-2_4_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-N-2_4_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-N-2_4_null_5,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-N-2_4_null_6,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-N-2_4_null_7,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-N-2_4_null_8,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-N-2_4_null_9,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- 99 files changed, 247 insertions(+), 213 deletions(-) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-11_12_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-11_13_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-12_13_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-12_13_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-12_14_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-12_14_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-12_13_null_0,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-13_14_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-12_14_null_0,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-13_15_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-13_14_null,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-14_15_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-13_15_null,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-14_16_null,NEXUS_5,1.0,en].png} (100%) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesAddReactionButtonPreview-D-4_5_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesAddReactionButtonPreview-N-4_6_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-4_5_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-5_6_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-4_6_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-5_7_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-D-5_6_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-D-6_7_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-N-5_7_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-N-6_8_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-D-7_8_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-D-8_9_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-N-7_9_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-N-8_10_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-D-8_9_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-D-9_10_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-N-8_10_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-N-9_11_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-D-10_11_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-D-9_10_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-N-10_12_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-N-9_11_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-6_7_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-7_8_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-6_8_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-7_9_null,NEXUS_5,1.0,en].png diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt index 613caba586..b642e8e5aa 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt @@ -31,6 +31,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ListItem @@ -342,7 +343,7 @@ internal fun EmojiReactionsRow( ) { Row( horizontalArrangement = Arrangement.SpaceBetween, - modifier = modifier.padding(horizontal = 28.dp, vertical = 16.dp) + modifier = modifier.padding(horizontal = 24.dp, vertical = 16.dp) ) { // TODO use most recently used emojis here when available from the Rust SDK val defaultEmojis = sequenceOf( @@ -352,21 +353,25 @@ internal fun EmojiReactionsRow( val isHighlighted = highlightedEmojis.contains(emoji) EmojiButton(emoji, isHighlighted, onEmojiReactionClicked) } - - Icon( - imageVector = Icons.Outlined.AddReaction, - contentDescription = "Emojis", - tint = MaterialTheme.colorScheme.secondary, + Box( modifier = Modifier - .size(24.dp) - .align(Alignment.CenterVertically) - .clickable( - enabled = true, - onClick = onCustomReactionClicked, - indication = rememberRipple(bounded = false, radius = emojiRippleRadius), - interactionSource = remember { MutableInteractionSource() } - ) - ) + .size(48.dp), + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = Icons.Outlined.AddReaction, + contentDescription = "Emojis", + tint = MaterialTheme.colorScheme.secondary, + modifier = Modifier + .size(24.dp) + .clickable( + enabled = true, + onClick = onCustomReactionClicked, + indication = rememberRipple(bounded = false, radius = emojiRippleRadius), + interactionSource = remember { MutableInteractionSource() } + ) + ) + } } } @@ -385,12 +390,13 @@ private fun EmojiButton( Box( modifier = modifier .size(48.dp) - .background(backgroundColor, RoundedCornerShape(24.dp)), + .background(backgroundColor, CircleShape), + contentAlignment = Alignment.Center ) { Text( emoji, - fontSize = 28.dp.toSp(), + fontSize = 24.dp.toSp(), color = Color.White, modifier = Modifier .clickable( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt index 568bfefd18..930adf36cd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt @@ -113,6 +113,7 @@ sealed class MessagesReactionsButtonContent { } private val reactionEmojiLineHeight = 20.sp +private val addEmojiSize = 16.dp @Composable private fun TextContent( @@ -135,7 +136,8 @@ private fun IconContent( contentDescription = stringResource(id = R.string.screen_room_timeline_add_reaction), tint = ElementTheme.materialColors.secondary, modifier = modifier - .size(reactionEmojiLineHeight.toDp()) + .size(addEmojiSize) + ) @Composable @@ -173,15 +175,20 @@ internal fun MessagesReactionButtonPreview(@PreviewParameter(AggregatedReactionP ) } +@DayNightPreviews +@Composable +internal fun MessagesAddReactionButtonPreview() = ElementPreview { + MessagesReactionButton( + content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction), + onClick = {}, + onLongClick = {} + ) +} + @DayNightPreviews @Composable internal fun MessagesReactionExtraButtonsPreview() = ElementPreview { Row { - MessagesReactionButton( - content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction), - onClick = {}, - onLongClick = {} - ) MessagesReactionButton( content = MessagesReactionsButtonContent.Text("12 more"), onClick = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt index 1bd5cc2c1c..01800f6348 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt @@ -58,12 +58,12 @@ fun TimelineItemReactionsLayout( SubcomposeLayout(modifier) { constraints -> // Given the placeables and returns a structure representing // how they should wrap on to multiple rows given the constraints max width. - fun calculateRows(measurables: List): List> { + fun calculateRows(placeables: List): List> { val rows = mutableListOf>() var currentRow = mutableListOf() var rowX = 0 - measurables.forEach { placeable -> + placeables.forEach { placeable -> val horizontalSpacing = if (currentRow.isEmpty()) 0 else itemSpacing.toPx().toInt() // If the current view does not fit on this row bump to the next if (rowX + placeable.width > constraints.maxWidth) { @@ -146,12 +146,18 @@ fun TimelineItemReactionsLayout( } } - val reactionsPlaceables = subcompose(0, reactions).map { it.measure(constraints) } + var reactionsPlaceables = subcompose(0, reactions).map { it.measure(constraints) } if (reactionsPlaceables.isEmpty()) { return@SubcomposeLayout layoutRows(listOf()) } - val addMorePlaceable = subcompose(1, addMoreButton).first().measure(constraints) - val expandPlaceable = subcompose(2, expandButton).first().measure(constraints) + var expandPlaceable = subcompose(1, expandButton).first().measure(constraints) + // Enforce all reaction buttons have the same height + val maxHeight = (reactionsPlaceables + listOf(expandPlaceable)).maxOf { it.height } + val newConstrains = constraints.copy(minHeight = maxHeight) + reactionsPlaceables = subcompose(2, reactions).map { it.measure(newConstrains) } + expandPlaceable = subcompose(3, expandButton).first().measure(newConstrains) + val addMorePlaceable = subcompose(4, addMoreButton).first().measure(newConstrains) + // Calculate the layout of the rows with the reactions button and add more button val reactionsAndAddMore = calculateRows(reactionsPlaceables + listOf(addMorePlaceable)) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt index 39281dc26e..a5cdf4b6cc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt @@ -47,68 +47,74 @@ fun TimelineItemReactions( modifier: Modifier = Modifier, ) { var expanded: Boolean by rememberSaveable { mutableStateOf(false) } - - // In LTR languages we want an incoming message's reactions to be LRT and outgoing to be RTL. - // For RTL languages it should be the opposite. - val reactionsLayoutDirection = if (!isOutgoing) LocalLayoutDirection.current - else if (LocalLayoutDirection.current == LayoutDirection.Ltr) - LayoutDirection.Rtl - else - LayoutDirection.Ltr - - CompositionLocalProvider(LocalLayoutDirection provides reactionsLayoutDirection) { TimelineItemReactionsView( modifier = modifier, reactions = reactionsState.reactions, expanded = expanded, + isOutgoing = isOutgoing, onReactionClick = onReactionClicked, onReactionLongClick = onReactionLongClicked, onMoreReactionsClick = onMoreReactionsClicked, onToggleExpandClick = { expanded = !expanded }, ) - } } @Composable private fun TimelineItemReactionsView( reactions: ImmutableList, + isOutgoing: Boolean, expanded: Boolean, onReactionClick: (emoji: String) -> Unit, onReactionLongClick: (emoji: String) -> Unit, onMoreReactionsClick: () -> Unit, onToggleExpandClick: () -> Unit, modifier: Modifier = Modifier -) = TimelineItemReactionsLayout( - modifier = modifier, - itemSpacing = 4.dp, - rowSpacing = 4.dp, - expanded = expanded, - expandButton = { - MessagesReactionButton( - content = MessagesReactionsButtonContent.Text( - text = stringResource(id = if (expanded) R.string.screen_room_reactions_show_less else R.string.screen_room_reactions_show_more) - ), - onClick = onToggleExpandClick, - onLongClick = {} +) { + // In LTR languages we want an incoming message's reactions to be LRT and outgoing to be RTL. + // For RTL languages it should be the opposite. + val currentLayout = LocalLayoutDirection.current + val reactionsLayoutDirection = if (!isOutgoing) currentLayout + else if (currentLayout == LayoutDirection.Ltr) + LayoutDirection.Rtl + else + LayoutDirection.Ltr + + return CompositionLocalProvider(LocalLayoutDirection provides reactionsLayoutDirection) { + TimelineItemReactionsLayout( + modifier = modifier, + itemSpacing = 4.dp, + rowSpacing = 4.dp, + expanded = expanded, + expandButton = { + MessagesReactionButton( + content = MessagesReactionsButtonContent.Text( + text = stringResource(id = if (expanded) R.string.screen_room_reactions_show_less else R.string.screen_room_reactions_show_more) + ), + onClick = onToggleExpandClick, + onLongClick = {} + ) + }, + addMoreButton = { + MessagesReactionButton( + content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction), + onClick = onMoreReactionsClick, + onLongClick = {} + ) + }, + reactions = { + reactions.forEach { reaction -> + CompositionLocalProvider(LocalLayoutDirection provides currentLayout) { + MessagesReactionButton( + content = MessagesReactionsButtonContent.Reaction(reaction = reaction), + onClick = { onReactionClick(reaction.key) }, + onLongClick = { onReactionLongClick(reaction.key) } + ) + } + } + } ) - }, - addMoreButton = { - MessagesReactionButton( - content = MessagesReactionsButtonContent.Icon(Icons.Outlined.AddReaction), - onClick = onMoreReactionsClick, - onLongClick = {} - ) - }, - reactions = { - reactions.forEach { reaction -> - MessagesReactionButton( - content = MessagesReactionsButtonContent.Reaction(reaction = reaction), - onClick = { onReactionClick(reaction.key) }, - onLongClick = { onReactionLongClick(reaction.key) } - ) - } } -) +} @DayNightPreviews @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt index f794d60c81..1228c41a16 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt @@ -51,6 +51,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter @@ -177,10 +178,12 @@ fun AggregatedReactionButton( MaterialTheme.colorScheme.primary } + val roundedCornerShape = RoundedCornerShape(corner = CornerSize(percent = 50)) Surface( modifier = modifier + .background(buttonColor, roundedCornerShape) + .clip(roundedCornerShape) .clickable(onClick = onClick) - .background(buttonColor, RoundedCornerShape(corner = CornerSize(percent = 50))) .padding(vertical = 8.dp, horizontal = 12.dp), color = buttonColor ) { diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_2,NEXUS_5,1.0,en].png index 40cbe16de5..a72d4958d8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e635091747a867b0acbaac27225bb4a5c6e77a3b63005a03e80244425bffc839 -size 40314 +oid sha256:62e7ab953183e59348f5229dd398c6ffeeb7b60bffec9197139c58cfb335825c +size 38257 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_3,NEXUS_5,1.0,en].png index cdb2102b99..b755ac33c2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0b68e89219ad178eb4a4854d8a0800975628b098aea016ea74d3f9e3e6790363 -size 46990 +oid sha256:3d4e87ee1dceb9c0d9c464d3db5433e849eaafde2e6241ea00a72f7abc5ba971 +size 44852 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_4,NEXUS_5,1.0,en].png index f9c5109ceb..0fce5968de 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e456ca95ee33cf14cac839b2b57879d17ca47b156da65c8a870882a90ef2c84c -size 40664 +oid sha256:f54a07228afae0a4e03f824438fef9ac498551adc4740aca8bce0f79d7b5085e +size 38626 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_5,NEXUS_5,1.0,en].png index b0937d3b09..542d0d8682 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4bcce9d53c7c094698f07de6c6a25be2c7831707581861df4b0cdf0c3d6d1fd -size 40906 +oid sha256:cabf78c9278e2edcca8dac55f9bae26b38bf7a7be12e2ee5094922c2d0edc113 +size 38876 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_6,NEXUS_5,1.0,en].png index e561c5d902..16ce32c2bd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-D-0_1_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:849d063e18b29fd54021cfd0db6088221e53179563d1c044d7a10510cfa717ee -size 42158 +oid sha256:08d92ec4975dc62fe3b5bd332128da4b59b9f0c28bc797bb40df708ceb03fe5d +size 40325 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_2,NEXUS_5,1.0,en].png index 34754d19d9..361f1efce8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0321155131e845672b07cdbec0820892acd0728770befafa07dac888a4a44fc1 -size 38836 +oid sha256:2d1b47d5457264255a631e07bb8c109f1e4a90a50b2159d9bda42e8b94fb7ed6 +size 36919 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_3,NEXUS_5,1.0,en].png index 3f28eedfa0..744c2d05e6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a51de9335637898863151a3a3ffa81da9a0743189c854d11ad666d81d9f2b2f8 -size 45228 +oid sha256:3d61ec70383611690046153defdaf1580ab156a97e789d4dd7db76e30f0fd29b +size 43129 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_4,NEXUS_5,1.0,en].png index 410fe4da24..19cc8a54f0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66a7d55b40a9d5d7bc02f531bd3c80a1ba96e24a619c123f68549df355019558 -size 38989 +oid sha256:e7a4e6302b585ad904ebb029afa0c835383c2fa75e43e26cb3a1712caa150371 +size 37083 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_5,NEXUS_5,1.0,en].png index 6cc64a9ea3..3fa174aa4b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bb3eab588d8cc20eb2bcbbcb8a7acdf6431299bf575a124ed7aff8a4fe6cd15 -size 39182 +oid sha256:5aad5fda07e4add870d0e1835d136b54fa459bfc1c113b9d36b1564d21c2a15b +size 37252 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_6,NEXUS_5,1.0,en].png index 5107c6fc5f..0a2c54c087 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentPreview-N-0_2_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa5d6c7d504ab6436cfef4787a955422d9cc6b7c652ee70afedd0c6ca776d50a -size 40651 +oid sha256:e846c3d8ab9c2fffadf60cfc6ad9d959f50bcfe14c6006c3c303c5f8171430a3 +size 38646 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-11_12_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-10_11_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-D-11_12_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-11_13_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-10_12_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemAudioViewPreview-N-11_13_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-12_13_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-12_13_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-12_13_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-11_12_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-D-12_13_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-12_14_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-12_14_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-12_14_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-11_13_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewPreview-N-12_14_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-12_13_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-13_14_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-12_13_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-13_14_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-12_14_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-13_15_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-12_14_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-13_15_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-13_14_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-14_15_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-13_14_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-14_15_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-13_15_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-14_16_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-13_15_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-14_16_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesAddReactionButtonPreview-D-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesAddReactionButtonPreview-D-4_5_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..91af1135e8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesAddReactionButtonPreview-D-4_5_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1763f6e5114d406d9e2a5cbad96113d5d36f6ccfdee1cfade0146c1804d0a725 +size 5982 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesAddReactionButtonPreview-N-4_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesAddReactionButtonPreview-N-4_6_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..25592c4b2f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesAddReactionButtonPreview-N-4_6_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0582cf0802ca4a1e5fc823518f0dee9fbf31b3b2803c0a89baab35885139805e +size 5917 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-4_5_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 37cce233e5..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-4_5_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6c73a80923ec5b269bbb5f5108dde9617dac39e9c51f9199ab9229c4dd7ca2b0 -size 11532 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-5_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-5_6_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..08f201da01 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-D-5_6_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b009dd473a3a2d5eafae382f2477728cb7ef3053c52ec51bc3f3891769ae5f57 +size 10264 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-4_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-4_6_null,NEXUS_5,1.0,en].png deleted file mode 100644 index c122f1e1f8..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-4_6_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9eee3a5873231554cc0335fe7b552a62459f35e4ffb8c43b91a9746b34314bdc -size 11171 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-5_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-5_7_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1f7c631359 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionExtraButtonsPreview-N-5_7_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:153f8eb0807c8b34d2e990819e8f892382aae1125350d371d08a35454423a727 +size 9948 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowDarkPreview_0_null,NEXUS_5,1.0,en].png index fa1f71720b..e418577f55 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:85f9960cebee2e04d09f3ef17bd81e9adf9f463b24edae73a56d6d0c0a09ce48 -size 152228 +oid sha256:5bb246e6f180d6eef61c3dc0f884a070761d03b4f396d2ead1c178507a6bc70d +size 151279 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowLightPreview_0_null,NEXUS_5,1.0,en].png index abd9d48625..f9e018c372 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:39226828ae899c8b765827cad36f9966ec80c0a39ed406c1257224a56255af9b -size 157243 +oid sha256:2905961ddc979076374d5cd7fb0885b3e3fb7d13a3fa0ae915553d8bb0174c65 +size 156313 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsDarkPreview_0_null,NEXUS_5,1.0,en].png index 5349a17c04..f7bd5a3551 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88bdceab1a4e44f971ce507b5b6aeb5513657dfe6c4d61ec5483e215d83254d5 -size 81534 +oid sha256:35c0b796ce83fbe14b477f472344e4b3969033b77460a8744ccdb2a3235bec03 +size 81090 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsLightPreview_0_null,NEXUS_5,1.0,en].png index f5b5822a12..39a0c59701 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithManyReactionsLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:461683659d023323184049e122d02e4c63f217157e0bb3165f50be833d19ba7e -size 85517 +oid sha256:26b0a776582b02bf1ccdc7a75720097f9f653635ff14c42f432aadda4e832d61 +size 85152 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png index 48efd89d01..496a355edc 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26878fecd5785eed15a1fa46fabb0b2942139bf86dbce15ae544bfa5edf791e5 -size 127652 +oid sha256:2c5f983843e2d0689de6316091ccecbe4442b377427c8a5d9edf6a76e4f1d885 +size 127276 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png index 57d6316ca8..46037460cf 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemEventRowWithReplyLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cbdc89d08cd8e689bb603437dbbbeccab2870740752af313cd8b60ae48aee839 -size 132950 +oid sha256:b412cc33626f9987b35549c1cd693af7e16c79179093a74b7b38da5964da19c8 +size 132594 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-D-5_6_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-D-5_6_null,NEXUS_5,1.0,en].png deleted file mode 100644 index a47fa7ba44..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-D-5_6_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c81a4a854304b62bd356ae9a6d588918d11bf1df0aaa209910fa2fa970f47cb8 -size 26684 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-D-6_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-D-6_7_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e9c109b99c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-D-6_7_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c6a8e9a5fb8dac5bbcd7e0c0ac9df56759dc2e94a5d5195dd9102aba0c18617 +size 26509 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-N-5_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-N-5_7_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 20d82e1483..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-N-5_7_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f32e1f9f7c10672b3a35d6c0c3e3a9347b92ef3fe62be40bc378a5225da5d3d6 -size 26285 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-N-6_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-N-6_8_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..555d89e509 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsLayoutPreview-N-6_8_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79997cff1c86462be9b743f1c561d3b8667570b474e0187352d7d2e2cd056888 +size 26107 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-D-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-D-7_8_null,NEXUS_5,1.0,en].png deleted file mode 100644 index d85a93d2d4..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-D-7_8_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4e892d14d11fb576228418acd216468438f41a631a073e80638675ec48a91ed6 -size 12334 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-D-8_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-D-8_9_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2ddf5837cb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-D-8_9_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:064001a5d6bdf0c3c9c160e95c137789d874e8a595d3f30def3d7d77a049d4f9 +size 12113 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-N-7_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-N-7_9_null,NEXUS_5,1.0,en].png deleted file mode 100644 index a645425261..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-N-7_9_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:64c2e18e97bba9ff6ae6bb24a17fe567352cd49f1b5905065b7de8854369e22f -size 12209 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-N-8_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-N-8_10_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..42e074b9b7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewFewPreview-N-8_10_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36e2d799245e69505f55bfae68401570d983deb7747bbdfdd5f01372003587b3 +size 11994 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-D-8_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-D-8_9_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 7fd9264402..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-D-8_9_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6d0d29cc0acc0d9009c6fd6aebccf72d6244cec8ec3e3bf3bd445e7cced52561 -size 26004 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-D-9_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-D-9_10_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2ebedc0722 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-D-9_10_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4efea594700a66b3a8aa2fdf95d4eafd937fe3adce0c70982e3cf713aa662739 +size 25743 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-N-8_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-N-8_10_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 674e4ccb32..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-N-8_10_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dbfe5166cb1df7be35f2d3f114f73fb4425a10f4eb5bd24a87cafe3ddd76e873 -size 25719 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-N-9_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-N-9_11_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..924713cef0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewIncomingPreview-N-9_11_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:76d8e1608b83151e5f444669713808d48db77666b33b5ff44c8ee94b27b6bbca +size 25465 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-D-10_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-D-10_11_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..33128653fe --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-D-10_11_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8bbfc46af47025d2e2e22839b939f32b3fdfde904a7a08edcc4cfd270d20b9bf +size 25750 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-D-9_10_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-D-9_10_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 8fb3ea38a2..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-D-9_10_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d5f4241f356099cc2f2f55e557218c8872cd0f25f582ba75db254c11613090db -size 26026 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-N-10_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-N-10_12_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6d5ff5d621 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-N-10_12_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5ed3ac2e9d4f7c1090677a49ac25bf997ea09d53423ccbdd6b6ab330cf30098 +size 25518 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-N-9_11_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-N-9_11_null,NEXUS_5,1.0,en].png deleted file mode 100644 index fe175d156e..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewOutgoingPreview-N-9_11_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cf149f6a8c5c8d5d4591b9f6cfe8612a1af2b1cb101b0d1f699945bd711890b4 -size 25685 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-6_7_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-6_7_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 119678a386..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-6_7_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:82550de45d39c52f34fca45f84b6e9eff53dfef6d2e9622dcaf49c707927d665 -size 7873 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-7_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-7_8_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ba0bb563bd --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-D-7_8_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d75da9638e70268c303f7f1f8affa9302e449d00edef0f21df5155be3ce3ff3e +size 7646 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-6_8_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-6_8_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 896fd1051e..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-6_8_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fc39041ac035aa372640ca7b2e9af254564aa10ce7a7f4c55fbc4bca9f6dc6d9 -size 7844 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-7_9_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-7_9_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..396e287a9d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewPreview-N-7_9_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cc554dfec200c02710c7ef21be0cb22753b4457e96738d3a74500617d8854e41 +size 7656 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_0,NEXUS_5,1.0,en].png index f1add17e7a..7a219f8773 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cfc4f0cc8e252cdc9200286cf330c4bbdf3cd66ced2c23364cca680aca79954d -size 53293 +oid sha256:efe8b8964a4588512338873afcdad87ef9d6d1983032997c38e2f74c5cbd279b +size 52046 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_1,NEXUS_5,1.0,en].png index 3a9a78de1d..a66dc2643a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:01c7f94b759eea738fb42baa4b8b835c27d48e3fbfdd40d0a925080db8196a80 -size 65563 +oid sha256:44135b12caaa2fd0bb814a311e54ccd899c302c10daa4bce09330815ac7fef77 +size 64155 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_10,NEXUS_5,1.0,en].png index 2cf5e925c9..3665a2f820 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4360b6f5be55e5b468e735f83b6263ae3b0f1a3f7c0084e6dc3c07396723abd -size 51206 +oid sha256:aa580473120654d5e49e1386c03bc2f813f588af7eb9962c45b9896f6b7b7124 +size 50003 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_11,NEXUS_5,1.0,en].png index d025eed585..f03ba74880 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a58bb336bbf56b05584d73fa7fd3bd2e2fe97c82829a01988343d4c3b4c77ab -size 68727 +oid sha256:aa77a7edb5d8194dc7cbfff08d86f3d4b86ceaa900e3391c0d20258a39da95fa +size 67333 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_12,NEXUS_5,1.0,en].png index 78e3c4cef7..35f86e9a4a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_12,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_12,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff2c7a701945b72afc6731c9da722e0bf178879f0bd80bc40da7ebb1d6e90d28 -size 58505 +oid sha256:5122e480cb7c386427961e1e262dbee7e75b37939a7430235ed6e1d0c47cf544 +size 57260 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_2,NEXUS_5,1.0,en].png index 1c32b14777..b9b4cf2174 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:145856c3a7ff43702403ee5b86a7119b0475f03fdbc0f2e7f84e10350a64b150 -size 229842 +oid sha256:21b5e874428ef5912442d3a92ed694f0f016c3236781a02886c5c3a589bbb317 +size 229385 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_3,NEXUS_5,1.0,en].png index 53f57d95d2..450b33d8c2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1f0939b0c22ab89466953889e5bb63e11c45f02af79eeba3f377409af61d356 -size 230809 +oid sha256:0883699f8e551930478d0e43540497bea6f1cd9fe6f2b95df5c5e36370eb3a9a +size 230368 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_4,NEXUS_5,1.0,en].png index 76a8b6f2f9..86f0aa481a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4591b08ec297c0b024870e60ac18b03aadeaa8ff20a726dd991b18a80ec353ef -size 73607 +oid sha256:dae5b99b3234fe999673593dfe75feb44b0122e872af62dd2bed0db610132655 +size 72595 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_5,NEXUS_5,1.0,en].png index b76ddce111..fffa0ebe9c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6586d62bdbd3d4331105f561669c5383d578ad8af559270a26c7d9ff741b473b -size 89412 +oid sha256:74880d4c54a6ec9eff418ffdaa1da5888cc1b1ce4d92f958c36c36c7947a6321 +size 88190 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_6,NEXUS_5,1.0,en].png index 727cfa4b3c..d49e704e4b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9efb4310c2eed085f8f72be66d725c628e07373cd18c262c18be510d7b042f69 -size 393373 +oid sha256:a71e1d72dd7af142b2a8bb09810a416f43f619c697ed9c0c1cdf3d85df0259f5 +size 392639 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_7,NEXUS_5,1.0,en].png index 9c29642146..0290995c54 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19982d091ffcb15a422e75461adb3d039775645eaf74b6bfd4a3ee617dd4555f -size 347932 +oid sha256:52d83b9001db80db98e0a837672f218a77736e65549f5e31466f8aecadbfff54 +size 347295 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_8,NEXUS_5,1.0,en].png index 7aca1bcd17..1f8e834b83 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8981ec8fd5e68e6e332f98ee2c95fc0f902b9b912a05201cad1c6a9feb5dbc89 -size 54868 +oid sha256:79a36eab1ad6523438954ff55aa21ac92cd349f119e8eab4b61d25b4c44a56b1 +size 53627 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_9,NEXUS_5,1.0,en].png index 2c64ab5210..585b1e3980 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-D-2_3_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a5e787ce25a3542e398bec3aadd2b9b923c81dee4f3fbf8936c1c15e5aa1816b -size 67450 +oid sha256:f7b35322f9f344fb49ab2e2642a28c1c04bfd960a0354b4382240886616658bc +size 66053 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_0,NEXUS_5,1.0,en].png index cb3cb766a7..d52711f7e7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a8af5ff868170d946d03db115271e7ea9c0a15db35bf6815bf95cc4e4f7fa81 -size 51357 +oid sha256:3e6daf18be28261abd6753b39faa6f7bfc0b820d9e42a1bb0188cb4cc8ced6b4 +size 50224 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_1,NEXUS_5,1.0,en].png index 5b168bd85f..ac85571fb0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:438bc82c007c9cd72228f47681981e833645c591c0a48e8a5cb70bd51721d35a -size 62756 +oid sha256:5146396bf9b765169c583b14da54b43d55b2d306cf35890d500fde90be3fd36b +size 61348 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_10,NEXUS_5,1.0,en].png index d2d115d1e0..4a9590eb47 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a0926170ca4277e47f9ae49f5092c195cee2a84d67259ba8dc3aa3539d4faecf -size 49454 +oid sha256:28d13b05def8bc769e61a5814b6c96afe704c773d1d86f6d11b2021ebce81035 +size 48407 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_11,NEXUS_5,1.0,en].png index 7af4192912..06b5d396e4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68ce3ada02e8ed6cb1e257e49d8c703bd0ff3401bf0e520fa617ed1ea91c6756 -size 65691 +oid sha256:2d23ccbe1a72f6b375cd1eca980127ada3b8aa39aeb8b29e2a7caf80aea76165 +size 64255 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_12,NEXUS_5,1.0,en].png index 2eeac61c7d..289c358d9e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_12,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_12,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c902787fb2d84a0e92832020cdd447fc039264fc5add63abea32241251c92bf7 -size 56102 +oid sha256:9adc9689e6e88eea0fa467713bcf8941ca645219ce6e6f3cf4dd215c3d1301de +size 54965 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_2,NEXUS_5,1.0,en].png index 8e578cb1f9..6bd18e0e57 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:71b3c712fb8e4bca178afed2de6f4184bb717e3415622e97f14bb4fe36aaf9d5 -size 229097 +oid sha256:c471afa95f769bc06f52ccead68e7f50db5f6be436380e43b47c564b299fc2ee +size 228623 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_3,NEXUS_5,1.0,en].png index 13d92ee329..a841399bab 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f44d772e5b9fe65a79e05aaaa82536b19382f9d821c0412c47a7d330d539ffd2 -size 230080 +oid sha256:be72eda04000150aefc4570d68015d21e7647f448bbcd69ea2d75e62ec0b4caf +size 229617 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_4,NEXUS_5,1.0,en].png index cb7e8bc36f..3eca13fcfa 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc956e4fdab7bf15d71e10585947f9f5c24bc1d9e4eaa071de9b747fae879f53 -size 70883 +oid sha256:c14449fed7d0364077dc8c5804064dccb7b2969595c4c58e8da07fe03ccbe1bc +size 69731 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_5,NEXUS_5,1.0,en].png index 38441b5580..6ebc0f4cd2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fdf1b66ffe0b7042356d22aa835ceea2c5b697c4ce62287b5ec040f528210216 -size 84635 +oid sha256:5e29e1504333aa44eae6c3eb4af64c337fce01af895c7b920781ca895e6ea5a8 +size 83402 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_6,NEXUS_5,1.0,en].png index 2a6187d9a3..f8c0aa4bd2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ee754e789926bfd12212a21b6ff882ffca337d6903125ba5e1d7cd0c1c218ec -size 189364 +oid sha256:9045218a096f1104f1672f733fd0cfa21114dbc41d11062d4418febadb659c54 +size 189065 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_7,NEXUS_5,1.0,en].png index a65dbca59c..5f16ee28ee 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29f9ba8c19287b037f67aec5af4469174eb878b7ed5baf222f66e159faee6e16 -size 178410 +oid sha256:17afd91f19020067c3ef1b739834222e208d378e0a51cf37aa31063e187c7244 +size 178149 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_8,NEXUS_5,1.0,en].png index 01dfac9579..b5dbff5ef8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e438a2e542780851a7b570407e02f125e191d23b9c5299d9457baad78523de84 -size 52740 +oid sha256:45974a6f6a441f1214bd557c218877bc756ad49b4bd3e03313915ef9cfb9ac3c +size 51578 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_9,NEXUS_5,1.0,en].png index c72d23a518..f04e4afe02 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewPreview-N-2_4_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:17f11b2cb93b8736a2d1224b7ad70c7e3a1a45bcb7e5ceb60475dba734e6efec -size 64787 +oid sha256:713006427939a992312a0d7023afe3c81810caa5b879792c92ee89ee93881113 +size 63357 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index ba94c22dd0..791a623ef1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de7f21b5fcc1235a80ea2e7f9e26a93503ea2d0c0793a89db8657979afb33a7d -size 52267 +oid sha256:3959ae6151f02d7f41afe498b57a9b55255ea88335504d9d17750259100db1bc +size 51232 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 3dc03be3ae..633b9ae8b1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:613d55031bc30060bdd84596fb78de043ead483ac59fd42e48b15f0e56947a36 -size 53660 +oid sha256:432f2a5451368679c076515406808f1b136b3db8239841b7ffa65417c3d9b901 +size 52632 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 33a7951011..a8162ad980 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56440b44cf610b0baaf7401bebb8ba2f4504b5dd36cb54fe1669d5deec6d1674 -size 52530 +oid sha256:610aeadaa817fd4e50471fda96fe24aceb48ddb03a0e58d0305abdb96553e8ec +size 51463 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 01722cbae6..b36175d6f2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9387ab3e62a9e10721efdea3a6abcdf29db82a04ebdb3c4cefcad2a20ab9b9b8 -size 55386 +oid sha256:ca313b8e0c24369bee836649c7984ac18783893211df46f792e36261bbac0bc5 +size 54170 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index f96cc5216e..de7ac30a62 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0e3bd4e37bb665df997f189e5c2dc763c7f703b78384c63737675bd764fe7a8 -size 51174 +oid sha256:c5150ff49e5e3dbcf21f056f1502a8da71696a93f842d243ef4abce506921f93 +size 50486 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index f5df0bc94e..5bb2141eb2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5f6af4e49ea68ef43a7342910d5f6c72981044ad776ea484adc60fb5578a179 -size 49863 +oid sha256:51320b6491f229241d6ed52aaab871ea217d32ebe42ac42496bd702543ee45ff +size 48800 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 9eeb92ff02..5a207ffda8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ade57e690313eaaeddc7b21b9a2661026fca70e6af1b09aa331bba3d1b1bbaf3 -size 54240 +oid sha256:2d1da7016a99ce5c913f1f0c3c7c8047531af13412b846685683160212e88ef5 +size 53061 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 26d1cd82c3..4583dd5346 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:632dfa6edc34b7283654e43c3b7d9eb0334507644e50bc5fc7bdf8b5c45ed874 -size 55686 +oid sha256:962cebd43ee1f0872c25413d8a62c9dd279751e00b9c2154f156e49fd8c25ebd +size 54554 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 78402d239a..2d30ca5d01 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19420aad469fad8a218a0a8cd0f7cfbb9593d7ceb799478a7ad9b84d9c24602f -size 54598 +oid sha256:50b8c157da1134708777ce4940c58f50a48414e837762064059d44082a0303f7 +size 53415 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index 6207b630da..7d4f85fbe9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e224a21a1ae913066530e8be81760795b10be388cb7fa92fe399b1e11367d7af -size 57423 +oid sha256:e5ef4489d60dcb1ae6f976e229849af0c89dbeb7bcd4e37a92f18e2514387a1d +size 56253 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index 9a56904200..03f898df03 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cac4b5bdb9f225c5313f36caef190e3f698b734db79707928572c942fed85603 -size 52960 +oid sha256:3e2de325d2ddb67538519aa93844cada90abcc191f8db401b104239dfee8c1b9 +size 52262 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index b7a9808bc0..5405001cce 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26aef777d4601de18948ace8c85d5935eb8e008d07b4f3a09f40a416164bbc44 -size 51602 +oid sha256:a0a704d06f30f34414495bc6c74ac9a69bae32f62d0d9608c8ac43550c0cc7be +size 50426 From 615b05178e3c3a77206dc81e4996e19c8d2710d0 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 2 Aug 2023 21:53:05 +0200 Subject: [PATCH 225/251] Update rust sdk to 0.1.39 (#1024) Co-authored-by: ganfra --- gradle/libs.versions.toml | 2 +- .../impl/notification/NotificationMapper.kt | 22 +++++++------------ ...imelineEventToNotificationContentMapper.kt | 15 ++++++++----- .../matrix/impl/room/RoomContentForwarder.kt | 2 +- .../matrix/impl/room/RustMatrixRoom.kt | 5 ++--- .../timeline/MatrixTimelineDiffProcessor.kt | 9 ++++++-- .../impl/timeline/RoomTimelineExtensions.kt | 4 ++-- .../impl/timeline/RustMatrixTimeline.kt | 21 ++++++++---------- 8 files changed, 39 insertions(+), 41 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e908aba3a7..e5e0539b46 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -145,7 +145,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" } timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.38" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.39" sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" } sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt index 3a18c7f19a..0d9f794173 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt @@ -20,7 +20,6 @@ import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.notification.NotificationContent import io.element.android.libraries.matrix.api.notification.NotificationData import io.element.android.libraries.matrix.api.room.RoomMembershipState @@ -30,7 +29,7 @@ import org.matrix.rustcomponents.sdk.NotificationItem import org.matrix.rustcomponents.sdk.use class NotificationMapper( - private val sessionId: SessionId, + sessionId: SessionId, private val clock: SystemClock, ) { private val notificationContentMapper = NotificationContentMapper(sessionId) @@ -40,7 +39,6 @@ class NotificationMapper( roomId: RoomId, notificationItem: NotificationItem ): NotificationData { - val senderId = UserId(notificationItem.senderInfo.senderId) return notificationItem.use { item -> NotificationData( eventId = eventId, @@ -52,22 +50,20 @@ class NotificationMapper( isDirect = item.roomInfo.isDirect, isEncrypted = item.roomInfo.isEncrypted.orFalse(), isNoisy = item.isNoisy.orFalse(), - timestamp = item.timestamp(clock), - content = item.event.use { notificationContentMapper.map(senderId, it) }, + timestamp = item.timestamp() ?: clock.epochMillis(), + content = item.event.use { notificationContentMapper.map(it) }, contentUrl = null, ) } } } -class NotificationContentMapper( - private val sessionId: SessionId, -) { +class NotificationContentMapper(private val sessionId: SessionId) { private val timelineEventToNotificationContentMapper = TimelineEventToNotificationContentMapper() - fun map(senderId: UserId, notificationEvent: NotificationEvent): NotificationContent = + fun map(notificationEvent: NotificationEvent): NotificationContent = when (notificationEvent) { - is NotificationEvent.Timeline -> timelineEventToNotificationContentMapper.map(senderId, notificationEvent.event) + is NotificationEvent.Timeline -> timelineEventToNotificationContentMapper.map(notificationEvent.event) is NotificationEvent.Invite -> NotificationContent.StateEvent.RoomMemberContent( userId = sessionId.value, membershipState = RoomMembershipState.INVITE, @@ -75,8 +71,6 @@ class NotificationContentMapper( } } -private fun NotificationItem.timestamp(clock: SystemClock): Long { - // FIXME we can't get the timestamp from the notification item anymore, so we need to fake it -// return (this.event as? NotificationEvent.Timeline)?.event?.timestamp()?.toLong() - return clock.epochMillis() +private fun NotificationItem.timestamp(): Long? { + return (this.event as? NotificationEvent.Timeline)?.event?.timestamp()?.toLong() } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt index 1bf11b8e93..e30e57113d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt @@ -22,15 +22,18 @@ import io.element.android.libraries.matrix.impl.room.RoomMemberMapper import io.element.android.libraries.matrix.impl.timeline.item.event.EventMessageMapper import org.matrix.rustcomponents.sdk.MessageLikeEventContent import org.matrix.rustcomponents.sdk.StateEventContent +import org.matrix.rustcomponents.sdk.TimelineEvent import org.matrix.rustcomponents.sdk.TimelineEventType import org.matrix.rustcomponents.sdk.use import javax.inject.Inject class TimelineEventToNotificationContentMapper @Inject constructor() { - fun map(senderId: UserId, timelineEventType: TimelineEventType): NotificationContent { - return timelineEventType.use { - it.toContent(senderId = senderId) + fun map(timelineEvent: TimelineEvent): NotificationContent { + return timelineEvent.use { + timelineEvent.eventType().use { eventType -> + eventType.toContent(senderId = UserId(timelineEvent.senderId())) + } } } } @@ -72,7 +75,7 @@ private fun StateEventContent.toContent(): NotificationContent.StateEvent { private fun MessageLikeEventContent.toContent(senderId: UserId): NotificationContent.MessageLike { return use { - when (it) { + when (this) { MessageLikeEventContent.CallAnswer -> NotificationContent.MessageLike.CallAnswer MessageLikeEventContent.CallCandidates -> NotificationContent.MessageLike.CallCandidates MessageLikeEventContent.CallHangup -> NotificationContent.MessageLike.CallHangup @@ -84,10 +87,10 @@ private fun MessageLikeEventContent.toContent(senderId: UserId): NotificationCon MessageLikeEventContent.KeyVerificationMac -> NotificationContent.MessageLike.KeyVerificationMac MessageLikeEventContent.KeyVerificationReady -> NotificationContent.MessageLike.KeyVerificationReady MessageLikeEventContent.KeyVerificationStart -> NotificationContent.MessageLike.KeyVerificationStart - is MessageLikeEventContent.ReactionContent -> NotificationContent.MessageLike.ReactionContent(it.relatedEventId) + is MessageLikeEventContent.ReactionContent -> NotificationContent.MessageLike.ReactionContent(relatedEventId) MessageLikeEventContent.RoomEncrypted -> NotificationContent.MessageLike.RoomEncrypted is MessageLikeEventContent.RoomMessage -> { - NotificationContent.MessageLike.RoomMessage(senderId, EventMessageMapper().mapMessageType(it.messageType)) + NotificationContent.MessageLike.RoomMessage(senderId, EventMessageMapper().mapMessageType(messageType)) } MessageLikeEventContent.RoomRedaction -> NotificationContent.MessageLike.RoomRedaction MessageLikeEventContent.Sticker -> NotificationContent.MessageLike.Sticker diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt index 4e2d63d091..c4f272797a 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt @@ -80,6 +80,6 @@ class RoomContentForwarder( } private object NoOpTimelineListener : TimelineListener { - override fun onUpdate(diff: TimelineDiff) = Unit + override fun onUpdate(diff: List) = Unit } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 5503c22b1d..dc74a273f4 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -372,14 +372,13 @@ class RustMatrixRoom( } //TODO handle cancellation, need refactoring of how we are catching errors - private suspend fun sendAttachment(handle: () -> SendAttachmentJoinHandle): Result = withContext(roomDispatcher) { - runCatching { + private suspend fun sendAttachment(handle: () -> SendAttachmentJoinHandle): Result { + return runCatching { handle().use { it.join() } } } - } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineDiffProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineDiffProcessor.kt index b14d459697..fd880e7fe3 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineDiffProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/MatrixTimelineDiffProcessor.kt @@ -23,6 +23,7 @@ import kotlinx.coroutines.sync.withLock import org.matrix.rustcomponents.sdk.TimelineChange import org.matrix.rustcomponents.sdk.TimelineDiff import org.matrix.rustcomponents.sdk.TimelineItem +import timber.log.Timber internal class MatrixTimelineDiffProcessor( private val timelineItems: MutableStateFlow>, @@ -33,14 +34,18 @@ internal class MatrixTimelineDiffProcessor( suspend fun postItems(items: List) { updateTimelineItems { + Timber.v("Update timeline items from postItems (with ${items.size} items) on ${Thread.currentThread()}") val mappedItems = items.map { it.asMatrixTimelineItem() } addAll(0, mappedItems) } } - suspend fun postDiff(diff: TimelineDiff) { + suspend fun postDiffs(diffs: List) { updateTimelineItems { - applyDiff(diff) + Timber.v("Update timeline items from postDiffs (with ${diffs.size} items) on ${Thread.currentThread()}") + diffs.forEach { diff -> + applyDiff(diff) + } } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt index 9f8bccb1d0..bddd2bc872 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt @@ -35,10 +35,10 @@ import org.matrix.rustcomponents.sdk.TimelineItem import org.matrix.rustcomponents.sdk.TimelineListener import timber.log.Timber -internal fun Room.timelineDiffFlow(onInitialList: suspend (List) -> Unit): Flow = +internal fun Room.timelineDiffFlow(onInitialList: suspend (List) -> Unit): Flow> = callbackFlow { val listener = object : TimelineListener { - override fun onUpdate(diff: TimelineDiff) { + override fun onUpdate(diff: List) { trySendBlocking(diff) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt index 9ec44af998..aa8427608d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt @@ -32,7 +32,6 @@ import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.ensureActive import kotlinx.coroutines.flow.Flow @@ -43,7 +42,6 @@ import kotlinx.coroutines.flow.getAndUpdate import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.sample import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.BackPaginationStatus @@ -109,11 +107,11 @@ class RustMatrixTimeline( roomCoroutineScope.launch(dispatcher) { innerRoom.timelineDiffFlow { initialList -> postItems(initialList) - }.onEach { diff -> - if (diff.eventOrigin() == EventItemOrigin.SYNC) { + }.onEach { diffs -> + if (diffs.any { diff -> diff.eventOrigin() == EventItemOrigin.SYNC }) { onNewSyncedEvent() } - postDiff(diff) + postDiffs(diffs) }.launchIn(this) innerRoom.backPaginationStatusFlow() @@ -135,11 +133,10 @@ class RustMatrixTimeline( } } - @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) - override val timelineItems: Flow> = _timelineItems.sample(50) - .mapLatest { items -> - encryptedHistoryPostProcessor.process(items) - } + @OptIn(ExperimentalCoroutinesApi::class) + override val timelineItems: Flow> = _timelineItems.mapLatest { items -> + encryptedHistoryPostProcessor.process(items) + } private suspend fun postItems(items: List) = coroutineScope { // Split the initial items in multiple list as there is no pagination in the cached data, so we can post timelineItems asap. @@ -151,9 +148,9 @@ class RustMatrixTimeline( initLatch.complete(Unit) } - private suspend fun postDiff(timelineDiff: TimelineDiff) { + private suspend fun postDiffs(diffs: List) { initLatch.await() - timelineDiffProcessor.postDiff(timelineDiff) + timelineDiffProcessor.postDiffs(diffs) } private fun postPaginationStatus(status: BackPaginationStatus) { From 03cfe4acdeb9973d8f25f1bd2d7ee7fa980ebedb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 4 Aug 2023 11:43:06 +0200 Subject: [PATCH 226/251] Update dependency org.matrix.rustcomponents:sdk-android to v0.1.40 (#1026) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e5e0539b46..3c4cdc590c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -145,7 +145,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" } timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.39" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.40" sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" } sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" } From e2fb39618956063764c69809e2fa06e583e7970f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 4 Aug 2023 11:44:21 +0200 Subject: [PATCH 227/251] Update dependency com.google.firebase:firebase-bom to v32.2.2 (#1025) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3c4cdc590c..243dcb0cd8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -65,7 +65,7 @@ android_gradle_plugin = { module = "com.android.tools.build:gradle", version.ref android_desugar = "com.android.tools:desugar_jdk_libs:2.0.3" kotlin_gradle_plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } # https://firebase.google.com/docs/android/setup#available-libraries -google_firebase_bom = "com.google.firebase:firebase-bom:32.2.0" +google_firebase_bom = "com.google.firebase:firebase-bom:32.2.2" # AndroidX androidx_material = { module = "com.google.android.material:material", version.ref = "material" } From 8a0ad443d17785a7b2a3a9c538b71cb3e546c75a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 8 Aug 2023 08:00:11 +0200 Subject: [PATCH 228/251] Update plugin ktlint to v11.5.1 (#1034) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 243dcb0cd8..6661cdf517 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -196,7 +196,7 @@ kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } anvil = { id = "com.squareup.anvil", version.ref = "anvil" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } -ktlint = "org.jlleitschuh.gradle.ktlint:11.5.0" +ktlint = "org.jlleitschuh.gradle.ktlint:11.5.1" dependencygraph = { id = "com.savvasdalkitsis.module-dependency-graph", version.ref = "dependencygraph" } dependencycheck = { id = "org.owasp.dependencycheck", version.ref = "dependencycheck" } dependencyanalysis = { id = "com.autonomousapps.dependency-analysis", version.ref = "dependencyanalysis" } From c14cf15d4f542729c0aef5fffffdf2e7cf5fc284 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 8 Aug 2023 10:48:39 +0200 Subject: [PATCH 229/251] Use for instead of forEach with ranges (#1035) * Use `for` instead of `forEach` with ranges. `forEach` is several times slower when used with ranges. * Add changelog --- changelog.d/1035.bugfix | 1 + .../libraries/androidutils/diff/DiffCacheInvalidator.kt | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 changelog.d/1035.bugfix diff --git a/changelog.d/1035.bugfix b/changelog.d/1035.bugfix new file mode 100644 index 0000000000..4a2a6376ce --- /dev/null +++ b/changelog.d/1035.bugfix @@ -0,0 +1 @@ +Use `for` instead of `forEach` in `DefaultDiffCacheInvalidator` to improve performance. diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheInvalidator.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheInvalidator.kt index d9f378c8fa..4ebdc3224f 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheInvalidator.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/diff/DiffCacheInvalidator.kt @@ -38,9 +38,9 @@ interface DiffCacheInvalidator { class DefaultDiffCacheInvalidator : DiffCacheInvalidator { override fun onChanged(position: Int, count: Int, cache: MutableDiffCache) { - (position until position + count).forEach { + for (i in position until position + count) { // Invalidate cache - cache[it] = null + cache[i] = null } } From dcd5c2de7a690ff091b7d800c1a9ff12a7a24b3b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 8 Aug 2023 11:47:09 +0200 Subject: [PATCH 230/251] Update danger/danger-js action to v11.2.8 (#1008) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/danger.yml | 2 +- .github/workflows/quality.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml index 8186f0370c..4d997ec632 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -13,7 +13,7 @@ jobs: - run: | npm install --save-dev @babel/plugin-transform-flow-strip-types - name: Danger - uses: danger/danger-js@11.2.7 + uses: danger/danger-js@11.2.8 with: args: "--dangerfile ./tools/danger/dangerfile.js" env: diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 1cc49d5503..1efa0ae215 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -70,7 +70,7 @@ jobs: yarn add danger-plugin-lint-report --dev - name: Danger lint if: always() - uses: danger/danger-js@11.2.7 + uses: danger/danger-js@11.2.8 with: args: "--dangerfile ./tools/danger/dangerfile-lint.js" env: From 9b57383921c765ccbab24c3114fc5dbc15f28fda Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 8 Aug 2023 13:08:38 +0200 Subject: [PATCH 231/251] Update dependency io.sentry:sentry-android to v6.28.0 (#1028) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6661cdf517..744fecfc24 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -163,7 +163,7 @@ maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:2.0.0" # Analytics posthog = "com.posthog.android:posthog:2.0.3" -sentry = "io.sentry:sentry-android:6.27.0" +sentry = "io.sentry:sentry-android:6.28.0" matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:42b2faa417c1e95f430bf8f6e379adba25ad5ef8" # Di From af932b31c4d1087ebfcc3df3eaf180eca0d2a9b4 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 8 Aug 2023 18:11:37 +0200 Subject: [PATCH 232/251] [Compound] Implement components (Button) (#1021) * Create `CompoundButton` * Some fixes * Lint fixes * Start replacing existing `Button` usages * Replace button usages * Remove previous Button composable * Rename `CompoundButton` to `Button` * Fix emphasized button being displayed as Text * Fix cancel button in `WaitListView` * Update screenshots * Add shorthand functions for `OutlinedButton` and `TextButton` * Add changelog * Fix wrong size used for emphasized button in dialog * Create a private `ButtonInternal` implementation with the shared logic. - Make `ButtonStyle` private. - Rename `title` to `text`. - Rename `buttonStyle` and `buttonSize` to just `style` and `size`. * Fix several warnings and lint issues. * Update screenshots --------- Co-authored-by: ElementBot --- changelog.d/1021.misc | 1 + .../analytics/impl/AnalyticsOptInView.kt | 12 +- .../impl/addpeople/AddPeopleView.kt | 13 +- .../impl/configureroom/ConfigureRoomView.kt | 10 +- .../features/ftue/impl/welcome/WelcomeView.kt | 8 +- .../invitelist/impl/InviteListView.kt | 1 - .../impl/components/InviteSummaryRow.kt | 19 +- .../dialogs/SlidingSyncNotSupportedDialog.kt | 1 - .../ConfirmAccountProviderView.kt | 10 +- .../loginpassword/LoginPasswordView.kt | 4 +- .../screens/waitlistscreen/WaitListView.kt | 38 +-- .../features/messages/impl/MessagesView.kt | 1 - .../preview/AttachmentsPreviewView.kt | 9 +- .../impl/forward/ForwardMessagesView.kt | 5 +- .../messages/impl/report/ReportMessageView.kt | 4 +- .../onboarding/impl/OnBoardingView.kt | 40 +-- .../rageshake/impl/bugreport/BugReportView.kt | 7 +- .../impl/edit/RoomDetailsEditView.kt | 9 +- .../impl/invite/RoomInviteMembersView.kt | 4 +- .../impl/members/RoomMemberListView.kt | 10 +- .../components/RequestVerificationHeader.kt | 13 +- .../impl/VerifySelfSessionView.kt | 29 +- .../atomic/molecules/ButtonColumnMolecule.kt | 11 +- .../atomic/molecules/ButtonRowMolecule.kt | 9 +- .../designsystem/components/ProgressDialog.kt | 7 +- .../components/async/AsyncFailure.kt | 7 +- .../components/button/ButtonWithProgress.kt | 107 ------ .../components/dialogs/AlertDialogContent.kt | 40 +-- .../components/dialogs/ConfirmationDialog.kt | 9 +- .../components/dialogs/RetryDialog.kt | 59 +--- .../designsystem/ruler/WithRulers.kt | 10 +- .../designsystem/theme/components/Button.kt | 317 ++++++++++++++++-- .../theme/components/OutlinedButton.kt | 91 ----- .../theme/components/TextButton.kt | 76 ----- .../theme/components/previews/MenuPreview.kt | 5 +- ...otDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...otDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...otDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...tLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...tLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...tLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ViewPreview-D-0_1_null,NEXUS_5,1.0,en].png | 4 +- ...ViewPreview-N-0_2_null,NEXUS_5,1.0,en].png | 4 +- ...owDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...owDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...owDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...owDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...owDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_7,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_7,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...enPreview-D-0_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...enPreview-D-0_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...enPreview-D-0_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...enPreview-D-0_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...enPreview-N-0_1_null_0,NEXUS_5,1.0,en].png | 4 +- ...enPreview-N-0_1_null_1,NEXUS_5,1.0,en].png | 4 +- ...enPreview-N-0_1_null_2,NEXUS_5,1.0,en].png | 4 +- ...enPreview-N-0_1_null_3,NEXUS_5,1.0,en].png | 4 +- ...ViewDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...iewLightPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...tentDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...entLightPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +- ...stDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...tLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ationHeaderDark_0_null,NEXUS_5,1.0,en].png | 4 +- ...tionHeaderLight_0_null,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...culeDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...uleLightPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...culeDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...uleLightPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...lurePreviewDark_0_null,NEXUS_5,1.0,en].png | 4 +- ...urePreviewLight_0_null,NEXUS_5,1.0,en].png | 4 +- ...ProgressPreview_0_null,NEXUS_5,1.0,en].png | 3 - ...onDialogPreview_0_null,NEXUS_5,1.0,en].png | 3 + ...DialogPreview_0_null_0,NEXUS_5,1.0,en].png | 3 - ...DialogPreview_0_null_1,NEXUS_5,1.0,en].png | 3 - ...orDialogPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...ryDialogPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...ssDialogPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...ulerDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...lerLightPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...nus_MenuPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...s_ButtonPreview_0_null,NEXUS_5,1.0,en].png | 3 - ...tonLargePreview_0_null,NEXUS_5,1.0,en].png | 3 + ...onMediumPreview_0_null,NEXUS_5,1.0,en].png | 3 + ...tonLargePreview_0_null,NEXUS_5,1.0,en].png | 3 + ...onMediumPreview_0_null,NEXUS_5,1.0,en].png | 3 + ...dButtonsPreview_0_null,NEXUS_5,1.0,en].png | 3 - ...tonLargePreview_0_null,NEXUS_5,1.0,en].png | 3 + ...onMediumPreview_0_null,NEXUS_5,1.0,en].png | 3 + ...xtButtonPreview_0_null,NEXUS_5,1.0,en].png | 3 - ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ViewDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...iewLightPreview_0_null,NEXUS_5,1.0,en].png | 4 +- 228 files changed, 805 insertions(+), 950 deletions(-) create mode 100644 changelog.d/1021.misc delete mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonWithProgress.kt delete mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedButton.kt delete mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextButton.kt delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.button_null_Buttons_ButtonWithProgressPreview_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null_0,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null_1,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_ButtonPreview_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_FilledButtonLargePreview_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_FilledButtonMediumPreview_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonLargePreview_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonMediumPreview_0_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonsPreview_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonLargePreview_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonMediumPreview_0_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonPreview_0_null,NEXUS_5,1.0,en].png diff --git a/changelog.d/1021.misc b/changelog.d/1021.misc new file mode 100644 index 0000000000..ffb278b33d --- /dev/null +++ b/changelog.d/1021.misc @@ -0,0 +1 @@ +Add Button component based on Compound designs diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt index 5453daa0f9..817bf9a600 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt @@ -54,6 +54,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.text.buildAnnotatedStringWithStyledPart import io.element.android.libraries.designsystem.theme.components.Button +import io.element.android.libraries.designsystem.theme.components.ButtonSize import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton @@ -188,17 +189,16 @@ private fun AnalyticsOptInFooter( modifier = modifier, ) { Button( + text = stringResource(id = CommonStrings.action_ok), onClick = onTermsAccepted, modifier = Modifier.fillMaxWidth(), - ) { - Text(text = stringResource(id = CommonStrings.action_ok)) - } + ) TextButton( + text = stringResource(id = CommonStrings.action_not_now), + buttonSize = ButtonSize.Medium, onClick = onTermsDeclined, modifier = Modifier.fillMaxWidth(), - ) { - Text(text = stringResource(id = CommonStrings.action_not_now)) - } + ) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt index da1f43391b..e5f701036e 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleView.kt @@ -28,7 +28,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.dp import io.element.android.features.createroom.impl.R import io.element.android.features.createroom.impl.components.UserListView import io.element.android.features.createroom.impl.userlist.UserListEvents @@ -36,7 +35,6 @@ import io.element.android.features.createroom.impl.userlist.UserListState import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.theme.aliasButtonText import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text @@ -103,16 +101,11 @@ fun AddPeopleViewTopBar( }, navigationIcon = { BackButton(onClick = onBackPressed) }, actions = { + val textActionResId = if (hasSelectedUsers) CommonStrings.action_next else CommonStrings.action_skip TextButton( - modifier = Modifier.padding(horizontal = 8.dp), + text = stringResource(id = textActionResId), onClick = onNextPressed, - ) { - val textActionResId = if (hasSelectedUsers) CommonStrings.action_next else CommonStrings.action_skip - Text( - text = stringResource(id = textActionResId), - style = ElementTheme.typography.aliasButtonText, - ) - } + ) } ) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index c964fa1d57..d1f29559bd 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -57,7 +57,6 @@ import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.RetryDialog import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.theme.aliasButtonText import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text @@ -195,15 +194,10 @@ fun ConfigureRoomToolbar( navigationIcon = { BackButton(onClick = onBackPressed) }, actions = { TextButton( - modifier = Modifier.padding(horizontal = 8.dp), + text = stringResource(CommonStrings.action_create), enabled = isNextActionEnabled, onClick = onNextPressed, - ) { - Text( - text = stringResource(CommonStrings.action_create), - style = ElementTheme.typography.aliasButtonText, - ) - } + ) } ) } diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/welcome/WelcomeView.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/welcome/WelcomeView.kt index 7397e5ecc5..eebef5b3f2 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/welcome/WelcomeView.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/welcome/WelcomeView.kt @@ -97,9 +97,11 @@ fun WelcomeView( } }, footer = { - Button(modifier = Modifier.fillMaxWidth(), onClick = onContinueClicked) { - Text(text = stringResource(CommonStrings.action_continue)) - } + Button( + text = stringResource(CommonStrings.action_continue), + modifier = Modifier.fillMaxWidth(), + onClick = onContinueClicked + ) Spacer(modifier = Modifier.height(32.dp)) } ) diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt index e2e5927a66..a129668305 100644 --- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt +++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListView.kt @@ -86,7 +86,6 @@ fun InviteListView( title = stringResource(titleResource), submitText = stringResource(CommonStrings.action_decline), cancelText = stringResource(CommonStrings.action_cancel), - emphasizeSubmitButton = true, onSubmitClicked = { state.eventSink(InviteListEvents.ConfirmDeclineInvite) }, onDismiss = { state.eventSink(InviteListEvents.CancelDeclineInvite) } ) diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt index c2bbd5b023..a45c3c4808 100644 --- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt +++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt @@ -20,7 +20,6 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -49,8 +48,8 @@ import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAto import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.theme.aliasButtonText import io.element.android.libraries.designsystem.theme.components.Button +import io.element.android.libraries.designsystem.theme.components.ButtonSize import io.element.android.libraries.designsystem.theme.components.OutlinedButton import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.theme.ElementTheme @@ -133,23 +132,19 @@ internal fun DefaultInviteSummaryRow( // CTAs Row(Modifier.padding(top = 12.dp)) { OutlinedButton( - content = { Text(stringResource(CommonStrings.action_decline), style = ElementTheme.typography.aliasButtonText) }, + text = stringResource(CommonStrings.action_decline), onClick = onDeclineClicked, - modifier = Modifier - .weight(1f) - .heightIn(max = 36.dp), - contentPadding = PaddingValues(horizontal = 24.dp, vertical = 0.dp), + modifier = Modifier.weight(1f), + buttonSize = ButtonSize.Medium, ) Spacer(modifier = Modifier.width(12.dp)) Button( - content = { Text(stringResource(CommonStrings.action_accept), style = ElementTheme.typography.aliasButtonText) }, + text = stringResource(CommonStrings.action_accept), onClick = onAcceptClicked, - modifier = Modifier - .weight(1f) - .heightIn(max = 36.dp), - contentPadding = PaddingValues(horizontal = 24.dp, vertical = 0.dp), + modifier = Modifier.weight(1f), + buttonSize = ButtonSize.Medium, ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/dialogs/SlidingSyncNotSupportedDialog.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/dialogs/SlidingSyncNotSupportedDialog.kt index 5beb14b0b4..6ef5be06ab 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/dialogs/SlidingSyncNotSupportedDialog.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/dialogs/SlidingSyncNotSupportedDialog.kt @@ -35,7 +35,6 @@ internal fun SlidingSyncNotSupportedDialog( submitText = stringResource(CommonStrings.action_learn_more), onSubmitClicked = onLearnMoreClicked, onCancelClicked = onDismiss, - emphasizeSubmitButton = true, title = stringResource(CommonStrings.dialog_title_error), content = stringResource(R.string.screen_change_server_error_no_sliding_sync_message), ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt index faaabf38cd..239ee3c6ac 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderView.kt @@ -36,11 +36,10 @@ import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage -import io.element.android.libraries.designsystem.components.button.ButtonWithProgress import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.matrix.api.auth.OidcDetails import io.element.android.libraries.testtags.TestTags @@ -87,7 +86,7 @@ fun ConfirmAccountProviderView( }, footer = { ButtonColumnMolecule { - ButtonWithProgress( + Button( text = stringResource(id = R.string.screen_account_provider_continue), showProgress = isLoading, onClick = { eventSink.invoke(ConfirmAccountProviderEvents.Continue) }, @@ -97,14 +96,13 @@ fun ConfirmAccountProviderView( .testTag(TestTags.loginContinue) ) TextButton( + text = stringResource(id = R.string.screen_account_provider_change), onClick = onChange, enabled = true, modifier = Modifier .fillMaxWidth() .testTag(TestTags.loginChangeServer) - ) { - Text(text = stringResource(id = R.string.screen_account_provider_change)) - } + ) } } ) { diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt index d62506ff75..d06ab86b06 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/loginpassword/LoginPasswordView.kt @@ -60,11 +60,11 @@ import io.element.android.features.login.impl.error.loginError import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.components.button.BackButton -import io.element.android.libraries.designsystem.components.button.ButtonWithProgress import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.components.form.textFieldState import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.OutlinedTextField @@ -141,7 +141,7 @@ fun LoginPasswordView( // Flexible spacing to keep the submit button at the bottom Spacer(modifier = Modifier.weight(1f)) // Submit - ButtonWithProgress( + Button( text = stringResource(R.string.screen_login_submit), showProgress = isLoading, onClick = ::submit, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt index 73fbaf30f9..025a67516d 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/waitlistscreen/WaitListView.kt @@ -29,7 +29,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.foundation.layout.widthIn -import androidx.compose.material3.ButtonDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.BiasAbsoluteAlignment @@ -52,7 +51,6 @@ import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.components.dialogs.RetryDialog import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.theme.aliasButtonText import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.Text @@ -141,18 +139,10 @@ private fun WaitListContent( .padding(horizontal = 16.dp, vertical = 16.dp) ) { if (state.loginAction !is Async.Success) { - TextButton( - onClick = onCancelClicked, - colors = ButtonDefaults.buttonColors( - containerColor = Color.White, - contentColor = Color.Black, - disabledContainerColor = Color.White, - disabledContentColor = Color.Black, - ), - ) { - Text( + ElementTheme(darkTheme = true) { + TextButton( text = stringResource(CommonStrings.action_cancel), - style = ElementTheme.typography.aliasButtonText, + onClick = onCancelClicked, ) } } @@ -208,22 +198,14 @@ private fun WaitListContent( } } if (state.loginAction is Async.Success) { - Button( - onClick = { state.eventSink.invoke(WaitListEvents.Continue) }, - colors = ButtonDefaults.buttonColors( - containerColor = Color.White, - contentColor = Color.Black, - disabledContainerColor = Color.White, - disabledContentColor = Color.Black, - ), - modifier = Modifier - .fillMaxWidth() - .align(Alignment.BottomCenter) - .padding(bottom = 8.dp) - ) { - Text( + ElementTheme(darkTheme = true) { + Button( text = stringResource(id = CommonStrings.action_continue), - style = ElementTheme.typography.aliasButtonText, + onClick = { state.eventSink.invoke(WaitListEvents.Continue) }, + modifier = Modifier + .fillMaxWidth() + .align(Alignment.BottomCenter) + .padding(bottom = 8.dp), ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index b68a889384..34ac14af57 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -220,7 +220,6 @@ private fun ReinviteDialog(state: MessagesState) { content = stringResource(id = R.string.screen_room_invite_again_alert_message), cancelText = stringResource(id = CommonStrings.action_cancel), submitText = stringResource(id = CommonStrings.action_invite), - emphasizeSubmitButton = true, onSubmitClicked = { state.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) }, onDismiss = { state.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Cancel)) } ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt index ecdbc183b1..c696098fed 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewView.kt @@ -39,7 +39,6 @@ import io.element.android.libraries.designsystem.components.ProgressDialogType import io.element.android.libraries.designsystem.components.dialogs.RetryDialog import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.theme.components.Scaffold -import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.ui.strings.CommonStrings @@ -155,12 +154,8 @@ private fun AttachmentsPreviewBottomActions( ButtonRowMolecule( modifier = modifier, ) { - TextButton(onClick = onCancelClicked) { - Text(stringResource(id = CommonStrings.action_cancel)) - } - TextButton(onClick = onSendClicked) { - Text(stringResource(id = CommonStrings.action_send)) - } + TextButton(stringResource(id = CommonStrings.action_cancel), onClick = onCancelClicked) + TextButton(stringResource(id = CommonStrings.action_send), onClick = onSendClicked) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt index 1b9a3c5c99..0ff62d4788 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt @@ -123,11 +123,10 @@ fun ForwardMessagesView( }, actions = { TextButton( + text = stringResource(CommonStrings.action_send), enabled = state.selectedRooms.isNotEmpty(), onClick = { state.eventSink(ForwardMessagesEvents.ForwardEvent) } - ) { - Text(text = stringResource(CommonStrings.action_send)) - } + ) } ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt index 7924f07620..394bff6439 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt @@ -44,11 +44,11 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.components.button.BackButton -import io.element.android.libraries.designsystem.components.button.ButtonWithProgress import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.theme.aliasScreenTitle +import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.OutlinedTextField import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text @@ -150,7 +150,7 @@ fun ReportMessageView( Spacer(modifier = Modifier.height(24.dp)) - ButtonWithProgress( + Button( text = stringResource(CommonStrings.action_send), enabled = state.reason.isNotBlank() && !isSending, showProgress = isSending, diff --git a/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt b/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt index 0651d9cc2e..1adfe6bd93 100644 --- a/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt +++ b/features/onboarding/impl/src/main/kotlin/io/element/android/features/onboarding/impl/OnBoardingView.kt @@ -23,10 +23,8 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.QrCode -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.BiasAlignment @@ -42,9 +40,8 @@ import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMo import io.element.android.libraries.designsystem.atomic.pages.OnBoardingPage import io.element.android.libraries.designsystem.preview.DayNightPreviews import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.theme.aliasButtonText import io.element.android.libraries.designsystem.theme.components.Button -import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.IconSource import io.element.android.libraries.designsystem.theme.components.OutlinedButton import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.testtags.TestTags @@ -144,46 +141,27 @@ private fun OnBoardingButtons( } if (state.canLoginWithQrCode) { Button( + text = stringResource(id = R.string.screen_onboarding_sign_in_with_qr_code), + leadingIcon = IconSource.Vector(Icons.Default.QrCode), onClick = onSignInWithQrCode, - enabled = true, - modifier = Modifier - .fillMaxWidth() - ) { - Icon( - imageVector = Icons.Default.QrCode, contentDescription = null, - tint = MaterialTheme.colorScheme.onPrimary - ) - Spacer(Modifier.width(14.dp)) - Text( - text = stringResource(id = R.string.screen_onboarding_sign_in_with_qr_code), - style = ElementTheme.typography.aliasButtonText, - ) - } + modifier = Modifier.fillMaxWidth() + ) } Button( + text = stringResource(id = signInButtonStringRes), onClick = onSignIn, - enabled = true, modifier = Modifier .fillMaxWidth() .testTag(TestTags.onBoardingSignIn) - ) { - Text( - text = stringResource(id = signInButtonStringRes), - style = ElementTheme.typography.aliasButtonText, - ) - } + ) if (state.canCreateAccount) { OutlinedButton( + text = stringResource(id = R.string.screen_onboarding_sign_up), onClick = onCreateAccount, enabled = true, modifier = Modifier .fillMaxWidth() - ) { - Text( - text = stringResource(id = R.string.screen_onboarding_sign_up), - style = ElementTheme.typography.aliasButtonText, - ) - } + ) } Spacer(modifier = Modifier.height(16.dp)) } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt index 095f85e9c7..32d45b29ff 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt @@ -48,8 +48,8 @@ import io.element.android.libraries.designsystem.components.preferences.Preferen import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.debugPlaceholderBackground -import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator +import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.OutlinedTextField import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.LogCompositions @@ -149,14 +149,13 @@ fun BugReportView( // Submit PreferenceRow { Button( + text = stringResource(id = CommonStrings.action_send), onClick = { eventSink(BugReportEvents.SendBugReport) }, enabled = state.submitEnabled, modifier = Modifier .fillMaxWidth() .padding(top = 24.dp, bottom = 16.dp) - ) { - Text(text = stringResource(id = CommonStrings.action_send)) - } + ) } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt index 14a3f38d2f..cd0cbf878e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt @@ -68,7 +68,6 @@ import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.theme.aliasButtonText import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Scaffold @@ -115,17 +114,13 @@ fun RoomDetailsEditView( navigationIcon = { BackButton(onClick = onBackPressed) }, actions = { TextButton( + text = stringResource(CommonStrings.action_save), enabled = state.saveButtonEnabled, onClick = { focusManager.clearFocus() state.eventSink(RoomDetailsEditEvents.Save) }, - ) { - Text( - text = stringResource(CommonStrings.action_save), - style = ElementTheme.typography.aliasButtonText, - ) - } + ) } ) }, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt index b53800236e..8f01569f01 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersView.kt @@ -128,10 +128,8 @@ fun RoomInviteMembersTopBar( navigationIcon = { BackButton(onClick = onBackPressed) }, actions = { TextButton( + text = stringResource(CommonStrings.action_send), onClick = onSendPressed, - content = { - Text(stringResource(CommonStrings.action_send)) - }, enabled = canSend, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt index bad1dc3591..b0bac12d4e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt @@ -45,7 +45,6 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.theme.aliasButtonText import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.Scaffold @@ -212,14 +211,9 @@ private fun RoomMemberListTopBar( actions = { if (canInvite) { TextButton( - modifier = Modifier.padding(horizontal = 8.dp), + text = stringResource(CommonStrings.action_invite), onClick = onInvitePressed, - ) { - Text( - text = stringResource(CommonStrings.action_invite), - style = ElementTheme.typography.aliasButtonText, - ) - } + ) } } ) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt index 16eea28f20..8920efdeff 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt @@ -19,7 +19,6 @@ package io.element.android.features.roomlist.impl.components import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -37,7 +36,7 @@ import androidx.compose.ui.unit.dp import io.element.android.features.roomlist.impl.R import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.theme.aliasButtonText +import io.element.android.libraries.designsystem.theme.components.ButtonSize import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Surface @@ -83,15 +82,11 @@ internal fun RequestVerificationHeader( ) Spacer(modifier = Modifier.height(12.dp)) Button( + text = stringResource(CommonStrings.action_continue), + buttonSize = ButtonSize.Medium, modifier = Modifier.fillMaxWidth(), - contentPadding = PaddingValues(horizontal = 20.dp, vertical = 7.dp), onClick = onVerifyClicked, - ) { - Text( - stringResource(CommonStrings.action_continue), - style = ElementTheme.typography.aliasButtonText - ) - } + ) } } } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt index a116300dc6..0ffd518669 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionView.kt @@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.widthIn import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -44,15 +43,16 @@ import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage -import io.element.android.libraries.designsystem.components.button.ButtonWithProgress import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.theme.aliasButtonText +import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.matrix.api.verification.VerificationEmoji import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.coroutines.sync.Mutex import io.element.android.features.verifysession.impl.VerifySelfSessionState.VerificationStep as FlowStep @Composable @@ -75,6 +75,7 @@ fun VerifySelfSessionView( val buttonsVisible by remember(verificationFlowStep) { derivedStateOf { verificationFlowStep != FlowStep.AwaitingOtherDeviceResponse && verificationFlowStep != FlowStep.Completed } } + Mutex() HeaderFooterPage( modifier = modifier, header = { @@ -219,23 +220,21 @@ internal fun BottomMenu(screenState: VerifySelfSessionState, goBack: () -> Unit) ButtonColumnMolecule( modifier = Modifier.padding(bottom = 20.dp) ) { - ButtonWithProgress( - text = positiveButtonTitle?.let { stringResource(it) }, - showProgress = isVerifying, - modifier = Modifier.fillMaxWidth(), - onClick = { positiveButtonEvent?.let { eventSink(it) } } - ) + if (positiveButtonTitle != null) { + Button( + text = stringResource(positiveButtonTitle), + showProgress = isVerifying, + modifier = Modifier.fillMaxWidth(), + onClick = { positiveButtonEvent?.let { eventSink(it) } } + ) + } if (negativeButtonTitle != null) { TextButton( + text = stringResource(negativeButtonTitle), modifier = Modifier.fillMaxWidth(), onClick = negativeButtonCallback, enabled = negativeButtonEnabled, - ) { - Text( - text = stringResource(negativeButtonTitle), - style = ElementTheme.typography.aliasButtonText, - ) - } + ) } } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonColumnMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonColumnMolecule.kt index a8ad97a950..9fc688f227 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonColumnMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonColumnMolecule.kt @@ -28,7 +28,7 @@ import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.theme.components.Button -import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.OutlinedButton import io.element.android.libraries.designsystem.theme.components.TextButton @Composable @@ -59,11 +59,8 @@ internal fun ButtonColumnMoleculeDarkPreview() = @Composable private fun ContentToPreview() { ButtonColumnMolecule { - Button(onClick = {}, modifier = Modifier.fillMaxWidth()) { - Text(text = "Button") - } - TextButton(onClick = {}, modifier = Modifier.fillMaxWidth()) { - Text(text = "TextButton") - } + Button(text = "Button", onClick = {}, modifier = Modifier.fillMaxWidth()) + OutlinedButton(text = "OutlinedButton", onClick = {}, modifier = Modifier.fillMaxWidth()) + TextButton(text = "TextButton", onClick = {}, modifier = Modifier.fillMaxWidth()) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonRowMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonRowMolecule.kt index b8b5a2146f..7c03a2106e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonRowMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ButtonRowMolecule.kt @@ -25,7 +25,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton @Composable @@ -54,11 +53,7 @@ internal fun ButtonRowMoleculeDarkPreview() = @Composable private fun ContentToPreview() { ButtonRowMolecule { - TextButton(onClick = { }) { - Text("Button 1") - } - TextButton(onClick = { }) { - Text("Button 2") - } + TextButton(text = "Button 1", onClick = {}) + TextButton(text = "Button 2", onClick = {}) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt index a5ad996ea7..717f326779 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt @@ -129,9 +129,10 @@ private fun ProgressDialogContent( modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.BottomEnd ) { - TextButton(onClick = onCancelClicked) { - Text(stringResource(id = CommonStrings.action_cancel)) - } + TextButton( + text = stringResource(id = CommonStrings.action_cancel), + onClick = onCancelClicked, + ) } } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncFailure.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncFailure.kt index b5aa9b85d3..d30b78ca7f 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncFailure.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncFailure.kt @@ -48,9 +48,10 @@ fun AsyncFailure( Text(text = throwable.message ?: stringResource(id = CommonStrings.error_unknown)) if (onRetry != null) { Spacer(modifier = Modifier.height(24.dp)) - Button(onClick = onRetry) { - Text(text = stringResource(id = CommonStrings.action_retry)) - } + Button( + text = stringResource(id = CommonStrings.action_retry), + onClick = onRetry + ) } } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonWithProgress.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonWithProgress.kt deleted file mode 100644 index 06702de253..0000000000 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonWithProgress.kt +++ /dev/null @@ -1,107 +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.libraries.designsystem.components.button - -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.progressSemantics -import androidx.compose.material3.ButtonColors -import androidx.compose.material3.ButtonElevation -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.preview.ElementThemedPreview -import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.theme.aliasButtonText -import io.element.android.libraries.designsystem.theme.components.Button -import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator -import io.element.android.libraries.designsystem.theme.components.ElementButtonDefaults -import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme - -/** - * A component that will display a button with an indeterminate circular progressbar. - * When [showProgress] is true: - * - A circular progressbar is displayed. - * - [text] is replaced by [progressText], if defined. - * - [onClick] gets disabled. - */ -@Composable -fun ButtonWithProgress( - text: String?, - onClick: () -> Unit, - modifier: Modifier = Modifier, - showProgress: Boolean = false, - progressText: String? = text, - enabled: Boolean = true, - shape: Shape = ElementButtonDefaults.shape, - colors: ButtonColors = ElementButtonDefaults.buttonColors(), - elevation: ButtonElevation? = ElementButtonDefaults.buttonElevation(), - border: BorderStroke? = null, - contentPadding: PaddingValues = ElementButtonDefaults.ContentPadding, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, -) { - Button( - onClick = { - if (!showProgress) { - onClick() - } - }, - modifier = modifier, - enabled = enabled, - shape = shape, - colors = colors, - elevation = elevation, - border = border, - contentPadding = contentPadding, - interactionSource = interactionSource, - ) { - if (showProgress) { - CircularProgressIndicator( - modifier = Modifier - .progressSemantics() - .size(18.dp), - color = MaterialTheme.colorScheme.onPrimary, - strokeWidth = 2.dp, - ) - if (progressText != null) { - Spacer(Modifier.width(10.dp)) - Text(progressText, style = ElementTheme.typography.aliasButtonText) - } - } else if (text != null) { - Text(text, style = ElementTheme.typography.aliasButtonText) - } - } -} - -@Preview(group = PreviewGroup.Buttons) -@Composable -internal fun ButtonWithProgressPreview() = ElementThemedPreview { - ButtonWithProgress( - text = "Button with progress", - onClick = {}, - showProgress = true, - ) -} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/AlertDialogContent.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/AlertDialogContent.kt index c1e8b0c055..7b6179079e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/AlertDialogContent.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/AlertDialogContent.kt @@ -27,7 +27,6 @@ import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ProvideTextStyle import androidx.compose.material3.Surface -import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment @@ -38,7 +37,10 @@ import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.Placeable import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.theme.components.ButtonSize +import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.theme.ElementTheme import kotlin.math.max @@ -53,7 +55,6 @@ internal fun SimpleAlertDialogContent( onSubmitClicked: () -> Unit = {}, thirdButtonText: String? = null, onThirdButtonClicked: () -> Unit = {}, - emphasizeSubmitButton: Boolean = false, shape: Shape = AlertDialogDefaults.shape, containerColor: Color = AlertDialogDefaults.containerColor, iconContentColor: Color = AlertDialogDefaults.iconContentColor, @@ -71,30 +72,23 @@ internal fun SimpleAlertDialogContent( if (thirdButtonText != null) { // If there is a 3rd item it should be at the end of the dialog // Having this 3rd action is discouraged, see https://m3.material.io/components/dialogs/guidelines#e13b68f5-e367-4275-ad6f-c552ee8e358f - TextButton(onClick = onThirdButtonClicked) { - Text( - text = thirdButtonText, - style = ElementTheme.typography.fontBodyMdRegular, - ) - } - } - TextButton(onClick = onCancelClicked) { - Text( - text = cancelText, - style = ElementTheme.typography.fontBodyMdRegular, + TextButton( + text = thirdButtonText, + buttonSize = ButtonSize.Medium, + onClick = onThirdButtonClicked, ) } + TextButton( + text = cancelText, + buttonSize = ButtonSize.Medium, + onClick = onCancelClicked, + ) if (submitText != null) { - TextButton(onClick = onSubmitClicked) { - Text( - text = submitText, - style = if (emphasizeSubmitButton) { - ElementTheme.typography.fontBodyMdMedium - } else { - ElementTheme.typography.fontBodyMdRegular - } - ) - } + Button( + text = submitText, + buttonSize = ButtonSize.Medium, + onClick = onSubmitClicked, + ) } } }, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt index b11c9f878d..c5725456a3 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt @@ -26,11 +26,9 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.Dp import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.utils.BooleanProvider import io.element.android.libraries.ui.strings.CommonStrings @OptIn(ExperimentalMaterial3Api::class) @@ -44,7 +42,6 @@ fun ConfirmationDialog( submitText: String = stringResource(id = CommonStrings.action_ok), cancelText: String = stringResource(id = CommonStrings.action_cancel), thirdButtonText: String? = null, - emphasizeSubmitButton: Boolean = false, onCancelClicked: () -> Unit = onDismiss, onThirdButtonClicked: () -> Unit = {}, shape: Shape = AlertDialogDefaults.shape, @@ -71,7 +68,6 @@ fun ConfirmationDialog( titleContentColor = titleContentColor, textContentColor = textContentColor, tonalElevation = tonalElevation, - emphasizeSubmitButton = emphasizeSubmitButton, ) } } @@ -87,7 +83,6 @@ private fun ConfirmationDialogContent( title: String? = null, thirdButtonText: String? = null, onThirdButtonClicked: () -> Unit = {}, - emphasizeSubmitButton: Boolean = false, shape: Shape = AlertDialogDefaults.shape, containerColor: Color = AlertDialogDefaults.containerColor, iconContentColor: Color = AlertDialogDefaults.iconContentColor, @@ -106,7 +101,6 @@ private fun ConfirmationDialogContent( onCancelClicked = onCancelClicked, thirdButtonText = thirdButtonText, onThirdButtonClicked = onThirdButtonClicked, - emphasizeSubmitButton = emphasizeSubmitButton, shape = shape, containerColor = containerColor, iconContentColor = iconContentColor, @@ -119,7 +113,7 @@ private fun ConfirmationDialogContent( @Preview(group = PreviewGroup.Dialogs) @Composable -internal fun ConfirmationDialogPreview(@PreviewParameter(BooleanProvider::class) emphasizeSubmitButton: Boolean) = +internal fun ConfirmationDialogPreview() = ElementThemedPreview { DialogPreview { ConfirmationDialogContent( @@ -130,7 +124,6 @@ internal fun ConfirmationDialogPreview(@PreviewParameter(BooleanProvider::class) thirdButtonText = "Disable", onSubmitClicked = {}, onCancelClicked = {}, - emphasizeSubmitButton = emphasizeSubmitButton, ) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt index 5e22779085..5e680456cb 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt @@ -18,7 +18,7 @@ package io.element.android.libraries.designsystem.components.dialogs import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialogDefaults -import androidx.compose.material3.TextButton +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -28,10 +28,9 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup -import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings +@OptIn(ExperimentalMaterial3Api::class) @Composable fun RetryDialog( content: String, @@ -48,44 +47,22 @@ fun RetryDialog( textContentColor: Color = AlertDialogDefaults.textContentColor, tonalElevation: Dp = AlertDialogDefaults.TonalElevation, ) { - AlertDialog( - modifier = modifier, - onDismissRequest = onDismiss, - title = { - Text( - text = title, - style = ElementTheme.typography.fontHeadingSmRegular, - ) - }, - text = { - Text( - text = content, - style = ElementTheme.typography.fontBodyMdRegular, - ) - }, - confirmButton = { - TextButton(onClick = onRetry) { - Text( - text = retryText, - style = ElementTheme.typography.fontBodyMdRegular, - ) - } - }, - dismissButton = { - TextButton(onClick = onDismiss) { - Text( - text = dismissText, - style = ElementTheme.typography.fontBodyMdRegular, - ) - } - }, - shape = shape, - containerColor = containerColor, - iconContentColor = iconContentColor, - titleContentColor = titleContentColor, - textContentColor = textContentColor, - tonalElevation = tonalElevation, - ) + AlertDialog(modifier = modifier, onDismissRequest = onDismiss) { + RetryDialogContent( + title = title, + content = content, + retryText = retryText, + dismissText = dismissText, + onRetry = onRetry, + onDismiss = onDismiss, + shape = shape, + containerColor = containerColor, + iconContentColor = iconContentColor, + titleContentColor = titleContentColor, + textContentColor = textContentColor, + tonalElevation = tonalElevation, + ) + } } @Composable diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt index a5fc895e5d..e413dff1f2 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt @@ -24,8 +24,8 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.theme.components.ButtonSize import io.element.android.libraries.designsystem.theme.components.OutlinedButton -import io.element.android.libraries.designsystem.theme.components.Text /** * Debug tool to add a vertical and a horizontal ruler on top of the content. @@ -76,8 +76,10 @@ internal fun WithRulerDarkPreview() = @Composable private fun ContentToPreview() { WithRulers(xRulersOffset = 20.dp, yRulersOffset = 15.dp) { - OutlinedButton(onClick = {}) { - Text(text = "A Button with rulers on it!") - } + OutlinedButton( + text = "A Button with rulers on it!", + buttonSize = ButtonSize.Medium, + onClick = {}, + ) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt index 64c79a3906..59307bfb6b 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt @@ -18,68 +18,323 @@ package io.element.android.libraries.designsystem.theme.components import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.RowScope -import androidx.compose.material3.ButtonColors +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.progressSemantics +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Share import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.ButtonElevation +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.theme.ElementTheme + +// Designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&mode=design&t=U03tOFZz5FSLVUMa-1 @Composable fun Button( + text: String, onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, - shape: Shape = ElementButtonDefaults.shape, - colors: ButtonColors = ElementButtonDefaults.buttonColors(), - elevation: ButtonElevation? = ElementButtonDefaults.buttonElevation(), - border: BorderStroke? = null, - contentPadding: PaddingValues = ElementButtonDefaults.ContentPadding, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - content: @Composable RowScope.() -> Unit + buttonSize: ButtonSize = ButtonSize.Large, + showProgress: Boolean = false, + leadingIcon: IconSource? = null, +) = ButtonInternal(text, onClick, ButtonStyle.Filled, modifier, enabled, buttonSize, showProgress, leadingIcon) + +@Composable +fun OutlinedButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + buttonSize: ButtonSize = ButtonSize.Large, + showProgress: Boolean = false, + leadingIcon: IconSource? = null, +) = ButtonInternal(text, onClick, ButtonStyle.Outlined, modifier, enabled, buttonSize, showProgress, leadingIcon) + +@Composable +fun TextButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + buttonSize: ButtonSize = ButtonSize.Large, + showProgress: Boolean = false, + leadingIcon: IconSource? = null, +) = ButtonInternal(text, onClick, ButtonStyle.Text, modifier, enabled, buttonSize, showProgress, leadingIcon) + +@Composable +private fun ButtonInternal( + text: String, + onClick: () -> Unit, + style: ButtonStyle, + modifier: Modifier = Modifier, + enabled: Boolean = true, + size: ButtonSize = ButtonSize.Large, + showProgress: Boolean = false, + leadingIcon: IconSource? = null, ) { + val minHeight = when (size) { + ButtonSize.Medium -> 40.dp + ButtonSize.Large -> 48.dp + } + + val paddingValues = when (size) { + ButtonSize.Medium -> { + when (style) { + ButtonStyle.Text -> PaddingValues(horizontal = 12.dp, vertical = 10.dp) + else -> PaddingValues(horizontal = 16.dp, vertical = 10.dp) + } + } + ButtonSize.Large -> { + when (style) { + ButtonStyle.Text -> PaddingValues(horizontal = 16.dp, vertical = 13.dp) + else -> PaddingValues(horizontal = 24.dp, vertical = 13.dp) + } + } + } + + val shape = when (style) { + ButtonStyle.Filled, ButtonStyle.Outlined -> RoundedCornerShape(percent = 50) + ButtonStyle.Text -> RectangleShape + } + + val colors = when (style) { + ButtonStyle.Filled -> ButtonDefaults.buttonColors( + containerColor = ElementTheme.materialColors.primary, + contentColor = ElementTheme.materialColors.onPrimary, + disabledContainerColor = ElementTheme.colors.bgActionPrimaryDisabled, + disabledContentColor = ElementTheme.colors.textOnSolidPrimary + ) + ButtonStyle.Outlined, ButtonStyle.Text -> ButtonDefaults.buttonColors( + containerColor = Color.Transparent, + contentColor = ElementTheme.materialColors.primary, + disabledContainerColor = Color.Transparent, + disabledContentColor = ElementTheme.colors.textDisabled, + ) + } + + val border = when (style) { + ButtonStyle.Filled, ButtonStyle.Text -> null + ButtonStyle.Outlined -> BorderStroke( + width = 1.dp, + color = ElementTheme.colors.borderInteractiveSecondary + ) + } + + val textStyle = when (size) { + ButtonSize.Medium -> MaterialTheme.typography.labelLarge + ButtonSize.Large -> ElementTheme.typography.fontBodyLgMedium + } + androidx.compose.material3.Button( - onClick = onClick, - modifier = modifier, + onClick = { + if (!showProgress) { + onClick() + } + }, + modifier = modifier.heightIn(min = minHeight), enabled = enabled, shape = shape, colors = colors, - elevation = elevation, + elevation = null, border = border, - contentPadding = contentPadding, - interactionSource = interactionSource, - content = content, - ) + contentPadding = paddingValues, + interactionSource = remember { MutableInteractionSource() }, + ) { + when { + showProgress -> { + CircularProgressIndicator( + modifier = Modifier + .progressSemantics() + .size(20.dp), + color = LocalContentColor.current, + strokeWidth = 2.dp, + ) + } + leadingIcon != null -> { + androidx.compose.material.Icon( + painter = leadingIcon.getPainter(), + contentDescription = null, + tint = LocalContentColor.current, + modifier = Modifier.size(20.dp), + ) + } + else -> Unit + } + Text( + text = text, + style = textStyle, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.padding(horizontal = 8.dp), + ) + } } -object ElementButtonDefaults { - val ContentPadding = PaddingValues(horizontal = 24.dp, vertical = 14.dp) - val shape: Shape @Composable get() = ButtonDefaults.shape - @Composable - fun buttonElevation(): ButtonElevation = ButtonDefaults.buttonElevation() +sealed interface IconSource { + data class Resource(val id: Int) : IconSource + data class Vector(val vector: ImageVector) : IconSource @Composable - fun buttonColors(): ButtonColors = ButtonDefaults.buttonColors() + fun getPainter(): Painter = when (this) { + is Resource -> painterResource(id) + is Vector -> rememberVectorPainter(image = vector) + } +} +enum class ButtonSize { + Medium, Large +} + +private enum class ButtonStyle { + Filled, Outlined, Text } @Preview(group = PreviewGroup.Buttons) @Composable -internal fun ButtonPreview() = ElementThemedPreview { - Column { - Button(onClick = {}, enabled = true) { - Text(text = "Click me! - Enabled") - } - Button(onClick = {}, enabled = false) { - Text(text = "Click me! - Disabled") +internal fun FilledButtonMediumPreview() { + ButtonCombinationPreview( + buttonStyle = ButtonStyle.Filled, + buttonSize = ButtonSize.Medium, + ) +} + +@Preview(group = PreviewGroup.Buttons) +@Composable +internal fun FilledButtonLargePreview() { + ButtonCombinationPreview( + buttonStyle = ButtonStyle.Filled, + buttonSize = ButtonSize.Large, + ) +} + +@Preview(group = PreviewGroup.Buttons) +@Composable +internal fun OutlinedButtonMediumPreview() { + ButtonCombinationPreview( + buttonStyle = ButtonStyle.Outlined, + buttonSize = ButtonSize.Medium, + ) +} + +@Preview(group = PreviewGroup.Buttons) +@Composable +internal fun OutlinedButtonLargePreview() { + ButtonCombinationPreview( + buttonStyle = ButtonStyle.Outlined, + buttonSize = ButtonSize.Large, + ) +} + +@Preview(group = PreviewGroup.Buttons) +@Composable +internal fun TextButtonMediumPreview() { + ButtonCombinationPreview( + buttonStyle = ButtonStyle.Text, + buttonSize = ButtonSize.Medium, + ) +} + +@Preview(group = PreviewGroup.Buttons) +@Composable +internal fun TextButtonLargePreview() { + ButtonCombinationPreview( + buttonStyle = ButtonStyle.Text, + buttonSize = ButtonSize.Large, + ) +} + +@Composable +private fun ButtonCombinationPreview( + buttonStyle: ButtonStyle, + buttonSize: ButtonSize, + modifier: Modifier = Modifier, +) { + ElementThemedPreview { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier + .padding(16.dp) + .width(IntrinsicSize.Max), + ) { + // Normal + ButtonRowPreview( + modifier = Modifier.then(modifier), + buttonStyle = buttonStyle, + buttonSize = buttonSize, + ) + + // With icon + ButtonRowPreview( + modifier = Modifier.then(modifier), + leadingIcon = IconSource.Vector(Icons.Outlined.Share), + buttonStyle = buttonStyle, + buttonSize = buttonSize, + ) + + // With progress + ButtonRowPreview( + modifier = Modifier.then(modifier), + showProgress = true, + buttonStyle = buttonStyle, + buttonSize = buttonSize, + ) } } } + +@Composable +private fun ButtonRowPreview( + buttonStyle: ButtonStyle, + buttonSize: ButtonSize, + modifier: Modifier = Modifier, + leadingIcon: IconSource? = null, + showProgress: Boolean = false, +) { + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally)) { + ButtonInternal( + text = "A button", + showProgress = showProgress, + onClick = {}, + style = buttonStyle, + size = buttonSize, + leadingIcon = leadingIcon, + modifier = Modifier.then(modifier), + ) + ButtonInternal( + text = "A button", + showProgress = showProgress, + enabled = false, + onClick = {}, + style = buttonStyle, + size = buttonSize, + leadingIcon = leadingIcon, + modifier = Modifier.then(modifier), + ) + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedButton.kt deleted file mode 100644 index fa7ee261f6..0000000000 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/OutlinedButton.kt +++ /dev/null @@ -1,91 +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.libraries.designsystem.theme.components - -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.RowScope -import androidx.compose.material3.ButtonColors -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.ButtonElevation -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.preview.ElementThemedPreview -import io.element.android.libraries.designsystem.preview.PreviewGroup - -@Composable -fun OutlinedButton( - onClick: () -> Unit, - modifier: Modifier = Modifier, - enabled: Boolean = true, - shape: Shape = ElementOutlinedButtonDefaults.shape, - colors: ButtonColors = ElementOutlinedButtonDefaults.buttonColors(), - elevation: ButtonElevation? = ElementOutlinedButtonDefaults.buttonElevation(), - border: BorderStroke? = ElementOutlinedButtonDefaults.border, - contentPadding: PaddingValues = ElementOutlinedButtonDefaults.ContentPadding, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - content: @Composable RowScope.() -> Unit -) { - androidx.compose.material3.Button( - onClick = onClick, - modifier = modifier, - enabled = enabled, - shape = shape, - colors = colors, - elevation = elevation, - border = border, - contentPadding = contentPadding, - interactionSource = interactionSource, - content = content, - ) -} - -object ElementOutlinedButtonDefaults { - val ContentPadding = PaddingValues(horizontal = 24.dp, vertical = 14.dp) - val shape: Shape @Composable get() = ButtonDefaults.outlinedShape - val border: BorderStroke @Composable get() = ButtonDefaults.outlinedButtonBorder - @Composable - fun buttonElevation(): ButtonElevation = ButtonDefaults.buttonElevation() - - @Composable - fun buttonColors(): ButtonColors = ButtonDefaults.outlinedButtonColors() - - -} - -@Preview(group = PreviewGroup.Buttons) -@Composable -internal fun OutlinedButtonsPreview() = ElementThemedPreview { ContentToPreview() } - -@Composable -private fun ContentToPreview() { - Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { - OutlinedButton(onClick = {}, enabled = true) { - Text(text = "Click me! - Enabled") - } - OutlinedButton(onClick = {}, enabled = false) { - Text(text = "Click me! - Disabled") - } - } -} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextButton.kt deleted file mode 100644 index 3b4b50a5e7..0000000000 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextButton.kt +++ /dev/null @@ -1,76 +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.libraries.designsystem.theme.components - -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.RowScope -import androidx.compose.material3.ButtonColors -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.ButtonElevation -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.tooling.preview.Preview -import io.element.android.libraries.designsystem.preview.ElementThemedPreview -import io.element.android.libraries.designsystem.preview.PreviewGroup - -@Composable -fun TextButton( - onClick: () -> Unit, - modifier: Modifier = Modifier, - enabled: Boolean = true, - shape: Shape = ButtonDefaults.textShape, - colors: ButtonColors = ButtonDefaults.textButtonColors(), - elevation: ButtonElevation? = null, - border: BorderStroke? = null, - contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - content: @Composable RowScope.() -> Unit -) { - androidx.compose.material3.TextButton( - onClick = onClick, - modifier = modifier, - enabled = enabled, - shape = shape, - colors = colors, - elevation = elevation, - border = border, - contentPadding = contentPadding, - interactionSource = interactionSource, - content = content, - ) -} - -@Preview(group = PreviewGroup.Buttons) -@Composable -internal fun TextButtonPreview() = ElementThemedPreview { ContentToPreview() } - -@Composable -private fun ContentToPreview() { - Column { - TextButton(onClick = {}, enabled = true) { - Text(text = "Click me! - Enabled") - } - TextButton(onClick = {}, enabled = false) { - Text(text = "Click me! - Disabled") - } - } -} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt index f1c2cd444d..8e7da6926e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt @@ -32,16 +32,13 @@ import io.element.android.libraries.designsystem.theme.components.DropdownMenu import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem import io.element.android.libraries.designsystem.theme.components.DropdownMenuItemText import io.element.android.libraries.designsystem.theme.components.Icon -import io.element.android.libraries.designsystem.theme.components.Text @Preview(group = PreviewGroup.Menus) @Composable internal fun MenuPreview() { ElementThemedPreview { var isExpanded by remember { mutableStateOf(false) } - Button(onClick = { isExpanded = !isExpanded }) { - Text("Toggle") - } + Button(text = "Toggle", onClick = { isExpanded = !isExpanded }) DropdownMenu(expanded = isExpanded, onDismissRequest = { isExpanded = false }) { for (i in 0..5) { val leadingIcon: @Composable (() -> Unit)? = if (i in 2..3) { diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_0,NEXUS_5,1.0,en].png index a4d544ce2d..ef042a9cbc 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c51726746993ad3ed0f6da9fe49a02b87ec518495e04f6cdb28ab4454dc72938 -size 25457 +oid sha256:46791d70473aca73286b0e4474bd0b649c918005516e5bc9b6a3bbdb9a8f5fd2 +size 26694 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_1,NEXUS_5,1.0,en].png index c5627b9105..6ced266412 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:14ffd433679cbadf1bb325257786a834fce3e362d2ed763c1823b0c958c0f38a -size 27496 +oid sha256:bcbe69c4bdba925ca2e5926dfb916d9f83e5c17717b8fc6d3665f87ceb32d23d +size 28738 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_2,NEXUS_5,1.0,en].png index c4f2de8348..458c56746a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:85d3ea85f270fae4db7e14ab9c2c19a6c4f3a68e1c2112010471cb69cdb71b2b -size 22071 +oid sha256:67a4bb7f73aeb7e8f014cf33147c08b6b95fc2f15c8b843c9d83c9bf24fa150a +size 22026 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_0,NEXUS_5,1.0,en].png index 1aecdf6d10..dc5da9d55a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd79483dea8ce19284069511ec96dfc68704a9ff2f1a33be312bbcdb387123c6 -size 26655 +oid sha256:4417dad9a43b1759acb558213bb87538535a7e9a2b87c7292b4531d77551d7c8 +size 27993 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_1,NEXUS_5,1.0,en].png index 192e7b90a4..34177a4f02 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:931423fea194fb1bede5cb3d73083fb65335716ca088fbda707f4f4727eae4dd -size 28757 +oid sha256:92376ec0846541af886963486f25a1b6de3db6d318ca5088098b322d24255570 +size 30079 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_2,NEXUS_5,1.0,en].png index 7ae3d51e4a..eb1a23dd1a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bfa6e3d8698a2327d6e3cc14318856f221355581e6c661b92d2e215daf8f30cd -size 22879 +oid sha256:c46a163c5b47489334a8a9475fc2a39e4682081023e21a326f2cbc2e88f035e0 +size 22852 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.analytics.impl_null_DefaultGroup_AnalyticsOptInViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.analytics.impl_null_DefaultGroup_AnalyticsOptInViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 02064ef869..482d85290c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.analytics.impl_null_DefaultGroup_AnalyticsOptInViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.analytics.impl_null_DefaultGroup_AnalyticsOptInViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:434f7619fb6bd337ede775fc343acc05950907987523acbd0f578624e5d26857 -size 49246 +oid sha256:5fd66b90f0566317a48565be56e75496a7b156fc96354ea61a0cc0a7f036625a +size 49329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.analytics.impl_null_DefaultGroup_AnalyticsOptInViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.analytics.impl_null_DefaultGroup_AnalyticsOptInViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 293c11b5c8..9dc5bdf600 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.analytics.impl_null_DefaultGroup_AnalyticsOptInViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.analytics.impl_null_DefaultGroup_AnalyticsOptInViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93036a30e1af96805c20ea5d65b6d3eb40d2ee11f8f3420436e936dd5b4ca38b -size 50201 +oid sha256:5b6dc784e88a7c9ef9d5812ee474e8cbc5cafc55dfd21adeb15d3ace99a41db0 +size 50340 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 772474c81f..1389cf753d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:92fda75e246b46ab786da2f54f920bf96959618ac20cece5f6ca754f7fcf6914 -size 14161 +oid sha256:ae8be2032172fbda58c917e00401f59a4c6363dee0bb8304f6a0bba18abbb67d +size 14135 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index b88f3011ca..064c330088 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2a005b84bbf1be15028d36e153e79eb5acbe41e65e3f274a6d78b22be95efb5 -size 28509 +oid sha256:427314c0d0749e03b8639b6b0e15f294a8090b392f297e6a5a1714b77d3b174c +size 28520 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 7cfc3e1c03..7ef3674a20 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6bb2d3d6e83f5b0ca17809c415e636bf5dbfa846259e8fabd6c3afae37d7d1f9 -size 15231 +oid sha256:af55dec154a30df77baa8f2e8ebfe2787cc99ac0d717274e319ea89c3b46d6eb +size 15206 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 965959b5ee..9affad216c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3f7631f15054ac55688f76805a6f1924e79058881957b67284865b6508886cc -size 29249 +oid sha256:c69b2b87544bccb46590aafcdd638e8fa4c81c6c3ca53052cede42e1926301f3 +size 29275 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 80cfcc631c..51c26f2a7b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3cb08dc14db764d1bc432dc58374cad50da83c5e30e7e48077b6f400aa7a3c9b -size 57811 +oid sha256:e5bdcb1947e4f2c40d48ce69d2d7218f3aed7a96706a36f62eb6ee8cf18123f1 +size 58121 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 9dcb049366..a703dff4dd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d0a9c923401980b00b756159f9c77f153892b79cc2b806150734b6bd8f67fe4d -size 83675 +oid sha256:8ee9b90232d573c09c824cbf63673cfca3e0a6823cd86914b4ea798e13b3d860 +size 83688 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index eb7fecd069..cef9210e1c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e543f94df5f2de717e7e870fe043ecf72b29aeaca23d33b9b16af76e2cc1636 -size 61222 +oid sha256:5411b9f81d37d7834eb7c50c59cb2bc1686f5f706fe96952147515ef1c041a62 +size 61494 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 81b39fcbeb..fefba0ee23 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1b94a9fdcf1f2c62fa2a0f574210b85de96ba068ba17ba562af79f21d7a38f8f -size 86854 +oid sha256:1395d6bfe4258c13cbc76711505bc58666064777f0219d8d89203ebb313c3bb0 +size 86882 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 49d7942204..4d54d1492d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5312bd01a41dc0d1b022b6ce7258435d1cd9de75bd4231ce7f86ed6f84de2e4b -size 26306 +oid sha256:cb391a409ce4174127ad8ad4961975092f92a24224381a0c8c5e9940eb555235 +size 27629 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index bd930af4d9..ae86a46363 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1b9b88deb5557da8b4670231ee41aa737543244d61abfd399d7de0f6d4496dde -size 27439 +oid sha256:97ffdf54dd223794d0ccc013b773b381d8e047f6854a927bf5b20b67b4c75015 +size 28863 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-D-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-D-0_1_null,NEXUS_5,1.0,en].png index a72f564891..14e58b56ce 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-D-0_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-D-0_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1387a1337da70f8e87474aef106110f0dfb55e59f340e0906390e509da1dd0b4 -size 299376 +oid sha256:6d2eab27a1132893542b240399a4003b4049b72903a822077f69dd9dafd26f71 +size 299106 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-N-0_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-N-0_2_null,NEXUS_5,1.0,en].png index a12f3bceee..d38be285ba 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-N-0_2_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.ftue.impl.welcome_null_DefaultGroup_WelcomeViewPreview-N-0_2_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cdf0215f1ba1f6a89a6204e19b3df7dec1e64d7fd71bdf8c706e1969e11e702d -size 404366 +oid sha256:fbad2f74e329a424e75ef96cb0c427a7baba044f3267346756324c39ebd6add6 +size 404196 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png index ddc9420223..d4db900fe9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07e722c2936e1332168319059d5b4a553b1fc7f03da8aff40b005a05b203632d -size 28679 +oid sha256:7ee847a64a963e1536e8fa118516cb58dd3ef450decbaafda3cae067bd44e8c4 +size 28447 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png index cfe7f4094d..22db922bfd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eab30f7ec6ad8e289f4987d23f373ca8f96220a5d0762d46723058bca53d0a41 -size 33580 +oid sha256:0aa12e1e306c98dc03ce08acf178624c17622546ab32d93571d0e6f73530e95a +size 33101 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_2,NEXUS_5,1.0,en].png index f0155d216f..07f66bcad7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a344ef3f77b759e1d43733f85fc7ee9bee98d6eb3980ce13c435bb33c0703f0 -size 33720 +oid sha256:ad431cfafb873a27010ef2f5191efbbb7a7d43518f3fd72cc1d93bde3fbc5db4 +size 33246 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_3,NEXUS_5,1.0,en].png index a38e7f59a4..5a0c58a0f6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6e5874a0e224636c5ad13f4022e8f2ef3dff97206a5551734de560fbc40c562a -size 14447 +oid sha256:f8edf269361969e35486f0bfdfcfe99c69c82adbeea87c5ccc7af244a68dfdbe +size 14022 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 8f6d7d3782..0a3714c60a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e526f418f5c8d1d0c51509808e22548ce3c295752e7a1cf28cfe394fccc01f4 -size 28834 +oid sha256:36dbf0c8406baed1c3125173b0113e071e28e8370b7fe6ec7887687e59e98035 +size 28612 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_0,NEXUS_5,1.0,en].png index 29cf018c14..76d31c9467 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:97639d0985e0303e3a58120fbb7a2bba67b4279d198140a9f4cf6b19aa74bed0 -size 29199 +oid sha256:d7c5c16830d017da9d76cdf9469fe7e2d7e718b5d1cb2408e15fdcf773d707d6 +size 29132 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_1,NEXUS_5,1.0,en].png index 1acc83ec3c..8560c591fe 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0372c5389850f853652ad40ad54069e456aa33e13335e733d779a8d4d384049 -size 35297 +oid sha256:791944236c8027c25bad58e2a6db65a72d310fdc54095ed7475701a9fad689c7 +size 35006 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_2,NEXUS_5,1.0,en].png index ddb0df10ed..07cd0f89ce 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:62f57ee9fc205539ede541baf8e4b887ef7776675c4760acd5b27545cd021df8 -size 35420 +oid sha256:adfadfbbcd6920bca70c435cbb68c83e18cfd9e15eac4545fa5f1e404b258255 +size 35121 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_3,NEXUS_5,1.0,en].png index 0b5b749dfd..4b96dac84e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea79f0de34ba0613fea7ba98c65f5ff925a336ddf2d6734ea77b1a2d559527f0 -size 14416 +oid sha256:1694a5589d39a7b4d7fcc823a0d6b2c86e9bb9c759ace8e5790df85ebc7ea3af +size 14167 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_4,NEXUS_5,1.0,en].png index d7bead23cf..cee0955c2c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl.components_null_DefaultGroup_InviteSummaryRowLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b13ebc73e257d5f588f9e74d0a35bc9af951faece57556636378914786429e3 -size 29503 +oid sha256:5575ca96bb0b925109b5aad2207c304a0d1d54e8eeace250369e23982f23ec09 +size 29424 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index bbd5173671..62e1d90606 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5fcd5bd88a1aea468c8f5e08a3e07a323bd0e76f8db913611929c80184c2f68c -size 53541 +oid sha256:014ace4752ba1f47272fd5320aa567a3b825c159548b36a1cdea34eb933cf0f0 +size 52332 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 418e6be50b..a16df32332 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3a5f5dcad110d0fe9cc1e81537319feac59c7d25084d9b77ed22c44969a7237 -size 48499 +oid sha256:7fa8aed2bf9fd7b52feeecee0b6a754f5d4d6ac7874812582af6f62d57e8386d +size 50351 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index f3aeeb5b53..d5b81b4310 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee4f54670538988698e947dd42d6d3bc585f743684e83202d48f8d02d1bdf1f8 -size 49214 +oid sha256:55f3f0dad422de2490c3685530684ef4b94e03600a9a6f477a7841f64caacd50 +size 51093 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index f669883f45..71cf000cdc 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:442ff73662328311d0f8d9ef0000bf2be3b6141810b26f864847217081900411 -size 41007 +oid sha256:a01b90afcca784a2ebd08e7645cc6caf296c03c37ca6e2fa295ce8ca8b907d8b +size 41481 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index f669883f45..71cf000cdc 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:442ff73662328311d0f8d9ef0000bf2be3b6141810b26f864847217081900411 -size 41007 +oid sha256:a01b90afcca784a2ebd08e7645cc6caf296c03c37ca6e2fa295ce8ca8b907d8b +size 41481 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index e123bbbe3f..23026cee2b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f20e6a88d40320286d73dfa346a02783a00b1fb4a10c804c2f147e2f909c5a2a -size 56083 +oid sha256:0219335bf2f3be0a4a785d7ca2e05700278401aa090d5972a76ce077cfbe5be6 +size 55406 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 816e5fe57d..b6f3e9b164 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c25bf522377a036e8faae35e8afe458d82cf3c761e2df237a1a4d5785f44e188 -size 50202 +oid sha256:b877305884353e5cf2b95cdbaad90a60bec8508b98ca702ea63272dde221667c +size 51997 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index a57d29443c..0adcc297e5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:904a991dd0b753af68f0ab626d1f236dc9ea273a35f100475cadad8f4332d25d -size 50896 +oid sha256:9b8a1fbf13bdc64c63ddffab55137c7c313630e11bd6c20da07593d8cc5460cc +size 52593 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index d61deff1b1..8cb8c5a578 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98ff0c7db1cd224a6d9897b1daf49f57fc659b25e60a9d670e8b1c66811d4fed -size 42505 +oid sha256:c0d554cf60b5c6c6a58e1793c1155605c4c17606a0dfc92c8f4e9d39bab5bc56 +size 43014 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index d61deff1b1..8cb8c5a578 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98ff0c7db1cd224a6d9897b1daf49f57fc659b25e60a9d670e8b1c66811d4fed -size 42505 +oid sha256:c0d554cf60b5c6c6a58e1793c1155605c4c17606a0dfc92c8f4e9d39bab5bc56 +size 43014 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index d36f847e7b..cb0e85f506 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42d22676a81b8c1f313f2f21814b787c54fa52f8b344207b509fbb798f2f58a1 -size 20075 +oid sha256:129144d77e162fb3263884191e240a3cb3b259501f69d0b346a73bd5273b9f66 +size 21290 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 292f5d9f30..b41e16aeb7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:34645175d5acb2915d04b9306d648a287cae769bffcae7ecaffd293d43580683 -size 29023 +oid sha256:20191d257eb9db133382d8ca3ed15980bf4950a3583947a4145e1393509a70ac +size 30395 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index fe795d5c26..ea8435fda4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56f203dce2ed8ff23a75eb66bcb116fb4af61b63b1ad095ecebf6d97ba557a2c -size 31392 +oid sha256:d9790e38cc340cbaf2db8fc72578f7f3ad4760b132acb3bee1e82c9cb41ccefd +size 32710 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 1393c4aa2a..4fcb063611 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d23fb856aa3402cebf5c976fe32f0475f2f58b37a9ac9d028b0164dae4de397 -size 13357 +oid sha256:c57da4d726af9971ec746d01fb2791ce0ffbb7e8a643ea49fcdccf2c60c60579 +size 13350 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 7ab75f845d..930ac7b554 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e748443031dd55fdf60f13debd2484f8169b7eb23980786ad73898004c7bcbfe -size 19615 +oid sha256:1287b345aec7c250f9f47722d3dc3ab6979f0311ee35916d013545f1675aad1d +size 20912 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 7b458addc7..014432348c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c2419ccf59c98899071e705b63feb83e8976acca53ccb8f375551aa056f9950 -size 29204 +oid sha256:f3401af59f4fcdc85914f00080029114d56dd2d5808ee5ef0cd2f36e9e3977b6 +size 30649 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index c64c365b53..d76ee1dcce 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:598f0a995896c32b1755951779f3784dbbe5062b7039bb8952d383acf2399efd -size 31871 +oid sha256:fd82812f25b2a11e280cab9606b88f48dc88ec45a2067742582ba82d9426d738 +size 33345 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index c4d8cc0f03..ca129e97f3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bf0bc49ba28e3e31414b1d1eb177c119529bc7c95209a8bbefafa6fc07a6fba -size 12603 +oid sha256:6445a52dfcf1c34488fc60a30eec00e467a300dc5d4766498456a7a3b8ff93ea +size 12576 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png index 84cc8df088..4cd0b5b3f2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb559f5cd8b391ab4406c903a59376061255142317b035bd084f153779036eb9 -size 36589 +oid sha256:64a4a845c409c02afb7944a0e60566ed3f8ab5a2a6c9ad2dbf4f068df9ed1a7d +size 37965 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png index a489609443..7f826958be 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae73b980357c9def721335ecd69ffb8d921963d422e38c84735f53b6f4a596f2 -size 35101 +oid sha256:d436ecedf88500e967d39adf4ee1878c956efc068c0ac5dd8b4a327cd208821e +size 36509 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png index b3f1ae991d..9ce6529df8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fac71b82a06f65e799ca345cc9b2e22af6c0000b9900bdcdc584ca6b0f6c2c4d -size 34178 +oid sha256:ba54e1084b5eeec31a86c8ed77e0e60dee7696e910eee3d03a794b6771b5c6be +size 35443 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png index 521af2091c..f5d9f7d328 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e66b4ae355ae37f7810db1923c4018a44b1056c6471a3ed675032f036d30ff34 -size 32685 +oid sha256:22c9cb863c50b76ee4d95fe7def73c9cbf15d3754fc708fb8e85078e31fdd722 +size 33936 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.confirmaccountprovider_null_DefaultGroup_ConfirmAccountProviderViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.confirmaccountprovider_null_DefaultGroup_ConfirmAccountProviderViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 363280c1fa..b1ae254500 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.confirmaccountprovider_null_DefaultGroup_ConfirmAccountProviderViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.confirmaccountprovider_null_DefaultGroup_ConfirmAccountProviderViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52a5e65b23072ccfdc7220daca437e93342c04cee91cd971ba8d13872968ae7d -size 36962 +oid sha256:c9fb80a007891ceba86d3cdf7711ba3a8d585358fea9558a0c451f67de58b94b +size 37346 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.confirmaccountprovider_null_DefaultGroup_ConfirmAccountProviderViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.confirmaccountprovider_null_DefaultGroup_ConfirmAccountProviderViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 232ce0d49c..9f053deea5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.confirmaccountprovider_null_DefaultGroup_ConfirmAccountProviderViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.confirmaccountprovider_null_DefaultGroup_ConfirmAccountProviderViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea4a9e4b5488e925652ff747997dcbb58c55dd126c7b81f5222c30291270d7e6 -size 39147 +oid sha256:c9b3fcd78615bb01e7d6c044519a835222a5791547aa0c99a6bb7dc0a0ec72d4 +size 39687 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 79c5d3efd4..73636beab6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c60ca1b2273df93b927550e51d69f2b3a2b59f5060e0794b1cc7dbaa432dc51 -size 36879 +oid sha256:12ce43111318a2d77ea2dd878afb2dc8f668bb976b2a7367b732a0a5c86e71a3 +size 37168 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 3444da12ca..1077e0866b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a47d3a323db2c60d2825eccbc28b2df21ff7c5f2f2da86e27c61059493b18f70 -size 38074 +oid sha256:d7702e2ac4263ad23d209bce8ad6fce2a7ba3f4ca20f47c7585ff7e6e0965eed +size 38169 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 79c5d3efd4..73636beab6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c60ca1b2273df93b927550e51d69f2b3a2b59f5060e0794b1cc7dbaa432dc51 -size 36879 +oid sha256:12ce43111318a2d77ea2dd878afb2dc8f668bb976b2a7367b732a0a5c86e71a3 +size 37168 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index e9acc8b13b..10967a68ff 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c26b06e09a81b2990a7505b70a3bf2647e508d631e9fc65ee0a37364c685808 -size 38928 +oid sha256:7f133c65e9857599199920927e526d1c3a89b6eea0bbae6d3951f858de11530b +size 39119 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index b79bfe022e..01c9056835 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:baa415403a4ecac05895381608a24844b5c6f90c561f561668c55fe821740cb8 -size 40193 +oid sha256:ad6ab5e91e5ef1ff565dda6e36c51bcd9458dba1aa99624afb63fea4f0f7f1d7 +size 40259 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index e9acc8b13b..10967a68ff 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.loginpassword_null_DefaultGroup_LoginPasswordViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c26b06e09a81b2990a7505b70a3bf2647e508d631e9fc65ee0a37364c685808 -size 38928 +oid sha256:7f133c65e9857599199920927e526d1c3a89b6eea0bbae6d3951f858de11530b +size 39119 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 6b5e4c405f..b8278d25aa 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e97d88bef72c332cd145dc1080a989d163478c0252a9993f2f474bd51f2e4da8 -size 148762 +oid sha256:890331a5e2d87f5a326815a2729edb3fc709a509d17dfd3e33ca5d5f9dd98a3e +size 148093 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 151e09cfc0..e94b044001 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8edd6d72db9efaaed76ac64f9882a5b66ac30747355815955157a3f3fc98c2c -size 149344 +oid sha256:ae596d57381c529975d11e32c3ff3e64ec8de4eba7c52aa69fcf04e2c6854b6f +size 148784 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 847b4e1273..3c80137baf 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2cb81f2228ad63ff38bb6abe44c305adc66378bf85d36264219427be58ac58e3 -size 63343 +oid sha256:5e75dc8c50d9173a803c4dc8cee3b35f9bf3905bc6a19dd9479fe2d51c7f266c +size 64148 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 6b5e4c405f..b8278d25aa 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e97d88bef72c332cd145dc1080a989d163478c0252a9993f2f474bd51f2e4da8 -size 148762 +oid sha256:890331a5e2d87f5a326815a2729edb3fc709a509d17dfd3e33ca5d5f9dd98a3e +size 148093 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 822450c8af..56757c755b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:85222447b700315e6eea458fc72d61fb741018cb80c3a4530d4efc08ac9335ac -size 129373 +oid sha256:74b7fa3ce27045b0349950e976c424a155ca5aef680a58789877cdf7a7e9a32d +size 128877 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 6b5e4c405f..b8278d25aa 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e97d88bef72c332cd145dc1080a989d163478c0252a9993f2f474bd51f2e4da8 -size 148762 +oid sha256:890331a5e2d87f5a326815a2729edb3fc709a509d17dfd3e33ca5d5f9dd98a3e +size 148093 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 151e09cfc0..e94b044001 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8edd6d72db9efaaed76ac64f9882a5b66ac30747355815955157a3f3fc98c2c -size 149344 +oid sha256:ae596d57381c529975d11e32c3ff3e64ec8de4eba7c52aa69fcf04e2c6854b6f +size 148784 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 13ea2accd4..fd02412ab9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d48708d932ca9111f74d75654663e66a5eba1b419130ca3f00f0d6f997f119b9 -size 64166 +oid sha256:cdf49456caeab58f7761581ed37ba283f63ca16cccfb173b127e9218f28bb87c +size 65084 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index 6b5e4c405f..b8278d25aa 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e97d88bef72c332cd145dc1080a989d163478c0252a9993f2f474bd51f2e4da8 -size 148762 +oid sha256:890331a5e2d87f5a326815a2729edb3fc709a509d17dfd3e33ca5d5f9dd98a3e +size 148093 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index 822450c8af..56757c755b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:85222447b700315e6eea458fc72d61fb741018cb80c3a4530d4efc08ac9335ac -size 129373 +oid sha256:74b7fa3ce27045b0349950e976c424a155ca5aef680a58789877cdf7a7e9a32d +size 128877 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index a3c7eda016..07a0f8f685 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8e33e4ccb77ad7b3e317ee9b70360de9b0151f7391671455f0014a1a290974f -size 395734 +oid sha256:31d1194224f676b4700f963ec4d4c6c013e6f6eca0d6c1852d4d7e3f7c099eef +size 396102 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index d0e5516377..e7a7c8b9fd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b57f3a4ca9ac352fe3669a9388c85e25e91e0d54f309e9ca4f49fcb79b19527 -size 16083 +oid sha256:b9cbd731a497489af3daf7bf74ef44b0955a060fd322f26d9a4711fa464e549e +size 16451 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 8840b9892a..65b36c5ea6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a968039c2fa94e89c75e395bab808d2960c37c43576b558438ec17bcd17e507 -size 184797 +oid sha256:7b38a09834ee743825ce2d47bd62819c87b59777595029261b4ee6bd341d1644 +size 185141 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 7b19571de0..4940569821 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07da432732e812eaa091563610a3a0b490ca7f829260a5b61b4412d6c1875c8b -size 99982 +oid sha256:5e0d53c92cd4f5bd41449324e10ae978b431358b3e1a467684c9f09c69851c82 +size 101210 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 749c81631f..363747c718 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8423bf9617b4834c42a0f0783d18c41b695cbb6e7716230a1f940b99a7b0efd9 -size 13283 +oid sha256:0c33b0191730e4d499f6e89b073cf8da6b60ffae77ea821a0cb514aca1684ca3 +size 13691 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 735c9f5e32..01187f262b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4990c37241c349490619538b909e62bd76f8c49ffc3f9b7a2cf8fcaf9866bc08 -size 12852 +oid sha256:6fd448acd5377cb28dfeb61ca58b1320b0bfed16475eb0e6ffc1867206e6f50a +size 13242 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 90ddee2001..7721bc70f4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:71cee553053649ba9c83f5219d367d860897bc1ca3a5a0c35a234563d6bb5c16 -size 26604 +oid sha256:5300c5ca063458064bd305fd00062d307c09caed86ed95640cc4104310480b53 +size 26877 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index d48b777667..0651b1469b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:599e2496f9364d55dcc0cdb48b4b4919c052b0a66634d69bbb40834b03559b97 -size 26204 +oid sha256:f5d48d17f097b49c70fed6ad640d3310d3eb6608ed97830345c1e561ee8e5d16 +size 26480 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index d5c01a3a16..b707751d97 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4561451a8bbd583ffd2bb38be5c41fe9ae787b54500cc4c14af8d85989558fec -size 26387 +oid sha256:bfbeb57a1b19d25d37a3fb6e50221528d368e99ce48124cb1944a3973aa6f6c2 +size 26481 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index d5c01a3a16..b707751d97 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4561451a8bbd583ffd2bb38be5c41fe9ae787b54500cc4c14af8d85989558fec -size 26387 +oid sha256:bfbeb57a1b19d25d37a3fb6e50221528d368e99ce48124cb1944a3973aa6f6c2 +size 26481 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png index d5c01a3a16..b707751d97 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4561451a8bbd583ffd2bb38be5c41fe9ae787b54500cc4c14af8d85989558fec -size 26387 +oid sha256:bfbeb57a1b19d25d37a3fb6e50221528d368e99ce48124cb1944a3973aa6f6c2 +size 26481 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 1835595e41..239fd9659d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c196974276b6e6e665140f5e45529d529ffa53b91ae714bbd24921e9a81c838 -size 14418 +oid sha256:65937f0bf26349bc168ec709c9a560f48b3ddb336e5ccc39cf80eb3bfef71c30 +size 14747 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 01deacb825..84b632c77d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e9241a489f3d2fb93ce0b9678608418be5857a09e56b5726d1912599cc6e712 -size 13810 +oid sha256:89cca739d912bfb3b78e589c323f11e8c12c8df506dc7a650e3fb452ec1b7973 +size 14136 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 0772a81b3f..21a8770743 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3383f2ec12b187ad1d6402fb852c98d8041376c6ecee372e91529554bd30994 -size 28286 +oid sha256:a0ea82c7d990a967689b9c362ed1bc4af29651c7e7431fa5c4d831ed07a90776 +size 28635 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index 0f9de74d6d..13083c21f5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6665acc104fcc5477be20460ea5b3ba7cd75491bcd3c7df6fa244747ab55706c -size 27690 +oid sha256:ec1dd51fd31366ac1baa9e249fec51229b497543db1b0487222020e2295cd89d +size 28032 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index a8a622afed..92f7208067 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00e3ad9d2805d4d8e856e30226e0da4297ef5ae25c1e3b5760bf23e0611a7b97 -size 27977 +oid sha256:072e97b246b48d090a3ab9f42528cfd714dde2f0ca928dbdfd315cadfc110dc8 +size 28093 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index a8a622afed..92f7208067 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00e3ad9d2805d4d8e856e30226e0da4297ef5ae25c1e3b5760bf23e0611a7b97 -size 27977 +oid sha256:072e97b246b48d090a3ab9f42528cfd714dde2f0ca928dbdfd315cadfc110dc8 +size 28093 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_7,NEXUS_5,1.0,en].png index a8a622afed..92f7208067 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00e3ad9d2805d4d8e856e30226e0da4297ef5ae25c1e3b5760bf23e0611a7b97 -size 27977 +oid sha256:072e97b246b48d090a3ab9f42528cfd714dde2f0ca928dbdfd315cadfc110dc8 +size 28093 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 5b3f3b3f31..55b466482a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9034720950ce90da57f7fb3b811dddf95cb5f621a917b2fb1e930fb8aecac02c -size 44089 +oid sha256:93fc8ccac0599b3ad6e16b4cf706d53f7aa51c41b50afdb64eb66992c338c65c +size 44392 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 3ec76db242..ddcffd970b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd04b0d19c47008bec4e2a458647bbf4f0e4157452d380db65210e3a7ebeed3e -size 45123 +oid sha256:7d4dab3b14b9ef45c243621f4253f177623103b539df82166223291baf6622cd +size 45080 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index b600b0e223..35f6114312 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7b4b03c456266fbc7b476f4340551a9acaf731678af59961bb0890032c8cc53 -size 44669 +oid sha256:1504172c389945b60733645f3166bd6053ad5af62aab9ca31da6e4089d27eb38 +size 44636 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index f0d1df632d..f8198ed1a5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5a3b32af711ee8de6e2efbef25c1b747883807f341a67f2fad5c343be89b3bcc -size 42863 +oid sha256:afa74a13826fdfdb795c705ae537cf9cef516ebc2ff84a0f4e1d4bcf413ceada +size 43221 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 682e400eb9..ca7a4913ba 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82183d73c4ec193b2833ef5a007facd98d61293864532a200799f1ed75291002 -size 35550 +oid sha256:fb5749e3df13b1f7c178c7754e6c0726ee95b5b9b76563fffa6a37d317baf121 +size 35401 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 325e083ec6..bb7fd6311b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08465420dfb59d2fa6895106ed649787659e08898efdfac5957d70c6e36d64ae -size 45770 +oid sha256:1b5737eab1418e738ef66397e5a2d2c8ee70369d1a84dd59ff35e7fbf4d3b520 +size 45979 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 1206c45222..ca9291e476 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07e4c2347902204df2860f5ff48412c31b9904e61d2e3e481806ceb79b2f8d2a -size 47550 +oid sha256:1a58a45c850eb9f3095dff489b306c250484124d399421c9afece6d7061be253 +size 47472 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 1e8fe02f36..1b643ab7d8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c31855849005a3bf4f5eb02b8461cc2d26ba84637115d7e8390aa0732caf6013 -size 47013 +oid sha256:9452d0ee6d79778004e4b68a089d0105d4f87a2de4d89c36b889c73c9087c96c +size 46934 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index 8a5d3932f4..fe16c0bce6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2df48ac9b873b317c7cb1163bb96d2d0fac6f6c7e00747ec8e10e0e7a22d8cc4 -size 45129 +oid sha256:a9ce8f73922b6df91f3383f3d27464a2951595da9c63fbdbf8c0f87efd0d4115 +size 45445 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index efd9526466..a9608936b3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:48738c3a3a6d9caee6590fb756cef164b58d3238fe0d86fb4cf8acfac627e71f -size 36913 +oid sha256:b07653056ee30fb780009ce9b333c7cf3ce8eefae46374d3c9fb940557c87e48 +size 36774 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index de7ac30a62..04875a5d63 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c5150ff49e5e3dbcf21f056f1502a8da71696a93f842d243ef4abce506921f93 -size 50486 +oid sha256:8b51e48deaf036b33200c41adbc732ec90208812f7aca533645bd2bf781637e9 +size 51599 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index 03f898df03..fe2be30344 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e2de325d2ddb67538519aa93844cada90abcc191f8db401b104239dfee8c1b9 -size 52262 +oid sha256:02bf9db04c6b88bb28ca4941d25abfa1dc563c494693c8f9db93e658633b8f46 +size 53496 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_0,NEXUS_5,1.0,en].png index 636c99bead..232570b461 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:75bea68dfb5165f0f8c755237b627f5c61411fb2ff4d55a18d1daedf054d15ff -size 338382 +oid sha256:56f53e2c13480d5f31d38c671612c81fa802e1f35a55ada99d544917f01b91ba +size 338952 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_1,NEXUS_5,1.0,en].png index 14d6ab78da..45d008bb1b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:66e66c919bcc118c87c1d1095d03a105d42f3d630466f0a08d2cef011e67e9e4 -size 327542 +oid sha256:cfe3663daab250bcd733ab746e4864472691b40cf927a3cd73d15b323dec2b68 +size 329945 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_2,NEXUS_5,1.0,en].png index 5cadf29e4c..7349cf6808 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c3c01fb242bd0be1dd894542b3980d359e9276a55afe019ba95423eb7eec7a2 -size 340958 +oid sha256:c449e6e4162f12a6be7faa668b930b294489ae217c2a7493f5671145a926079e +size 340944 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_3,NEXUS_5,1.0,en].png index 68cacc0096..e6a8465877 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-D-0_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:595309f942c0ddc37965b8c1e2ef7afd2dc896929e71efd0696ad9214178c9ab -size 322751 +oid sha256:0355120442254880360b982df6c2c7cce84a471f632436c96e79ffafed35de14 +size 324378 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_0,NEXUS_5,1.0,en].png index f32ba6cbaf..98974210b9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69c933682865e48212cad79bb2194f2bb20a8133e7c76ac1f53f8691445e840d -size 421976 +oid sha256:5d05151967955c961ca47619de2f28f86d0350299b61fbef0371c1706439a844 +size 421908 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_1,NEXUS_5,1.0,en].png index ebb9513813..28985b1af6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:afcb322f07f594e26b051146b22919daf35317c844669e6204c1f4d29f9fb7ad -size 404245 +oid sha256:3a2056882e65afe8fc2d0dc60efc7912ca5674acb57e3690136a8704f9f063ed +size 407942 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_2,NEXUS_5,1.0,en].png index 2c884eeef0..2622fa3931 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88d2194ffc00644a2666175e86ca49dfbad8988fbb98346128d0add77c79b8eb -size 421289 +oid sha256:6521d409bc1fc70fa422ffec400d9f20d0b7fe16adebb7f5b0a9e5f135328ffc +size 422213 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_3,NEXUS_5,1.0,en].png index 5ce6c750e1..e28f44bb13 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenPreview-N-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c3c17831149c3a6a5b058e8b557b357e84157cb6a0bc56b945c9e2dd3bdc0de -size 393706 +oid sha256:d39cfdb0db12a6d14f3105aa168ea002a845d2083f07ecfabe0858739507018d +size 396789 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewDarkPreview_0_null,NEXUS_5,1.0,en].png index 2d5adfdf5c..679e2d22b9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf048607d3003004608786fe123bca039b5a6175aa52194ca54b4d54a69b7213 -size 24135 +oid sha256:472f27ff67cf4b822b272ad78e5933026e25b8da02830a75a9898a1c2b59459c +size 25349 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewLightPreview_0_null,NEXUS_5,1.0,en].png index d96d3a80f6..9274fb7079 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:57c119c7b2291b4a6431f9ed7805e74464a175e0a169f147ba77627b9174d2bc -size 25074 +oid sha256:3cc91235bda4aa191101eb0485cd90abdc94060f31685a01b8d134043c2edbfb +size 26374 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentDarkPreview_0_null,NEXUS_5,1.0,en].png index aa6c59c6f7..6a40cded49 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8415a958d4157f226fed8456fc0d004c1163d1ab9e24e57bfb2b369b4f7d0eb0 -size 26039 +oid sha256:24c0af07f894fc4220366c11dbd5b3d02e204cd3c9d6788d26d7dc29451e3791 +size 27297 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentLightPreview_0_null,NEXUS_5,1.0,en].png index 7b5233f7b2..06b65974f0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2de43d9a1e0652b2326f6b5462a915296388b61806ab2c8c62e4a7c15808a8a1 -size 27112 +oid sha256:268bbe371de1fb7b72376d3844e394bf4bd00cf1112b8d96d9404cf409543f32 +size 28488 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index c4fe8ba2ee..4f09628e11 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ef9e33bf65764ef25b4184c9f49be8befe4f6fad77d5f6fafc5ffcb7c09fb51 -size 64682 +oid sha256:67144695fd2efa7e81d910606e2ca4486a4365b54067fb4d9c1fb26303f86bfe +size 65099 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 073cfa83f8..7da87c44da 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ce0000ec9242c0b65fb06d1fea4622143bda5bebb691f8114890edf47f341ec -size 55166 +oid sha256:647b616a0153b433399004436b2d7259b2e0c12a4cb621d87df4cb0d576e6fcf +size 55422 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 1ca257c3ff..2884bfee01 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cbe0ca40022613ac7df059bad8a8cb3f60b1242a0d28b3cd2b5a59a7a26d828d -size 67488 +oid sha256:abf293ca05d36da1de3f68d8b3d60b2da80c26dfb189698b949cd06c48239f73 +size 67839 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index b9be444888..bb6eead4a7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd04de63275687ad4a905a3438f47898f864ff75591f6fc191d074550787661d -size 59880 +oid sha256:92b64a2dd7c5c268a12ac1ae154c5daeaeb267c0590852b3bb83793b4a01f631 +size 60229 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 1bbdba6045..c1c10133e1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07ce6ce83db08f8801f83dca8256c3fd133fc3501e237db6518d482f2248fc76 -size 29665 +oid sha256:8e2185d93e6f642c095cf63c9c42cdcf8dc035cbcb81ca0e9f70217e7b688596 +size 29671 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index e10aecaa41..9635b285ba 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:378b3d99b8a5c4f30a6148af614526a376b02f30afe97f2d6a7c10c097d70593 -size 23386 +oid sha256:8543706025ae8c983e95b20f62d8433dbcc03ba66e4aecb3f8c4dbfbd8c7405d +size 23390 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 9e1c8d9f20..82a1740d64 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e07de26f3432ed6b5a6b85490a52ea7324d11ce7722657770ba88c0ebba5f37d -size 54138 +oid sha256:6fb7373b0e6d3396eb134b8e589f64efedfea9066be7daabaf6b29d56bdc8343 +size 54130 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 3cad85cbf3..44f9d7a47a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:685460d930fee9e3c2f982b2245bce5c47e1a40570b5a69151cb3b12ad0cf2c0 -size 28315 +oid sha256:3cdca75d40ed26c3cf33bf2df54a53e93f72a72331ff1dd9bb289769e0ee4138 +size 28618 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index fc0e472e08..e66e45dcb2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7ef8974e2d69d8ae325a3465088c93018312716636fa1624d5fdf66813aac4b -size 27887 +oid sha256:2d711ae9a79869cdd5b5fd6dbfdc23a3d77761368257de84b9c7181185e6bf40 +size 28186 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index d42d35871e..33bd173990 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d2d88ed1abec3c370b0794ad81647a4d6885545b57f171ee98907c59e8a689e -size 28903 +oid sha256:4c19435d814b5a06e6e93e4549f76568235c0c018e10a7fdeb05d947620f67b7 +size 28931 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png index 1ac04e5716..7715deafc5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90246e3319170b6dbad8c4a18db08f82c1d09a62856f6b8f457479b84e81061a -size 25384 +oid sha256:f835ef43c56477c85b65eb5cb0ce6a601148a8adac2491e6597a74099e5cb06d +size 25356 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 70780621e1..cf00e4c5e7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ea01e575d960dc098a747b6f37891535e2a1028722d9923c92b81c97b99be3c -size 30740 +oid sha256:d4963634d3c2b6da6e05bf4cd00b255f0bd204bf572320cde153b7f079a10613 +size 30709 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index e5687588c6..075d730052 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af817f9b053559a95b43bc40d4ad289f3fe72f28cbd6088f4bc6fceab52d2c48 -size 24201 +oid sha256:aff84e6d8ed41ac483896a7551f55a2943a1ceb5945abdc19818de2838da74b4 +size 24178 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index aefafbc1e3..170443ddad 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cbd283f464eefde6a8f99d4546d89de62aa88aa57eea87115a11ac2de90c02a8 -size 55627 +oid sha256:c305179405a43cbd48b76a3531fd9437526bbf833a0be150744d8925faf738fe +size 55605 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index a71e49a925..4bf960778d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b334c7d8bb45aef8b7c6bb4f9b016f344283e36b45f762c49b0acac195b214eb -size 30407 +oid sha256:d7aa9b2ce8f4f0afebaff4283f83bd15c016453421796b3057a041e957e447d2 +size 30612 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index db80f0d5c2..47b62b7c8c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7198551e3d09343b6ba499349d7634b116d42707077ad31220493098051b3bff -size 29152 +oid sha256:e803a1d9dbb1445edf48e8373b4ff87ae2c2c341d049eff96310cc336eef8d57 +size 29340 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index 99e13a9113..45d21256f6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4493895c285381147d7ee542166fdc946b1fd1a8bcbdeb23df391f63e0f731fe -size 29369 +oid sha256:0c999ef4fd12d2703933f221e04c40c95982477d1b9731735f0419496b05aa85 +size 29381 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_6,NEXUS_5,1.0,en].png index f5566a7ddc..48f3e48564 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d4131fae961f950e77a6c435703fd6193e9bcee80b5c0fea32bc9383c4db787b -size 26352 +oid sha256:00c3ef64f46f20dc460fe096c7aaff42a5335f3c0f31caf795cd489b517619ca +size 26330 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 5a29b71fcb..983013c965 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f19d6be4e6e65007ee9548cb6494c6f760f8282f24ada04a53089a142fe8d0b7 -size 14028 +oid sha256:be77c7e5d2380e02916a095fb8e848e6ed48b89651022c89e781a6bfb7cbd3d7 +size 14413 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 195813c9b1..fe7e5e228f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:445afeb50307433ab21756b60d2d0f1f365db74a99d81db9ab994927d823ccaa -size 28495 +oid sha256:5d2b60d0486efd7fd5f23f5d91aa20c10c080aacdfb7b224bcd89c7d5265d4ca +size 28593 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 7158d39702..0d51af3915 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2947b905a604c522696036b5ab408ec22d2ec6dd8b75579c5727c6626776abdd -size 11520 +oid sha256:698c99b9853d7b9859488f0a5e8d267f078b182da5a86a9fd8d26fe047c8f413 +size 11910 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 8124a1de37..46da1c22cd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c7dcf21a708c134ac856b5b27c398330c73f25a69d9d46fc839e7aa8f2c02ef -size 26291 +oid sha256:a9ce615462c5f6f608c5ccd9fd2bf625d73fc512d7684db6358eff46228f9892 +size 26593 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_4,NEXUS_5,1.0,en].png index e8ebb07f1c..0c6a30f778 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ea55df98f4ef544494dabbf661c1623d567291702a68993ab65f4a67770954f -size 13576 +oid sha256:153421bc23ccdab25a935d23599e37fa7614da878f83e93762c69695e35a2244 +size 13942 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 87e6a4037f..05266e3a11 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da3f4846d648ab399604db869980c41243ba6e7f91f16365cdae00305ce5723e -size 45416 +oid sha256:27202c3c88c6eb67ee082592ecc564cd79fad5bbb669cb9f9a92a8b20880f144 +size 45532 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png index 311cf7c8e0..38994ec87e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0fc4fe77d849f04f55c5142f95493a1a05cf2ba44293b5fe81414336d1687dad -size 38540 +oid sha256:c6fb457bab92987c715938b89a2dda81ae4f58599f84526e102c786370c37c74 +size 38647 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_0,NEXUS_5,1.0,en].png index 751bfeff5d..6ca9645a30 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9c4c8b226405f3c92536e329eea5f8ed73b66f9bf8b18c484472212551ab13c -size 15034 +oid sha256:75fda6e4e92b021ad0fa927bb6736841de01ecf8c1b7ff1a355bf8bec01dd2dc +size 15340 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_1,NEXUS_5,1.0,en].png index e722eddaf7..a4e250b6d6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:71612a9c2685e71e2075d8f592833448fae446edeee64f996626fb713142d02b -size 29353 +oid sha256:f6f6e4f7785d35847a2bc38db7090d882435365cfdc3f7a14d8f457d1acf03c2 +size 29429 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_2,NEXUS_5,1.0,en].png index 85ea57bf0f..225513dea3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dee1f76c085ea1779e79f6e3f1f67d63c154989bb07efff4af8261073980ed84 -size 12354 +oid sha256:947a7f8e7faa0e40e47c1939cf255a9063e91c4e4f5787099d46ddc3c8abff68 +size 12657 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_3,NEXUS_5,1.0,en].png index 767762b05a..fd375990d4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0b1dd6dc2e1c333203063f86a1dcf442ba6aabaf1a4210a8d6429f402ccda5a1 -size 27229 +oid sha256:15b8a86cf3fbc4e851a42557b9da9a84ddf34c5964d5dbedc14a77d7c90ef482 +size 27439 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_4,NEXUS_5,1.0,en].png index 596de325e2..54756514d1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a0d5a7fe904c98ece401a8e91685f9f15f02647eb269acbc20d93ead9d5665a -size 14439 +oid sha256:35ae824d0ed03be1f0ec4068c5972d56b80637baf497ad4700a24f9a62a7173c +size 14737 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png index f051ab3fff..2ac4ea2741 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1b2742a29099293462ef6a020f4440143db9512b70b5638d09af685c012bb11 -size 46910 +oid sha256:4ed1acfdc4a74864d8c54577e3038017f3ecb1791144d6a8c62e825d458db16c +size 46995 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png index ddf4f582db..7740716b68 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a5e412535f5c0fc384983f0b58319151c2e60a7b30f4f1e89a4411717ed93eb -size 40701 +oid sha256:8e53678bab7f61340db9ddb6266857d664027cc83e803cf993b1b64fe0cfde44 +size 40777 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 9db420c070..0f1091ee6a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:41c107c84e391b34ca13210b904327ca348625393339b95de8e740ff231c53e8 -size 13120 +oid sha256:791760246c638a89757995a3e5772150a9071f55bef1f35ba0f5ef8705d2b173 +size 13107 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_2,NEXUS_5,1.0,en].png index 1fb5bc6b4a..526629ae68 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a1ac52a5f865e8adcb8c6429e0d6a7f088bad2f0607b3362f899f3b87526aec6 -size 14016 +oid sha256:010fb88e2a8c3d8ebd5ed2180b2dcf8cc207e9ade5a49414be9f6dbd70cd2e16 +size 14008 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl.components_null_DefaultGroup_PreviewRequestVerificationHeaderDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl.components_null_DefaultGroup_PreviewRequestVerificationHeaderDark_0_null,NEXUS_5,1.0,en].png index 82120c314f..225ec899ce 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl.components_null_DefaultGroup_PreviewRequestVerificationHeaderDark_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl.components_null_DefaultGroup_PreviewRequestVerificationHeaderDark_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a7fa18ff871febb39ed15e617a873cc77b7a2bb1f8263ca6078086d5cd9b7c9 -size 28752 +oid sha256:0c3327e517224d5896e737f357a0a6edfd54c034e6da3c6c3302d91a6122a336 +size 28604 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl.components_null_DefaultGroup_PreviewRequestVerificationHeaderLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl.components_null_DefaultGroup_PreviewRequestVerificationHeaderLight_0_null,NEXUS_5,1.0,en].png index 2182656f2c..8a2465e607 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl.components_null_DefaultGroup_PreviewRequestVerificationHeaderLight_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl.components_null_DefaultGroup_PreviewRequestVerificationHeaderLight_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ed90625ca0a30b12cdf53c34afdc7a29f410be83588adf64da7290345810f15 -size 28893 +oid sha256:5a5545f6fc8a18336e655c50787b706cfb5fde10b6b075b16013c6c90d27726f +size 28732 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 37560a5913..ae17140c2d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0334a218c09298714de3ad44fbc2dc310efe542cc21609902d962a5059194cec -size 58860 +oid sha256:1a4308687706a3f04660151013d63dab0c674650b1c158a9fd41f35898ae0b80 +size 58675 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index f3c4d30238..dc017f51f6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomlist.impl_null_DefaultGroup_RoomListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:05d71406e279c54b23d9a3731b624d643ab820c3cee180536d1d4cbb50fa0d3f -size 62283 +oid sha256:8359eb7337d0b58151e282f7686ebfc75b9b046f6bacc9fa750cba6ff57866df +size 62060 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 936fe4c686..64f3005666 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b17397fbe00c25f0ec2012c84daa0483d53a4618777444d4cfd180d50c3cfc1 -size 27247 +oid sha256:ec007743c395328cc4b276221aaec7bbfd71d04d79470f94cb6576785d04c6b3 +size 27088 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 0c3abd6102..5d1f622540 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52854ce12dcc8304004a2b6ebc2def5513dc390483f59d51ca30e149ec12d8e2 -size 56374 +oid sha256:085ec9629209f6b08ec436c95b7df7db4b0cc5bb87c3389aa4f23f8d02dc1158 +size 56221 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index d9d8c00aa8..548fad8c57 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac9abae6c6ac9b3b5d774f091ec89a40131b81a408624957cb16e33586f464e0 -size 57241 +oid sha256:7e3dc126bd7d1009b2b4296d22aa88047f7e71d1a5d9ff39523d2358cc4a34f2 +size 57563 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 0ebaa9612b..ac6fbdffcc 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:115e7b766c41009e2ab75d0fc5b44e967e9e53e141bf36003fa8dfc1993cff83 -size 30679 +oid sha256:75248001be16310b2860afb8dd6eae5cbbb3b89d22467493a64e2dbb0ddb161f +size 30563 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index c432ea640a..4edb7f637a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:408ee6e94c45930a4c4ad7413e65eaf314a848f6bea7f3740a49cc68a7bd6f88 -size 25834 +oid sha256:0b149df2993eda450c861d97a78b36d823696b0068ab45552086eaaac92b49b6 +size 25699 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 3bf8f2f81e..3417705d4c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee6e86ac798549910a5350dd371219ad0e0a1616d3f616e31dc2c38482ac2453 -size 28724 +oid sha256:3f13fc514ffc482bf2d863b6e8e2c305bef5568df9ceb21046714a9f6aed4592 +size 28594 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 36a54d5785..d4440d8324 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b8870f4e25d40bac82e6f252f74d12f89a00b89b5ede2cdbfc613fbb3a4bc8f -size 56838 +oid sha256:9497101bca7966cfa64cfc203201a20cc97add051dcd25b64d4297d0d4df8864 +size 56821 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index 1c70233409..20937eae64 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:01485b9e3264e64273d720474b947b3372b7b3368b8eb5b575d4ca1593e0eb9d -size 58058 +oid sha256:53dd35c89ef71da224c1dcd677de042fa356f82040bdab8d2653a656ecc5fced +size 58248 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index 06d46ba33b..214dd48f88 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fe8368c7bc5a16f178c114391aa7a2f7801aeb4b73f8df0f2ad9bda1e690059b -size 32121 +oid sha256:b943326eb8a8dfad99c7e47108a8459246d3f22c0f7fce55c2c9a30c404ed39b +size 32050 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index df4474d272..ea653c834e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.verifysession.impl_null_DefaultGroup_VerifySelfSessionViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f8fe0565a38d640227b5d4ffd552d7fba2c0fea7978a3d578c34b1dbc36ed9d -size 26670 +oid sha256:802e5a13191b96fd002f88b489cf3a353eb99ee721749a979d5cc4f1627938fe +size 26576 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonColumnMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonColumnMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png index 45788b5180..7686fdfe0b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonColumnMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonColumnMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6fbd442e62fc2c8e02040d0ef42be06af5a1d554ee386e983c4bccae199004aa -size 9456 +oid sha256:b4cece3ca4908302854c57084313d89320e4b5d645a3b43406b9876d70deb2ea +size 14549 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonColumnMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonColumnMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png index f02f83b162..e4cc246047 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonColumnMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonColumnMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f9e941ab8eceefdd858c81667a5bafa33a4efc7d232ad0d501547ecda37758d -size 9616 +oid sha256:9031a4cbdc155e05c4aa56e774fc01ed09f32cbae7ab443cdeb42a2f2d41a8a2 +size 14942 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png index dfdb106006..d89f7c3143 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f35e88e2b096b989eb669b8fee4196025177b29a9c9b729b843dcf7978df6add -size 7420 +oid sha256:07bc5d9f73124dc246c8eb96ba4e2cd7657b5ed397d668a43cc7cf1872a03762 +size 7660 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png index 0cff850500..e678873b95 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:60c7dc93e9f13f1452473f783e7ecf7fc776610108a0a5e8fdf92c9e4f93a6a3 -size 7528 +oid sha256:78d81c9074ee16f257a301a067a902961c8cf5502d1b41b22a1fe952eef1566c +size 7771 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewDark_0_null,NEXUS_5,1.0,en].png index c84d42e293..c6a4eab5da 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewDark_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewDark_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12075b74ecb0d505b38c99bac410d4195ed85e6cf8efd2ac07848962b7970238 -size 10972 +oid sha256:7b354e5d775173757829e50113b4859afd0883f0441ebd22d05545e2e882a3dd +size 11177 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewLight_0_null,NEXUS_5,1.0,en].png index 926dab7b5b..ca80beb9e9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewLight_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewLight_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7e2f73cd2a33a946b256dffb97ff4485a17040a2f7358a4773bfe028ccd9e4c -size 11042 +oid sha256:7aacb9ba54c904060cc40ab5e5725d92a152c9f3b9441fa2fb6d0fdd8449583a +size 11230 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.button_null_Buttons_ButtonWithProgressPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.button_null_Buttons_ButtonWithProgressPreview_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 31e652eda3..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.button_null_Buttons_ButtonWithProgressPreview_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f8b30dd030cae4bd394796da45e3e067c7aca5fb5176bdd73a8a24938f8571a1 -size 17707 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..70041ad838 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b81b47c80113bee5cd295e4425736f1abf6236350a582d0cfee304b056644cb +size 22850 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null_0,NEXUS_5,1.0,en].png deleted file mode 100644 index 178db99946..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null_0,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a66a098b4d029369637ffaf9d5ac24f7a76d168b526382c78167132756476c2c -size 22945 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null_1,NEXUS_5,1.0,en].png deleted file mode 100644 index 7376ab8e10..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null_1,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8a32ebcf17a8906570bc08df47cccac1ff4b5c7f7cd0bc414a188c9e302f6934 -size 22931 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ErrorDialogPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ErrorDialogPreview_0_null,NEXUS_5,1.0,en].png index 4e240079ab..99b294bc91 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ErrorDialogPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ErrorDialogPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c0be8e52cbd3d139601bcfe093174fa5d873f2433709a1306645062c9a8bfb9 -size 17921 +oid sha256:e847e0af0865d01abc77eb27d503581912d95b6d86d82cc310fcb085567b2834 +size 17830 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_RetryDialogPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_RetryDialogPreview_0_null,NEXUS_5,1.0,en].png index ecace25254..d664efd253 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_RetryDialogPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_RetryDialogPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:73c68fc2181960285410a51007a033b461abbd9ad89844d18eae10dcda26bdd4 -size 21302 +oid sha256:494a2d83511814b6d954f2e05b445e9e546f2a52ff31f10b330238ea15868299 +size 23546 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_Dialogs_ProgressDialogPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_Dialogs_ProgressDialogPreview_0_null,NEXUS_5,1.0,en].png index 9d7ab1352b..71196ad117 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_Dialogs_ProgressDialogPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_Dialogs_ProgressDialogPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22747c979bf704a554dfa4ee3ace1551e78f1180bc594c55f1497ec1d6529aa2 -size 21211 +oid sha256:15513c18852cbfe860e32f5cc74f21cc3c7d15a70eee50c7710e195cfec0c8ac +size 21567 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.ruler_null_DefaultGroup_WithRulerDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.ruler_null_DefaultGroup_WithRulerDarkPreview_0_null,NEXUS_5,1.0,en].png index 767844a2e8..7afa167e0a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.ruler_null_DefaultGroup_WithRulerDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.ruler_null_DefaultGroup_WithRulerDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9dadf14d9538191b4f9be41d8deb4e441008d80eb687d3f01484d6e497dcadb -size 14291 +oid sha256:49a7579caea03ac759e5201bb88e131cad9447afbad3c250703ece49cf025e6b +size 13561 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.ruler_null_DefaultGroup_WithRulerLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.ruler_null_DefaultGroup_WithRulerLightPreview_0_null,NEXUS_5,1.0,en].png index cd36276b8a..050cbf8497 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.ruler_null_DefaultGroup_WithRulerLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.ruler_null_DefaultGroup_WithRulerLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16f731c6c1d2cb9b462b22ab56475fda53844747eb0c587d801456903c3d7bcf -size 13697 +oid sha256:df1ee64f9a226fc3113caedab7b7e7450d155477b52694f134cf4da53523cfb6 +size 13124 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_Menus_MenuPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_Menus_MenuPreview_0_null,NEXUS_5,1.0,en].png index 8d195dfdef..4444536299 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_Menus_MenuPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_Menus_MenuPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6dc928db8fab90762a934c10dfc8d50aa0ed7ebe776bd2705c4f0cba2eac74fb -size 11262 +oid sha256:7cf38f5423e4d7163cf29495b01b5dc716f348a50f9c9683d293d1189d10b71c +size 11681 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_ButtonPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_ButtonPreview_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 53e4ece077..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_ButtonPreview_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3ffe5901b54ac442c8b71a7ff1d59bb3903ef2374c6fad378e55fa857e2ff34c -size 23162 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_FilledButtonLargePreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_FilledButtonLargePreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4bc307ba70 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_FilledButtonLargePreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fcae0955e194993a9cfe55a4279d7b9b601d4fa91e6ff67962d9339ec32eb952 +size 43832 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_FilledButtonMediumPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_FilledButtonMediumPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ce6e549f68 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_FilledButtonMediumPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13c793c97dc51d2cef3e3590efb65957e58bf77501f5791b2a0a8edb01a48c5f +size 42358 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonLargePreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonLargePreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..757ac6640e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonLargePreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3093cd24a222f9854c3927fdd3fb39d26b1ad602b6e8580966ecae36ff937358 +size 47957 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonMediumPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonMediumPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2cdea2bdc1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonMediumPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c6b89df7fc0d8cda26ec10ffcb5c5ca266e913c75da9f1943a22b6bb56691f9 +size 47167 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonsPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonsPreview_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index f918f32903..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_OutlinedButtonsPreview_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6efea5a0c9cd8e5626c16248e020b88a069ce878b4471e93dd9ccb956ddd41df -size 26956 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonLargePreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonLargePreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..00615a4f8c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonLargePreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:14b7f637d843a604237650f1b72220e7a581871b6ca1cbb6be184c9d5d6127d6 +size 32874 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonMediumPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonMediumPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..bcec196c90 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonMediumPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15de13a449591d15573d36f894e208530cb60b5e80f7e1779279e5147fb7934e +size 30692 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonPreview_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index ebcd57bd3b..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonPreview_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:640035df43196f84a7031578feb4b54f9702801c7974fc7af6a80eb907c40097 -size 17486 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 0ce9bb484a..8859798647 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:583910d40d3832aec4f2f1970f203e208ff6c8d4ea4a77fbbb3012a16d74b1d7 -size 23588 +oid sha256:9806655e512a275706e948825ef7d2faa6ba8fc25aef833648ede20952304f6a +size 24755 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 4f1e3774a2..ffe720d68e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eece69f07544c8e61080cdc4391a12bbb52b30759a330a0967678af73112fda5 -size 32910 +oid sha256:d2adb397be7481de953a8059f81629c7f3b2bcbd5f010fb9988a7188a03cce95 +size 34095 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index e1933a744c..94d65a2e4b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a5a75e7614025e08e320771628ddb7e3b24f055bc5ced0785b9b259f183200c -size 27249 +oid sha256:b102ab3bacac5c2241c2f0a4df8e82d24bb9799584049ea743cbaba8958bd58c +size 28522 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 38772ec2da..27345b0d8e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8cb222bf65bcc884814d8af92f032a79a2bdbf6f66f5ca792e7f72ad3917fbe -size 24442 +oid sha256:831f4350c9fa4749db0c7d86f20ab043d053efca870346102d359962eb5825e2 +size 25953 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index d0f492d482..adc547eb6b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a277bf8619ef6f8d52a83d33e855f03e28dfea14e2af2482db3f7ce1a89c35c4 -size 34396 +oid sha256:8f769f814da0a304e93ec614c305098b35b6d85f1abe40c54a82f5aff18cf603 +size 35893 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 94861e6fda..f4b0fda846 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d61c7c8f802a9f1bfefbf4d2b0e666a9e2162fa13bab523584baedf380c1df6 -size 28484 +oid sha256:57e338a521660fc912e111e9b9358c7ca7c33ce4d05cf96dc24b202d33d4d332 +size 30017 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewDarkPreview_0_null,NEXUS_5,1.0,en].png index 70da769e1f..b290efe8b7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:484c56c1ab68d394200202bda364175a1fbd27df31630649010852a4c2552870 -size 20801 +oid sha256:e680c33d3185285e0ee1dc42a9b0d49576889f794255a1cdc769f53f2a5efa05 +size 20743 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewLightPreview_0_null,NEXUS_5,1.0,en].png index f2a152da09..e388f0113f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8d6b5bcaa10f4ae2142edfef6745e22e0e9f8f52d9fedcbe77d3523ec4cb48d -size 21476 +oid sha256:d1dc8170afe756b20b0d3fcae2e2cfe0dac0e9b843be5733579563b6deeb9f6b +size 21437 From f15817447cd81b771195f16a157cfe7a954e185d Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 9 Aug 2023 11:16:47 +0200 Subject: [PATCH 233/251] Update rust sdk to 0.1.41 (just make it compiles) (#1040) Co-authored-by: ganfra --- gradle/libs.versions.toml | 2 +- .../libraries/matrix/impl/tracing/TracingConfiguration.kt | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 744fecfc24..9f93252e74 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -145,7 +145,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" } timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.40" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.41" sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" } sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/TracingConfiguration.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/TracingConfiguration.kt index f32b18c9f2..ac8c62aeb6 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/TracingConfiguration.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/TracingConfiguration.kt @@ -22,5 +22,10 @@ import timber.log.Timber fun setupTracing(tracingConfiguration: TracingConfiguration) { val filter = tracingConfiguration.filter Timber.v("Tracing config filter = $filter") - org.matrix.rustcomponents.sdk.setupTracing(filter) + val rustTracingConfiguration = org.matrix.rustcomponents.sdk.TracingConfiguration( + filter = filter, + writeToStdoutOrSystem = true, + writeToFiles = null, + ) + org.matrix.rustcomponents.sdk.setupTracing(rustTracingConfiguration) } From 3f1d241b48cd1db073278f5f70e251d779dece91 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 9 Aug 2023 12:18:49 +0200 Subject: [PATCH 234/251] Feature/fga/rust sdk tracing (#1036) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Align TracingConfiguration with iOS * Create TracingTree from rust sdk * tracing: create a working configuration with RustTracingTree * Tracing: WIP implementation of new api * Tracing: clean up * Tracing: use the latest api * Tracing: some more clean up * Remove generated logcat file after compressing it --------- Co-authored-by: ganfra Co-authored-by: Jorge Martín --- .../element/android/x/ElementXApplication.kt | 6 +- .../io/element/android/x/di/AppBindings.kt | 4 + .../x/initializer/MatrixInitializer.kt | 36 ------- .../x/initializer/TracingInitializer.kt | 57 ++++++++++++ .../rageshake/api/reporter/BugReporter.kt | 12 +++ features/rageshake/impl/build.gradle.kts | 1 + .../impl/reporter/DefaultBugReporter.kt | 67 ++++++++++++- .../impl/bugreport/FakeBugReporter.kt | 9 ++ .../api/tracing/TracingConfiguration.kt | 67 +------------ .../api/tracing/TracingFilterConfiguration.kt | 93 +++++++++++++++++++ .../matrix/api/tracing/TracingService.kt | 21 +---- .../api/tracing/WriteToFilesConfiguration.kt | 22 +++++ libraries/matrix/impl/build.gradle.kts | 2 +- .../matrix/impl/sync/RustSyncService.kt | 6 +- .../matrix/impl/tracing/LogEventLocation.kt | 42 +++++++++ .../matrix/impl/tracing/RustTracingService.kt | 51 ++++++++++ .../matrix/impl/tracing/RustTracingTree.kt | 75 +++++++++++++++ .../impl/tracing/TracingConfiguration.kt | 31 ------- .../android/samples/minimal/Singleton.kt | 15 ++- 19 files changed, 455 insertions(+), 162 deletions(-) delete mode 100644 app/src/main/kotlin/io/element/android/x/initializer/MatrixInitializer.kt create mode 100644 app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingFilterConfiguration.kt rename app/src/main/kotlin/io/element/android/x/initializer/TimberInitializer.kt => libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingService.kt (51%) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/WriteToFilesConfiguration.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/LogEventLocation.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingTree.kt delete mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/TracingConfiguration.kt diff --git a/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt b/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt index ec3259fb7c..da8592771c 100644 --- a/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt +++ b/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt @@ -24,8 +24,7 @@ import io.element.android.x.di.DaggerAppComponent import io.element.android.x.info.logApplicationInfo import io.element.android.x.initializer.CrashInitializer import io.element.android.x.initializer.EmojiInitializer -import io.element.android.x.initializer.MatrixInitializer -import io.element.android.x.initializer.TimberInitializer +import io.element.android.x.initializer.TracingInitializer class ElementXApplication : Application(), DaggerComponentOwner { @@ -39,8 +38,7 @@ class ElementXApplication : Application(), DaggerComponentOwner { appComponent = DaggerAppComponent.factory().create(applicationContext) AppInitializer.getInstance(this).apply { initializeComponent(CrashInitializer::class.java) - initializeComponent(TimberInitializer::class.java) - initializeComponent(MatrixInitializer::class.java) + initializeComponent(TracingInitializer::class.java) initializeComponent(EmojiInitializer::class.java) } logApplicationInfo() diff --git a/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt b/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt index 4d75d8601e..5fb3523d6e 100644 --- a/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt +++ b/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt @@ -17,11 +17,15 @@ package io.element.android.x.di import com.squareup.anvil.annotations.ContributesTo +import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.libraries.designsystem.utils.SnackbarDispatcher import io.element.android.libraries.di.AppScope +import io.element.android.libraries.matrix.api.tracing.TracingService @ContributesTo(AppScope::class) interface AppBindings { fun mainDaggerComponentOwner(): MainDaggerComponentsOwner fun snackbarDispatcher(): SnackbarDispatcher + fun tracingService(): TracingService + fun bugReporter(): BugReporter } diff --git a/app/src/main/kotlin/io/element/android/x/initializer/MatrixInitializer.kt b/app/src/main/kotlin/io/element/android/x/initializer/MatrixInitializer.kt deleted file mode 100644 index 5eebc88756..0000000000 --- a/app/src/main/kotlin/io/element/android/x/initializer/MatrixInitializer.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2022 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.x.initializer - -import android.content.Context -import androidx.startup.Initializer -import io.element.android.libraries.matrix.impl.tracing.setupTracing -import io.element.android.libraries.matrix.api.tracing.TracingConfigurations -import io.element.android.x.BuildConfig - -class MatrixInitializer : Initializer { - - override fun create(context: Context) { - if (BuildConfig.DEBUG) { - setupTracing(TracingConfigurations.debug) - } else { - setupTracing(TracingConfigurations.release) - } - } - - override fun dependencies(): List>> = listOf(TimberInitializer::class.java) -} diff --git a/app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt b/app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt new file mode 100644 index 0000000000..068d439994 --- /dev/null +++ b/app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022 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.x.initializer + +import android.content.Context +import androidx.startup.Initializer +import io.element.android.libraries.architecture.bindings +import io.element.android.libraries.matrix.api.tracing.TracingConfiguration +import io.element.android.libraries.matrix.api.tracing.TracingFilterConfigurations +import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration +import io.element.android.x.BuildConfig +import io.element.android.x.di.AppBindings +import timber.log.Timber + +class TracingInitializer : Initializer { + + override fun create(context: Context) { + val appBindings = context.bindings() + val tracingService = appBindings.tracingService() + val bugReporter = appBindings.bugReporter() + Timber.plant(tracingService.createTimberTree()) + val tracingConfiguration = if (BuildConfig.DEBUG) { + TracingConfiguration( + filterConfiguration = TracingFilterConfigurations.debug, + writesToLogcat = true, + writesToFilesConfiguration = WriteToFilesConfiguration.Disabled + ) + } else { + TracingConfiguration( + filterConfiguration = TracingFilterConfigurations.release, + writesToLogcat = false, + writesToFilesConfiguration = WriteToFilesConfiguration.Enabled( + directory = bugReporter.logDirectory().absolutePath, + filenamePrefix = "logs" + ) + ) + } + bugReporter.cleanLogDirectoryIfNeeded() + tracingService.setupTracing(tracingConfiguration) + } + + override fun dependencies(): List>> = mutableListOf() +} diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt index 0af13dcdda..99849ef1d4 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/reporter/BugReporter.kt @@ -16,6 +16,8 @@ package io.element.android.features.rageshake.api.reporter +import java.io.File + interface BugReporter { /** * Send a bug report. @@ -43,4 +45,14 @@ interface BugReporter { customFields: Map? = null, listener: BugReporterListener? ) + + /** + * Clean the log files if needed to avoid wasting disk space. + */ + fun cleanLogDirectoryIfNeeded() + + /** + * Provide the log directory. + */ + fun logDirectory(): File } diff --git a/features/rageshake/impl/build.gradle.kts b/features/rageshake/impl/build.gradle.kts index 3283e3f37a..464d521689 100644 --- a/features/rageshake/impl/build.gradle.kts +++ b/features/rageshake/impl/build.gradle.kts @@ -32,6 +32,7 @@ anvil { dependencies { implementation(projects.anvilannotations) anvil(projects.anvilcodegen) + implementation(projects.services.toolbox.api) implementation(projects.libraries.androidutils) implementation(projects.libraries.core) implementation(projects.libraries.network) diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt index 65dc48aca8..a8491b5a74 100755 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt @@ -18,6 +18,7 @@ package io.element.android.features.rageshake.impl.reporter import android.content.Context import android.os.Build +import android.text.format.DateUtils.DAY_IN_MILLIS import androidx.core.net.toFile import androidx.core.net.toUri import com.squareup.anvil.annotations.ContributesBinding @@ -27,10 +28,10 @@ import io.element.android.features.rageshake.api.reporter.BugReporterListener import io.element.android.features.rageshake.api.reporter.ReportType import io.element.android.features.rageshake.api.screenshot.ScreenshotHolder import io.element.android.features.rageshake.impl.R -import io.element.android.features.rageshake.impl.logs.VectorFileLogger import io.element.android.libraries.androidutils.file.compressFile import io.element.android.libraries.androidutils.file.safeDelete import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.extensions.toOnOff import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.mimetype.MimeTypes @@ -38,7 +39,10 @@ import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.network.useragent.UserAgentProvider import io.element.android.libraries.sessionstorage.api.SessionStore +import io.element.android.services.toolbox.api.systemclock.SystemClock +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import okhttp3.Call import okhttp3.MediaType.Companion.toMediaTypeOrNull @@ -65,6 +69,8 @@ class DefaultBugReporter @Inject constructor( @ApplicationContext private val context: Context, private val screenshotHolder: ScreenshotHolder, private val crashDataStore: CrashDataStore, + private val coroutineScope: CoroutineScope, + private val systemClock: SystemClock, private val coroutineDispatchers: CoroutineDispatchers, private val okHttpClient: Provider, private val userAgentProvider: UserAgentProvider, @@ -87,6 +93,7 @@ class DefaultBugReporter @Inject constructor( // filenames private const val LOG_CAT_ERROR_FILENAME = "logcatError.log" private const val LOG_CAT_FILENAME = "logcat.log" + private const val LOG_DIRECTORY_NAME = "logs" // private const val KEY_REQUESTS_FILENAME = "keyRequests.log" private const val BUFFER_SIZE = 1024 * 1024 * 50 @@ -158,9 +165,8 @@ class DefaultBugReporter @Inject constructor( val gzippedFiles = ArrayList() - val vectorFileLogger = VectorFileLogger.getFromTimber() - if (withDevicesLogs && vectorFileLogger != null) { - val files = vectorFileLogger.getLogFiles() + if (withDevicesLogs) { + val files = getLogFiles() files.mapNotNullTo(gzippedFiles) { f -> if (!mIsCancelled) { compressFile(f) @@ -168,6 +174,7 @@ class DefaultBugReporter @Inject constructor( null } } + files.deleteAllExceptMostRecent() } if (!mIsCancelled && (withCrashLogs || withDevicesLogs)) { @@ -458,6 +465,54 @@ class DefaultBugReporter @Inject constructor( ) } + override fun logDirectory(): File { + return File(context.cacheDir, LOG_DIRECTORY_NAME) + } + + override fun cleanLogDirectoryIfNeeded() { + coroutineScope.launch(coroutineDispatchers.io) { + // delete the log files older than 1 day, except the most recent one + deleteOldLogFiles(systemClock.epochMillis() - DAY_IN_MILLIS) + } + } + + /** + * @return the files on the log directory. + */ + private fun getLogFiles(): List { + return tryOrNull( + onError = { Timber.e(it, "## getLogFiles() failed") } + ) { + val logDirectory = logDirectory() + logDirectory.listFiles()?.toList() + }.orEmpty() + } + + /** + * Delete the log files older than the given time except the most recent one. + * @param time the time in ms + */ + private fun deleteOldLogFiles(time: Long) { + val logFiles = getLogFiles() + val oldLogFiles = logFiles.filter { it.lastModified() < time } + oldLogFiles.deleteAllExceptMostRecent() + } + + /** + * Delete all the log files except the most recent one. + * + */ + private fun List.deleteAllExceptMostRecent() { + if (size > 1) { + val mostRecentFile = maxByOrNull { it.lastModified() } + forEach { file -> + if (file != mostRecentFile) { + file.safeDelete() + } + } + } + } + // ============================================================================================================== // Logcat management // ============================================================================================================== @@ -485,6 +540,10 @@ class DefaultBugReporter @Inject constructor( Timber.e(error, "## saveLogCat() : fail to write logcat OOM") } catch (e: Exception) { Timber.e(e, "## saveLogCat() : fail to write logcat") + } finally { + if (logCatErrFile.exists()) { + logCatErrFile.safeDelete() + } } return null diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt index ac8940a1ac..82edaf563d 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/FakeBugReporter.kt @@ -21,6 +21,7 @@ import io.element.android.features.rageshake.api.reporter.BugReporterListener import io.element.android.features.rageshake.api.reporter.ReportType import io.element.android.libraries.matrix.test.A_FAILURE_REASON import kotlinx.coroutines.delay +import java.io.File class FakeBugReporter(val mode: FakeBugReporterMode = FakeBugReporterMode.Success) : BugReporter { override suspend fun sendBugReport( @@ -55,6 +56,14 @@ class FakeBugReporter(val mode: FakeBugReporterMode = FakeBugReporterMode.Succes delay(100) listener?.onUploadSucceed(null) } + + override fun cleanLogDirectoryIfNeeded() { + // No op + } + + override fun logDirectory(): File { + return File("fake") + } } enum class FakeBugReporterMode { diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingConfiguration.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingConfiguration.kt index 8bcd602b9f..6381cc7ed8 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingConfiguration.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * 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. @@ -17,64 +17,7 @@ package io.element.android.libraries.matrix.api.tracing data class TracingConfiguration( - val overrides: Map = emptyMap() -) { - - // Order should matters - private val targets: MutableMap = mutableMapOf( - Target.Common to LogLevel.Warn, - Target.Hyper to LogLevel.Warn, - Target.Sled to LogLevel.Warn, - Target.MatrixSdk.Root to LogLevel.Warn, - Target.MatrixSdk.Sled to LogLevel.Warn, - Target.MatrixSdk.Crypto to LogLevel.Debug, - Target.MatrixSdk.HttpClient to LogLevel.Debug, - Target.MatrixSdk.SlidingSync to LogLevel.Trace, - Target.MatrixSdk.BaseSlidingSync to LogLevel.Trace, - ) - - val filter: String - get() { - overrides.forEach { (target, logLevel) -> - targets[target] = logLevel - } - return targets.map { - if (it.key.filter.isEmpty()) { - it.value.filter - } else { - "${it.key.filter}=${it.value.filter}" - } - }.joinToString(separator = ",") - } -} - -sealed class Target(open val filter: String) { - object Common : Target("") - object Hyper : Target("hyper") - object Sled : Target("sled") - sealed class MatrixSdk(override val filter: String) : Target(filter) { - object Root : MatrixSdk("matrix_sdk") - object Sled : MatrixSdk("matrix_sdk_sled") - object Crypto: MatrixSdk("matrix_sdk_crypto") - object FFI : MatrixSdk("matrix_sdk_ffi") - object HttpClient : MatrixSdk("matrix_sdk::http_client") - object UniffiAPI : MatrixSdk("matrix_sdk_ffi::uniffi_api") - object SlidingSync : MatrixSdk("matrix_sdk::sliding_sync") - object BaseSlidingSync : MatrixSdk("matrix_sdk_base::sliding_sync") - } -} - -sealed class LogLevel(val filter: String) { - object Warn : LogLevel("warn") - object Trace : LogLevel("trace") - object Info : LogLevel("info") - object Debug : LogLevel("debug") - object Error : LogLevel("error") -} - -object TracingConfigurations { - val release = TracingConfiguration(overrides = mapOf(Target.Common to LogLevel.Info)) - val debug = TracingConfiguration(overrides = mapOf(Target.Common to LogLevel.Info)) - - fun custom(overrides: Map) = TracingConfiguration(overrides) -} + val filterConfiguration: TracingFilterConfiguration, + val writesToLogcat: Boolean, + val writesToFilesConfiguration: WriteToFilesConfiguration, +) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingFilterConfiguration.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingFilterConfiguration.kt new file mode 100644 index 0000000000..21c6954c2a --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingFilterConfiguration.kt @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2022 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.libraries.matrix.api.tracing + +data class TracingFilterConfiguration( + val overrides: Map = emptyMap(), +) { + + // Order should matters + private val targetsToLogLevel: MutableMap = mutableMapOf( + Target.COMMON to LogLevel.Info, + Target.HYPER to LogLevel.Warn, + Target.MATRIX_SDK_CRYPTO to LogLevel.Debug, + Target.MATRIX_SDK_HTTP_CLIENT to LogLevel.Debug, + Target.MATRIX_SDK_SLIDING_SYNC to LogLevel.Trace, + Target.MATRIX_SDK_BASE_SLIDING_SYNC to LogLevel.Trace, + Target.MATRIX_SDK_UI_TIMELINE to LogLevel.Info, + ) + + val filter: String + get() { + overrides.forEach { (target, logLevel) -> + targetsToLogLevel[target] = logLevel + } + return targetsToLogLevel.map { + if (it.key.filter.isEmpty()) { + it.value.filter + } else { + "${it.key.filter}=${it.value.filter}" + } + }.joinToString(separator = ",") + } +} + +enum class Target(open val filter: String) { + COMMON(""), + ELEMENT("elementx"), + HYPER("hyper"), + MATRIX_SDK_FFI("matrix_sdk_ffi"), + MATRIX_SDK_UNIFFI_API("matrix_sdk_ffi::uniffi_api"), + MATRIX_SDK_CRYPTO("matrix_sdk_crypto"), + MATRIX_SDK_HTTP_CLIENT("matrix_sdk::http_client"), + MATRIX_SDK_SLIDING_SYNC("matrix_sdk::sliding_sync"), + MATRIX_SDK_BASE_SLIDING_SYNC("matrix_sdk_base::sliding_sync"), + MATRIX_SDK_UI_TIMELINE("matrix_sdk_ui::timeline"), +} + +sealed class LogLevel(val filter: String) { + object Warn : LogLevel("warn") + object Trace : LogLevel("trace") + object Info : LogLevel("info") + object Debug : LogLevel("debug") + object Error : LogLevel("error") +} + +object TracingFilterConfigurations { + val release = TracingFilterConfiguration( + overrides = mapOf( + Target.COMMON to LogLevel.Info, + Target.ELEMENT to LogLevel.Debug + ), + ) + val debug = TracingFilterConfiguration( + overrides = mapOf( + Target.COMMON to LogLevel.Info, + Target.ELEMENT to LogLevel.Trace + ) + ) + + /** + * Use this method to create a custom configuration where all targets will have the same log level. + */ + fun custom(logLevel: LogLevel) = TracingFilterConfiguration(overrides = Target.values().associateWith { logLevel }) + + /** + * Use this method to override the log level of specific targets. + */ + fun custom(overrides: Map) = TracingFilterConfiguration(overrides) +} diff --git a/app/src/main/kotlin/io/element/android/x/initializer/TimberInitializer.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingService.kt similarity index 51% rename from app/src/main/kotlin/io/element/android/x/initializer/TimberInitializer.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingService.kt index 5a641d75c6..4a74f83b20 100644 --- a/app/src/main/kotlin/io/element/android/x/initializer/TimberInitializer.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingService.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * 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. @@ -14,22 +14,11 @@ * limitations under the License. */ -package io.element.android.x.initializer +package io.element.android.libraries.matrix.api.tracing -import android.content.Context -import androidx.startup.Initializer -import io.element.android.features.rageshake.impl.logs.VectorFileLogger -import io.element.android.x.BuildConfig import timber.log.Timber -class TimberInitializer : Initializer { - - override fun create(context: Context) { - if (BuildConfig.DEBUG) { - Timber.plant(Timber.DebugTree()) - } - Timber.plant(VectorFileLogger(context)) - } - - override fun dependencies(): List>> = emptyList() +interface TracingService { + fun setupTracing(tracingConfiguration: TracingConfiguration) + fun createTimberTree(): Timber.Tree } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/WriteToFilesConfiguration.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/WriteToFilesConfiguration.kt new file mode 100644 index 0000000000..cafa375a6a --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/WriteToFilesConfiguration.kt @@ -0,0 +1,22 @@ +/* + * 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.libraries.matrix.api.tracing + +sealed class WriteToFilesConfiguration { + object Disabled : WriteToFilesConfiguration() + data class Enabled(val directory: String, val filenamePrefix: String) : WriteToFilesConfiguration() +} diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts index 499e36988c..016833def6 100644 --- a/libraries/matrix/impl/build.gradle.kts +++ b/libraries/matrix/impl/build.gradle.kts @@ -29,7 +29,7 @@ anvil { } dependencies { -// api(projects.libraries.rustsdk) + // implementation(projects.libraries.rustsdk) implementation(libs.matrix.sdk) implementation(projects.libraries.di) implementation(projects.libraries.androidutils) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt index ca40ca400c..2546614a78 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt @@ -37,12 +37,12 @@ class RustSyncService( ) : SyncService { override suspend fun startSync() = runCatching { - Timber.v("Start sync") + Timber.i("Start sync") innerSyncService.start() } override fun stopSync() = runCatching { - Timber.v("Stop sync") + Timber.i("Stop sync") innerSyncService.pause() } @@ -50,7 +50,7 @@ class RustSyncService( roomListStateFlow .map(RoomListServiceState::toSyncState) .onEach { state -> - Timber.v("Sync state=$state") + Timber.i("Sync state=$state") } .distinctUntilChanged() .stateIn(sessionCoroutineScope, SharingStarted.Eagerly, SyncState.Idle) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/LogEventLocation.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/LogEventLocation.kt new file mode 100644 index 0000000000..51b8923cc8 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/LogEventLocation.kt @@ -0,0 +1,42 @@ +/* + * 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.libraries.matrix.impl.tracing + +/** + * This class is used to provide file, line, column information to the Rust SDK [org.matrix.rustcomponents.sdk.logEvent] method. + * The data is extracted from a [StackTraceElement] instance. + */ +data class LogEventLocation( + val file: String, + val line: UInt, + val column: UInt, +) { + + companion object { + /** + * Create a [LogEventLocation] from a [StackTraceElement]. + */ + fun from(stackTraceElement: StackTraceElement): LogEventLocation { + return LogEventLocation( + file = stackTraceElement.fileName, + line = stackTraceElement.lineNumber.toUInt(), + column = 0u, + ) + } + } +} + diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt new file mode 100644 index 0000000000..d3bd53bd10 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt @@ -0,0 +1,51 @@ +/* + * 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.libraries.matrix.impl.tracing + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.matrix.api.tracing.TracingConfiguration +import io.element.android.libraries.matrix.api.tracing.TracingService +import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration +import org.matrix.rustcomponents.sdk.TracingFileConfiguration +import timber.log.Timber +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class RustTracingService @Inject constructor() : TracingService { + + override fun setupTracing(tracingConfiguration: TracingConfiguration) { + val filter = tracingConfiguration.filterConfiguration + val rustTracingConfiguration = org.matrix.rustcomponents.sdk.TracingConfiguration( + filter = tracingConfiguration.filterConfiguration.filter, + writeToStdoutOrSystem = tracingConfiguration.writesToLogcat, + writeToFiles = when (val writeToFilesConfiguration = tracingConfiguration.writesToFilesConfiguration) { + is WriteToFilesConfiguration.Disabled -> null + is WriteToFilesConfiguration.Enabled -> TracingFileConfiguration( + path = writeToFilesConfiguration.directory, + filePrefix = writeToFilesConfiguration.filenamePrefix, + ) + }, + ) + org.matrix.rustcomponents.sdk.setupTracing(rustTracingConfiguration) + Timber.v("Tracing config filter = $filter") + } + + override fun createTimberTree(): Timber.Tree { + return RustTracingTree() + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingTree.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingTree.kt new file mode 100644 index 0000000000..2131bb83ba --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingTree.kt @@ -0,0 +1,75 @@ +/* + * 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.libraries.matrix.impl.tracing + +import android.util.Log +import io.element.android.libraries.matrix.api.tracing.Target +import org.matrix.rustcomponents.sdk.LogLevel +import org.matrix.rustcomponents.sdk.logEvent +import timber.log.Timber + +/** + * List of fully qualified class names to ignore when looking for the first stack trace element. + */ +private val fqcnIgnore = listOf( + Timber::class.java.name, + Timber.Forest::class.java.name, + Timber.Tree::class.java.name, + RustTracingTree::class.java.name, +) + +/** + * A Timber tree that passes logs to the Rust SDK. + */ +internal class RustTracingTree : Timber.Tree() { + + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + val location = getLogEventLocationFromStackTrace() + val logLevel = priority.toLogLevel() + logEvent( + file = location.file, + line = location.line, + column = location.column, + level = logLevel, + target = Target.ELEMENT.filter, + message = message, + ) + } + + /** + * Extract the [LogEventLocation] from the stack trace. + */ + private fun getLogEventLocationFromStackTrace(): LogEventLocation { + return Throwable(null, null).stackTrace + .first { it.className !in fqcnIgnore } + .let(LogEventLocation::from) + } +} + +/** + * Convert a log priority to a Rust SDK log level. + */ +private fun Int.toLogLevel(): LogLevel { + return when (this) { + Log.VERBOSE -> LogLevel.TRACE + Log.DEBUG -> LogLevel.DEBUG + Log.INFO -> LogLevel.INFO + Log.WARN -> LogLevel.WARN + Log.ERROR -> LogLevel.ERROR + else -> LogLevel.DEBUG + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/TracingConfiguration.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/TracingConfiguration.kt deleted file mode 100644 index ac8c62aeb6..0000000000 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/TracingConfiguration.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2022 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.libraries.matrix.impl.tracing - -import io.element.android.libraries.matrix.api.tracing.TracingConfiguration -import timber.log.Timber - -fun setupTracing(tracingConfiguration: TracingConfiguration) { - val filter = tracingConfiguration.filter - Timber.v("Tracing config filter = $filter") - val rustTracingConfiguration = org.matrix.rustcomponents.sdk.TracingConfiguration( - filter = filter, - writeToStdoutOrSystem = true, - writeToFiles = null, - ) - org.matrix.rustcomponents.sdk.setupTracing(rustTracingConfiguration) -} diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt index 5f8c6555a5..77057d2e45 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt @@ -17,19 +17,24 @@ package io.element.android.samples.minimal import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.matrix.impl.tracing.setupTracing -import io.element.android.libraries.matrix.api.tracing.TracingConfigurations +import io.element.android.libraries.matrix.api.tracing.TracingConfiguration +import io.element.android.libraries.matrix.api.tracing.TracingFilterConfigurations +import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration +import io.element.android.libraries.matrix.impl.tracing.RustTracingService import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.MainScope import kotlinx.coroutines.plus -import timber.log.Timber object Singleton { init { - Timber.plant(Timber.DebugTree()) - setupTracing(TracingConfigurations.debug) + val tracingConfiguration = TracingConfiguration( + filterConfiguration = TracingFilterConfigurations.debug, + writesToLogcat = true, + writesToFilesConfiguration = WriteToFilesConfiguration.Disabled + ) + RustTracingService().setupTracing(tracingConfiguration) } val appScope = MainScope() + CoroutineName("Minimal Scope") From 4e94d4da6b5703cc4eb614dc375980dba12e2f5c Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 9 Aug 2023 14:12:39 +0200 Subject: [PATCH 235/251] Enable filter push notifications by push rules (#1041) * Enable filter push notifications by push rules * Remove unused `filterByPushRules` parameter * Use fallback notification only for items not filetered by the push rules * Fix tests --- changelog.d/640.bugfix | 1 + .../matrix/api/notification/NotificationService.kt | 2 +- .../android/libraries/matrix/impl/RustMatrixClient.kt | 2 +- .../matrix/impl/notification/RustNotificationService.kt | 1 - .../matrix/test/notification/FakeNotificationService.kt | 2 +- .../push/impl/notifications/NotifiableEventResolver.kt | 8 ++------ 6 files changed, 6 insertions(+), 10 deletions(-) create mode 100644 changelog.d/640.bugfix diff --git a/changelog.d/640.bugfix b/changelog.d/640.bugfix new file mode 100644 index 0000000000..9edb67f4ce --- /dev/null +++ b/changelog.d/640.bugfix @@ -0,0 +1 @@ +Filter push notifications using push rules. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationService.kt index 4113953203..972873ab38 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationService.kt @@ -21,5 +21,5 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId interface NotificationService { - suspend fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId, filterByPushRules: Boolean): Result + suspend fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId): Result } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 09fd637dfd..545fbe0e3f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -100,7 +100,7 @@ class RustMatrixClient constructor( dispatchers = dispatchers, ) private val notificationClient = client.notificationClient().use { builder -> - builder.finish() + builder.filterByPushRules().finish() } private val notificationService = RustNotificationService(sessionId, notificationClient, dispatchers, clock) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt index 3dc432ad27..0f3aebd049 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt @@ -39,7 +39,6 @@ class RustNotificationService( userId: SessionId, roomId: RoomId, eventId: EventId, - filterByPushRules: Boolean, ): Result = withContext(dispatchers.io) { runCatching { val item = notificationClient.getNotification(roomId.value, eventId.value) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/FakeNotificationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/FakeNotificationService.kt index 5b863e65f8..7cb92d35c5 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/FakeNotificationService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/FakeNotificationService.kt @@ -23,7 +23,7 @@ import io.element.android.libraries.matrix.api.notification.NotificationData import io.element.android.libraries.matrix.api.notification.NotificationService class FakeNotificationService : NotificationService { - override suspend fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId, filterByPushRules: Boolean): Result { + override suspend fun getNotification(userId: SessionId, roomId: RoomId, eventId: EventId): Result { return Result.success(null) } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt index 55395101a6..e5af7785db 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt @@ -69,16 +69,12 @@ class NotifiableEventResolver @Inject constructor( userId = sessionId, roomId = roomId, eventId = eventId, - // FIXME should be true in the future, but right now it's broken - // (https://github.com/vector-im/element-x-android/issues/640#issuecomment-1612913658) - filterByPushRules = false, ).onFailure { Timber.tag(loggerTag.value).e(it, "Unable to resolve event: $eventId.") }.getOrNull() // TODO this notificationData is not always valid at the moment, sometimes the Rust SDK can't fetch the matching event return notificationData?.asNotifiableEvent(sessionId) - ?: fallbackNotifiableEvent(sessionId, roomId, eventId) } private fun NotificationData.asNotifiableEvent(userId: SessionId): NotifiableEvent? { @@ -119,10 +115,10 @@ class NotifiableEventResolver @Inject constructor( title = null, // TODO check if title is needed anymore ) } else { - null + fallbackNotifiableEvent(userId, roomId, eventId) } } - else -> null + else -> fallbackNotifiableEvent(userId, roomId, eventId) } } From 226a3dbf28eb69cbc3e8bbcf143937405d9a4f10 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 9 Aug 2023 14:37:43 +0200 Subject: [PATCH 236/251] Feature/fga/sync states (#1042) * Change RoomSummaryDataSource to RoomListService to better reflects the rust api * Better Sync management * Sync: improve sync spinner rendering * Sync: make test compiles * Sync: add more test for sync spinner * Sync: more clean-up * Sync: pr review --------- Co-authored-by: ganfra --- appnav/build.gradle.kts | 2 + .../android/appnav/LoggedInFlowNode.kt | 25 +-- .../appnav/loggedin/LoggedInPresenter.kt | 36 +++-- .../android/appnav/loggedin/LoggedInState.kt | 4 +- .../appnav/loggedin/LoggedInStateProvider.kt | 10 +- .../android/appnav/loggedin/LoggedInView.kt | 2 +- .../android/appnav/loggedin/SyncStateView.kt | 22 +-- .../appnav/loggedin/LoggedInPresenterTest.kt | 28 +++- .../room/LoadingRoomStateFlowFactoryTest.kt | 16 +- .../invitelist/impl/InviteListPresenter.kt | 7 +- .../impl/InviteListPresenterTests.kt | 76 ++++----- .../impl/forward/ForwardMessagesEvents.kt | 2 +- .../impl/forward/ForwardMessagesPresenter.kt | 6 +- .../impl/forward/ForwardMessagesState.kt | 2 +- .../forward/ForwardMessagesStateProvider.kt | 2 +- .../impl/forward/ForwardMessagesView.kt | 2 +- .../forward/ForwardMessagesPresenterTests.kt | 11 +- .../roomlist/impl/RoomListPresenter.kt | 2 +- .../DefaultInviteStateDataSource.kt | 7 +- .../impl/datasource/RoomListDataSource.kt | 9 +- .../roomlist/impl/RoomListPresenterTests.kt | 42 ++--- .../DefaultInviteStateDataSourceTest.kt | 34 ++-- .../libraries/matrix/api/MatrixClient.kt | 4 +- .../RoomList.kt} | 29 ++-- .../matrix/api/roomlist/RoomListService.kt | 56 +++++++ .../api/{room => roomlist}/RoomSummary.kt | 3 +- .../libraries/matrix/impl/RustMatrixClient.kt | 33 ++-- .../matrix/impl/di/SessionMatrixModule.kt | 6 +- .../matrix/impl/room/RoomContentForwarder.kt | 1 + .../impl/room/RustRoomSummaryDataSource.kt | 123 -------------- .../{room => roomlist}/RoomListExtensions.kt | 2 +- .../RoomSummaryDetailsFactory.kt | 5 +- .../RoomSummaryListProcessor.kt | 4 +- .../matrix/impl/roomlist/RustRoomList.kt | 29 ++++ .../impl/roomlist/RustRoomListService.kt | 151 ++++++++++++++++++ .../matrix/impl/sync/AppStateMapper.kt | 11 -- .../matrix/impl/sync/RustSyncService.kt | 12 +- .../matrix/impl/sync/SyncServiceExtension.kt | 4 +- .../libraries/matrix/test/FakeMatrixClient.kt | 6 +- .../matrix/test/room/RoomSummaryFixture.kt | 4 +- .../FakeRoomListService.kt} | 43 +++-- .../matrix/test/roomlist/SimpleRoomList.kt | 26 +++ .../matrix/ui/components/SelectedRoom.kt | 2 +- .../android/samples/minimal/RoomListScreen.kt | 2 +- 44 files changed, 547 insertions(+), 356 deletions(-) rename libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/{room/RoomSummaryDataSource.kt => roomlist/RoomList.kt} (65%) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt rename libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/{room => roomlist}/RoomSummary.kt (92%) delete mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt rename libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/{room => roomlist}/RoomListExtensions.kt (98%) rename libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/{room => roomlist}/RoomSummaryDetailsFactory.kt (89%) rename libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/{room => roomlist}/RoomSummaryListProcessor.kt (97%) create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomList.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt rename libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/{room/FakeRoomSummaryDataSource.kt => roomlist/FakeRoomListService.kt} (51%) create mode 100644 libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimpleRoomList.kt diff --git a/appnav/build.gradle.kts b/appnav/build.gradle.kts index 6abc3c656b..cffd318fb1 100644 --- a/appnav/build.gradle.kts +++ b/appnav/build.gradle.kts @@ -65,6 +65,8 @@ dependencies { testImplementation(libs.test.truth) testImplementation(libs.test.turbine) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.features.networkmonitor.test) + testImplementation(projects.tests.testutils) testImplementation(projects.features.rageshake.test) testImplementation(projects.features.rageshake.impl) testImplementation(projects.services.appnavstate.test) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index fe4e31c70d..ec8beb16c1 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -69,10 +69,13 @@ import io.element.android.libraries.matrix.ui.di.MatrixUIBindings import io.element.android.libraries.push.api.notifications.NotificationDrawerManager import io.element.android.services.appnavstate.api.AppNavigationStateService import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize +import timber.log.Timber @ContributesNode(AppScope::class) class LoggedInFlowNode @AssistedInject constructor( @@ -123,7 +126,6 @@ class LoggedInFlowNode @AssistedInject constructor( override fun onBuilt() { super.onBuilt() - lifecycle.subscribe( onCreate = { plugins().forEach { it.onFlowCreated(id, inputs.matrixClient) } @@ -138,12 +140,8 @@ class LoggedInFlowNode @AssistedInject constructor( backstack.push(NavTarget.Ftue) } }, - onStart = { - lifecycleScope.launch { - syncService.startSync() - } - }, onStop = { + //Counterpart startSync is done in observeSyncStateAndNetworkStatus method. syncService.stopSync() }, onDestroy = { @@ -153,22 +151,24 @@ class LoggedInFlowNode @AssistedInject constructor( loggedInFlowProcessor.stopObserving() } ) - observeSyncStateAndNetworkStatus() } + @OptIn(FlowPreview::class) private fun observeSyncStateAndNetworkStatus() { lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.RESUMED) { + repeatOnLifecycle(Lifecycle.State.STARTED) { combine( - syncService.syncState, + // small debounce to avoid spamming startSync when the state is changing quickly in case of error. + syncService.syncState.debounce(100), networkMonitor.connectivity ) { syncState, networkStatus -> - syncState == SyncState.Error && networkStatus == NetworkStatus.Online + Pair(syncState, networkStatus) } .distinctUntilChanged() - .collect { restartSync -> - if (restartSync) { + .collect { (syncState, networkStatus) -> + Timber.d("Sync state: $syncState, network status: $networkStatus") + if (syncState != SyncState.Running && networkStatus == NetworkStatus.Online) { syncService.startSync() } } @@ -351,3 +351,4 @@ class LoggedInFlowNode @AssistedInject constructor( backstack.push(NavTarget.InviteList) } } + diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt index 8910cc3976..6d386a17e5 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInPresenter.kt @@ -21,16 +21,27 @@ import android.os.Build import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import io.element.android.features.networkmonitor.api.NetworkMonitor +import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.permissions.api.PermissionsPresenter import io.element.android.libraries.permissions.noop.NoopPermissionsPresenter import io.element.android.libraries.push.api.PushService +import kotlinx.coroutines.delay import javax.inject.Inject +private const val DELAY_BEFORE_SHOWING_SYNC_SPINNER_IN_MILLIS = 1500L + class LoggedInPresenter @Inject constructor( private val matrixClient: MatrixClient, private val permissionsPresenterFactory: PermissionsPresenter.Factory, + private val networkMonitor: NetworkMonitor, private val pushService: PushService, ) : Presenter { @@ -53,18 +64,25 @@ class LoggedInPresenter @Inject constructor( pushService.registerWith(matrixClient, pushProvider, distributor) } - val syncState = matrixClient.syncService().syncState.collectAsState() + val roomListState by matrixClient.roomListService.state.collectAsState() + val networkStatus by networkMonitor.connectivity.collectAsState() val permissionsState = postNotificationPermissionsPresenter.present() - - // fun handleEvents(event: LoggedInEvents) { - // when (event) { - // } - // } - + var showSyncSpinner by remember { + mutableStateOf(false) + } + LaunchedEffect(roomListState, networkStatus) { + showSyncSpinner = when { + networkStatus == NetworkStatus.Offline -> false + roomListState == RoomListService.State.Running -> false + else -> { + delay(DELAY_BEFORE_SHOWING_SYNC_SPINNER_IN_MILLIS) + true + } + } + } return LoggedInState( - syncState = syncState.value, + showSyncSpinner = showSyncSpinner, permissionsState = permissionsState, - // eventSink = ::handleEvents ) } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt index 075242cddb..bb06952a50 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInState.kt @@ -16,11 +16,9 @@ package io.element.android.appnav.loggedin -import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.permissions.api.PermissionsState data class LoggedInState( - val syncState: SyncState, + val showSyncSpinner: Boolean, val permissionsState: PermissionsState, - // val eventSink: (LoggedInEvents) -> Unit ) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt index e8a8a4762c..3cfb03f123 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInStateProvider.kt @@ -17,22 +17,20 @@ package io.element.android.appnav.loggedin import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.permissions.api.createDummyPostNotificationPermissionsState open class LoggedInStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( - aLoggedInState(), - aLoggedInState(syncState = SyncState.Idle), + aLoggedInState(false), + aLoggedInState(true), // Add other state here ) } fun aLoggedInState( - syncState: SyncState = SyncState.Running, + showSyncSpinner: Boolean = true, ) = LoggedInState( - syncState = syncState, + showSyncSpinner = showSyncSpinner, permissionsState = createDummyPostNotificationPermissionsState(), - // eventSink = {} ) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt index 964cb447aa..0ade93a795 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInView.kt @@ -47,7 +47,7 @@ fun LoggedInView( modifier = Modifier .padding(top = 8.dp) .align(Alignment.TopCenter), - syncState = state.syncState, + isVisible = state.showSyncSpinner, ) PermissionsView( state = state.permissionsState, diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt index bdec2b528f..6d045a431a 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/SyncStateView.kt @@ -38,19 +38,18 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings @Composable fun SyncStateView( - syncState: SyncState, + isVisible: Boolean, modifier: Modifier = Modifier ) { val animationSpec = spring(stiffness = 500F) AnimatedVisibility( modifier = modifier, - visible = syncState.mustBeVisible(), + visible = isVisible, enter = fadeIn(animationSpec = animationSpec), exit = fadeOut(animationSpec = animationSpec), ) { @@ -60,15 +59,15 @@ fun SyncStateView( ) { Row( modifier = Modifier - .background(color = ElementTheme.colors.bgSubtleSecondary) - .padding(horizontal = 24.dp, vertical = 10.dp), + .background(color = ElementTheme.colors.bgSubtleSecondary) + .padding(horizontal = 24.dp, vertical = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(10.dp) ) { CircularProgressIndicator( modifier = Modifier - .progressSemantics() - .size(12.dp), + .progressSemantics() + .size(12.dp), color = ElementTheme.colors.textPrimary, strokeWidth = 1.5.dp, ) @@ -82,20 +81,13 @@ fun SyncStateView( } } -private fun SyncState.mustBeVisible() = when (this) { - SyncState.Idle -> true /* Cold start of the app */ - SyncState.Running -> false - SyncState.Error -> false /* In this case, the network error banner can be displayed */ - SyncState.Terminated -> true /* The app is resumed and the sync is started again */ -} - @DayNightPreviews @Composable internal fun SyncStateViewPreview() = ElementPreview { // Add a box to see the shadow Box(modifier = Modifier.padding(24.dp)) { SyncStateView( - syncState = SyncState.Idle + isVisible = true ) } } diff --git a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt index a811d0283d..4abc89e7ee 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/loggedin/LoggedInPresenterTest.kt @@ -20,13 +20,18 @@ 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.networkmonitor.api.NetworkStatus +import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.libraries.permissions.api.PermissionsPresenter import io.element.android.libraries.permissions.noop.NoopPermissionsPresenter import io.element.android.libraries.push.api.PushService import io.element.android.libraries.pushproviders.api.Distributor import io.element.android.libraries.pushproviders.api.PushProvider +import io.element.android.tests.testutils.consumeItemsUntilPredicate import kotlinx.coroutines.test.runTest import org.junit.Test @@ -42,14 +47,33 @@ class LoggedInPresenterTest { } } - private fun createPresenter(): LoggedInPresenter { + @Test + fun `present - show sync spinner`() = runTest { + val roomListService = FakeRoomListService() + val presenter = createPresenter(roomListService, NetworkStatus.Online) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.showSyncSpinner).isFalse() + consumeItemsUntilPredicate { it.showSyncSpinner } + roomListService.postState(RoomListService.State.Running) + consumeItemsUntilPredicate { !it.showSyncSpinner } + } + } + + private fun createPresenter( + roomListService: RoomListService = FakeRoomListService(), + networkStatus: NetworkStatus = NetworkStatus.Offline + ): LoggedInPresenter { return LoggedInPresenter( - matrixClient = FakeMatrixClient(), + matrixClient = FakeMatrixClient(roomListService = roomListService), permissionsPresenterFactory = object : PermissionsPresenter.Factory { override fun create(permission: String): PermissionsPresenter { return NoopPermissionsPresenter() } }, + networkMonitor = FakeNetworkMonitor(networkStatus), pushService = object : PushService { override fun notificationStyleChanged() { } diff --git a/appnav/src/test/kotlin/io/element/android/appnav/room/LoadingRoomStateFlowFactoryTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/room/LoadingRoomStateFlowFactoryTest.kt index 17b6f6deb9..f56367e5f8 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/room/LoadingRoomStateFlowFactoryTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/room/LoadingRoomStateFlowFactoryTest.kt @@ -18,12 +18,12 @@ package io.element.android.appnav.room import app.cash.turbine.test import com.google.common.truth.Truth -import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource +import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.FakeMatrixRoom -import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource +import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import kotlinx.coroutines.test.runTest import org.junit.Test @@ -47,29 +47,29 @@ class LoadingRoomStateFlowFactoryTest { @Test fun `flow should emit Loading and then Loaded when there is a room in cache after SS is loaded`() = runTest { val room = FakeMatrixRoom(sessionId= A_SESSION_ID, roomId = A_ROOM_ID) - val roomSummaryDataSource = FakeRoomSummaryDataSource() - val matrixClient = FakeMatrixClient(A_SESSION_ID, roomSummaryDataSource = roomSummaryDataSource) + val roomListService = FakeRoomListService() + val matrixClient = FakeMatrixClient(A_SESSION_ID, roomListService = roomListService) val flowFactory = LoadingRoomStateFlowFactory(matrixClient) flowFactory .create(this, A_ROOM_ID) .test { Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loading) matrixClient.givenGetRoomResult(A_ROOM_ID, room) - roomSummaryDataSource.postLoadingState(RoomSummaryDataSource.LoadingState.Loaded(1)) + roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1)) Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loaded(room)) } } @Test fun `flow should emit Loading and then Error when there is no room in cache after SS is loaded`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource() - val matrixClient = FakeMatrixClient(A_SESSION_ID, roomSummaryDataSource = roomSummaryDataSource) + val roomListService = FakeRoomListService() + val matrixClient = FakeMatrixClient(A_SESSION_ID, roomListService = roomListService) val flowFactory = LoadingRoomStateFlowFactory(matrixClient) flowFactory .create(this, A_ROOM_ID) .test { Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Loading) - roomSummaryDataSource.postLoadingState(RoomSummaryDataSource.LoadingState.Loaded(1)) + roomListService.postAllRoomsLoadingState(RoomList.LoadingState.Loaded(1)) Truth.assertThat(awaitItem()).isEqualTo(LoadingRoomState.Error) } } diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt index bba497e2fd..8c43b815ae 100644 --- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt +++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/InviteListPresenter.kt @@ -36,7 +36,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.room.RoomSummary +import io.element.android.libraries.matrix.api.roomlist.RoomSummary import io.element.android.libraries.push.api.notifications.NotificationDrawerManager import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.extensions.toAnalyticsJoinedRoom @@ -56,8 +56,9 @@ class InviteListPresenter @Inject constructor( @Composable override fun present(): InviteListState { val invites by client - .roomSummaryDataSource - .inviteRooms() + .roomListService + .invites() + .summaries .collectAsState() var seenInvites by remember { mutableStateOf>(emptySet()) } diff --git a/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt b/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt index 03e0f46de8..f3eef2784c 100644 --- a/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt +++ b/features/invitelist/impl/src/test/kotlin/io/element/android/features/invitelist/impl/InviteListPresenterTests.kt @@ -30,8 +30,8 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipState -import io.element.android.libraries.matrix.api.room.RoomSummary -import io.element.android.libraries.matrix.api.room.RoomSummaryDetails +import io.element.android.libraries.matrix.api.roomlist.RoomSummary +import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID_2 @@ -40,7 +40,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.FakeMatrixRoom -import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource +import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.libraries.push.api.notifications.NotificationDrawerManager import io.element.android.libraries.push.test.notifications.FakeNotificationDrawerManager import io.element.android.services.analytics.api.AnalyticsService @@ -51,9 +51,9 @@ class InviteListPresenterTests { @Test fun `present - starts empty, adds invites when received`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource() + val roomListService = FakeRoomListService() val presenter = createPresenter( - FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) + FakeMatrixClient(roomListService = roomListService) ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -61,7 +61,7 @@ class InviteListPresenterTests { val initialState = awaitItem() Truth.assertThat(initialState.inviteList).isEmpty() - roomSummaryDataSource.postInviteRooms(listOf(aRoomSummary())) + roomListService.postInviteRooms(listOf(aRoomSummary())) val withInviteState = awaitItem() Truth.assertThat(withInviteState.inviteList.size).isEqualTo(1) @@ -72,9 +72,9 @@ class InviteListPresenterTests { @Test fun `present - uses user ID and avatar for direct invites`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource().withDirectChatInvitation() + val roomListService = FakeRoomListService().withDirectChatInvitation() val presenter = createPresenter( - FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) + FakeMatrixClient(roomListService = roomListService) ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -98,9 +98,9 @@ class InviteListPresenterTests { @Test fun `present - includes sender details for room invites`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation() + val roomListService = FakeRoomListService().withRoomInvitation() val presenter = createPresenter( - FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) + FakeMatrixClient(roomListService = roomListService) ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -122,10 +122,10 @@ class InviteListPresenterTests { @Test fun `present - shows confirm dialog for declining direct chat invites`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource().withDirectChatInvitation() + val roomListService = FakeRoomListService().withDirectChatInvitation() val presenter = InviteListPresenter( FakeMatrixClient( - roomSummaryDataSource = roomSummaryDataSource, + roomListService = roomListService, ), FakeSeenInvitesStore(), FakeAnalyticsService(), @@ -148,9 +148,9 @@ class InviteListPresenterTests { @Test fun `present - shows confirm dialog for declining room invites`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation() + val roomListService = FakeRoomListService().withRoomInvitation() val presenter = createPresenter( - FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) + FakeMatrixClient(roomListService = roomListService) ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -169,9 +169,9 @@ class InviteListPresenterTests { @Test fun `present - hides confirm dialog when cancelling`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation() + val roomListService = FakeRoomListService().withRoomInvitation() val presenter = createPresenter( - FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) + FakeMatrixClient(roomListService = roomListService) ) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -190,10 +190,10 @@ class InviteListPresenterTests { @Test fun `present - declines invite after confirming`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation() + val roomListService = FakeRoomListService().withRoomInvitation() val fakeNotificationDrawerManager = FakeNotificationDrawerManager() val client = FakeMatrixClient( - roomSummaryDataSource = roomSummaryDataSource, + roomListService = roomListService, ) val room = FakeMatrixRoom() val presenter = createPresenter(client = client, notificationDrawerManager = fakeNotificationDrawerManager) @@ -217,9 +217,9 @@ class InviteListPresenterTests { @Test fun `present - declines invite after confirming and sets state on error`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation() + val roomListService = FakeRoomListService().withRoomInvitation() val client = FakeMatrixClient( - roomSummaryDataSource = roomSummaryDataSource, + roomListService = roomListService, ) val room = FakeMatrixRoom() val presenter = createPresenter(client) @@ -247,9 +247,9 @@ class InviteListPresenterTests { @Test fun `present - dismisses declining error state`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation() + val roomListService = FakeRoomListService().withRoomInvitation() val client = FakeMatrixClient( - roomSummaryDataSource = roomSummaryDataSource, + roomListService = roomListService, ) val room = FakeMatrixRoom() val presenter = createPresenter(client) @@ -279,10 +279,10 @@ class InviteListPresenterTests { @Test fun `present - accepts invites and sets state on success`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation() + val roomListService = FakeRoomListService().withRoomInvitation() val fakeNotificationDrawerManager = FakeNotificationDrawerManager() val client = FakeMatrixClient( - roomSummaryDataSource = roomSummaryDataSource, + roomListService = roomListService, ) val room = FakeMatrixRoom() val presenter = createPresenter(client = client, notificationDrawerManager = fakeNotificationDrawerManager) @@ -303,9 +303,9 @@ class InviteListPresenterTests { @Test fun `present - accepts invites and sets state on error`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation() + val roomListService = FakeRoomListService().withRoomInvitation() val client = FakeMatrixClient( - roomSummaryDataSource = roomSummaryDataSource, + roomListService = roomListService, ) val room = FakeMatrixRoom() val presenter = createPresenter(client) @@ -325,9 +325,9 @@ class InviteListPresenterTests { @Test fun `present - dismisses accepting error state`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource().withRoomInvitation() + val roomListService = FakeRoomListService().withRoomInvitation() val client = FakeMatrixClient( - roomSummaryDataSource = roomSummaryDataSource, + roomListService = roomListService, ) val room = FakeMatrixRoom() val presenter = createPresenter(client) @@ -352,11 +352,11 @@ class InviteListPresenterTests { @Test fun `present - stores seen invites when received`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource() + val roomListService = FakeRoomListService() val store = FakeSeenInvitesStore() val presenter = InviteListPresenter( FakeMatrixClient( - roomSummaryDataSource = roomSummaryDataSource, + roomListService = roomListService, ), store, FakeAnalyticsService(), @@ -368,19 +368,19 @@ class InviteListPresenterTests { awaitItem() // When one invite is received, that ID is saved - roomSummaryDataSource.postInviteRooms(listOf(aRoomSummary())) + roomListService.postInviteRooms(listOf(aRoomSummary())) awaitItem() Truth.assertThat(store.getProvidedRoomIds()).isEqualTo(setOf(A_ROOM_ID)) // When a second is added, both are saved - roomSummaryDataSource.postInviteRooms(listOf(aRoomSummary(), aRoomSummary(A_ROOM_ID_2))) + roomListService.postInviteRooms(listOf(aRoomSummary(), aRoomSummary(A_ROOM_ID_2))) awaitItem() Truth.assertThat(store.getProvidedRoomIds()).isEqualTo(setOf(A_ROOM_ID, A_ROOM_ID_2)) // When they're both dismissed, an empty set is saved - roomSummaryDataSource.postInviteRooms(listOf()) + roomListService.postInviteRooms(listOf()) awaitItem() Truth.assertThat(store.getProvidedRoomIds()).isEmpty() @@ -389,12 +389,12 @@ class InviteListPresenterTests { @Test fun `present - marks invite as new if they're unseen`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource() + val roomListService = FakeRoomListService() val store = FakeSeenInvitesStore() store.publishRoomIds(setOf(A_ROOM_ID)) val presenter = InviteListPresenter( FakeMatrixClient( - roomSummaryDataSource = roomSummaryDataSource, + roomListService = roomListService, ), store, FakeAnalyticsService(), @@ -405,7 +405,7 @@ class InviteListPresenterTests { }.test { awaitItem() - roomSummaryDataSource.postInviteRooms(listOf(aRoomSummary(), aRoomSummary(A_ROOM_ID_2))) + roomListService.postInviteRooms(listOf(aRoomSummary(), aRoomSummary(A_ROOM_ID_2))) skipItems(1) val withInviteState = awaitItem() @@ -417,7 +417,7 @@ class InviteListPresenterTests { } } - private suspend fun FakeRoomSummaryDataSource.withRoomInvitation(): FakeRoomSummaryDataSource { + private suspend fun FakeRoomListService.withRoomInvitation(): FakeRoomListService { postInviteRooms( listOf( RoomSummary.Filled( @@ -446,7 +446,7 @@ class InviteListPresenterTests { return this } - private suspend fun FakeRoomSummaryDataSource.withDirectChatInvitation(): FakeRoomSummaryDataSource { + private suspend fun FakeRoomListService.withDirectChatInvitation(): FakeRoomListService { postInviteRooms( listOf( RoomSummary.Filled( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesEvents.kt index 6b74918d71..0ae406efff 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesEvents.kt @@ -16,7 +16,7 @@ package io.element.android.features.messages.impl.forward -import io.element.android.libraries.matrix.api.room.RoomSummaryDetails +import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails sealed interface ForwardMessagesEvents { data class SetSelectedRoom(val room: RoomSummaryDetails) : ForwardMessagesEvents diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenter.kt index e1d7ed3e7e..273ed5906d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenter.kt @@ -35,8 +35,8 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.MatrixRoom -import io.element.android.libraries.matrix.api.room.RoomSummary -import io.element.android.libraries.matrix.api.room.RoomSummaryDetails +import io.element.android.libraries.matrix.api.roomlist.RoomSummary +import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList @@ -65,7 +65,7 @@ class ForwardMessagesPresenter @AssistedInject constructor( var results: SearchBarResultState> by remember { mutableStateOf(SearchBarResultState.NotSearching()) } val forwardingActionState: MutableState>> = remember { mutableStateOf(Async.Uninitialized) } - val summaries by client.roomSummaryDataSource.allRooms().collectAsState() + val summaries by client.roomListService.allRooms().summaries.collectAsState() LaunchedEffect(query, summaries) { val filteredSummaries = summaries.filterIsInstance() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesState.kt index 7540766097..953a7897f6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesState.kt @@ -18,7 +18,7 @@ package io.element.android.features.messages.impl.forward import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.room.RoomSummaryDetails +import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails import kotlinx.collections.immutable.ImmutableList data class ForwardMessagesState( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt index 75aacea616..56d7f63eb1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt @@ -20,7 +20,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.RoomMember -import io.element.android.libraries.matrix.api.room.RoomSummaryDetails +import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails import io.element.android.libraries.matrix.api.room.message.RoomMessage import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt index 0ff62d4788..0f00a6f090 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt @@ -63,7 +63,7 @@ import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.roomListRoomMessage import io.element.android.libraries.designsystem.theme.roomListRoomName import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.room.RoomSummaryDetails +import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails import io.element.android.libraries.matrix.ui.components.SelectedRoom import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt index 9d19932716..983b251df7 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/forward/ForwardMessagesPresenterTests.kt @@ -24,12 +24,12 @@ import io.element.android.features.messages.impl.forward.ForwardMessagesEvents import io.element.android.features.messages.impl.forward.ForwardMessagesPresenter import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.matrix.api.core.EventId -import io.element.android.libraries.matrix.api.room.RoomSummary +import io.element.android.libraries.matrix.api.roomlist.RoomSummary import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.FakeMatrixRoom -import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource import io.element.android.libraries.matrix.test.room.aRoomSummaryDetail +import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.test.runTest @@ -76,10 +76,10 @@ class ForwardMessagesPresenterTests { @Test fun `present - update query`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource().apply { + val roomListService = FakeRoomListService().apply { postAllRooms(listOf(RoomSummary.Filled(aRoomSummaryDetail()))) } - val client = FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) + val client = FakeMatrixClient(roomListService = roomListService) val presenter = aPresenter(client = client) moleculeFlow(RecompositionMode.Immediate) { presenter.present() @@ -166,11 +166,10 @@ class ForwardMessagesPresenterTests { } } - private fun CoroutineScope.aPresenter( + private fun CoroutineScope.aPresenter( eventId: EventId = AN_EVENT_ID, fakeMatrixRoom: FakeMatrixRoom = FakeMatrixRoom(), coroutineScope: CoroutineScope = this, client: FakeMatrixClient = FakeMatrixClient(), ) = ForwardMessagesPresenter(eventId.value, fakeMatrixRoom, coroutineScope, client) - } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt index 5a82b20723..dbef9c799b 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt @@ -135,6 +135,6 @@ class RoomListPresenter @Inject constructor( // Safe to give bigger size than room list val extendedRangeEnd = range.last + midExtendedRangeSize val extendedRange = IntRange(extendedRangeStart, extendedRangeEnd) - client.roomSummaryDataSource.updateAllRoomsVisibleRange(extendedRange) + client.roomListService.updateAllRoomsVisibleRange(extendedRange) } } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSource.kt index 3a89014799..e05efd3ddd 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSource.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSource.kt @@ -30,7 +30,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.room.RoomSummary +import io.element.android.libraries.matrix.api.roomlist.RoomSummary import kotlinx.coroutines.withContext import javax.inject.Inject @@ -44,8 +44,9 @@ class DefaultInviteStateDataSource @Inject constructor( @Composable override fun inviteState(): InvitesState { val invites by client - .roomSummaryDataSource - .inviteRooms() + .roomListService + .invites() + .summaries .collectAsState() val seenInvites by seenInvitesStore diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt index 714f6e2e11..e44bcd6b6b 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt @@ -27,8 +27,8 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.room.RoomSummary -import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource +import io.element.android.libraries.matrix.api.roomlist.RoomListService +import io.element.android.libraries.matrix.api.roomlist.RoomSummary import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -44,7 +44,7 @@ import kotlinx.coroutines.withContext import javax.inject.Inject class RoomListDataSource @Inject constructor( - private val roomSummaryDataSource: RoomSummaryDataSource, + private val roomListService: RoomListService, private val lastMessageTimestampFormatter: LastMessageTimestampFormatter, private val roomLastMessageFormatter: RoomLastMessageFormatter, private val coroutineDispatchers: CoroutineDispatchers, @@ -61,8 +61,9 @@ class RoomListDataSource @Inject constructor( } fun launchIn(coroutineScope: CoroutineScope) { - roomSummaryDataSource + roomListService .allRooms() + .summaries .onEach { roomSummaries -> replaceWith(roomSummaries) } diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index d2d52aecc8..eaa3801e12 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -47,8 +47,8 @@ import io.element.android.libraries.matrix.test.A_ROOM_NAME import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.FakeMatrixClient -import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled +import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService import io.element.android.tests.testutils.consumeItemsUntilPredicate import io.element.android.tests.testutils.testCoroutineDispatchers @@ -111,9 +111,9 @@ class RoomListPresenterTests { @Test fun `present - load 1 room with success`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource() + val roomListService = FakeRoomListService() val matrixClient = FakeMatrixClient( - roomSummaryDataSource = roomSummaryDataSource + roomListService = roomListService ) val presenter = createRoomListPresenter(matrixClient) moleculeFlow(RecompositionMode.Immediate) { @@ -123,7 +123,7 @@ class RoomListPresenterTests { // Room list is loaded with 16 placeholders Truth.assertThat(initialState.roomList.size).isEqualTo(16) Truth.assertThat(initialState.roomList.all { it.isPlaceholder }).isTrue() - roomSummaryDataSource.postAllRooms(listOf(aRoomSummaryFilled())) + roomListService.postAllRooms(listOf(aRoomSummaryFilled())) val withRoomState = consumeItemsUntilPredicate { state -> state.roomList.size == 1 }.last() Truth.assertThat(withRoomState.roomList.size).isEqualTo(1) Truth.assertThat(withRoomState.roomList.first()) @@ -133,15 +133,15 @@ class RoomListPresenterTests { @Test fun `present - load 1 room with success and filter rooms`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource() + val roomListService = FakeRoomListService() val matrixClient = FakeMatrixClient( - roomSummaryDataSource = roomSummaryDataSource + roomListService = roomListService ) val presenter = createRoomListPresenter(matrixClient) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - roomSummaryDataSource.postAllRooms(listOf(aRoomSummaryFilled())) + roomListService.postAllRooms(listOf(aRoomSummaryFilled())) val loadedState = consumeItemsUntilPredicate { state -> state.roomList.size == 1 }.last() // Test filtering with result loadedState.eventSink.invoke(RoomListEvents.UpdateFilter(A_ROOM_NAME.substring(0, 3))) @@ -160,39 +160,39 @@ class RoomListPresenterTests { @Test fun `present - update visible range`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource() + val roomListService = FakeRoomListService() val matrixClient = FakeMatrixClient( - roomSummaryDataSource = roomSummaryDataSource + roomListService = roomListService ) val presenter = createRoomListPresenter(matrixClient) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - roomSummaryDataSource.postAllRooms(listOf(aRoomSummaryFilled())) + roomListService.postAllRooms(listOf(aRoomSummaryFilled())) val loadedState = awaitItem() // check initial value - Truth.assertThat(roomSummaryDataSource.latestSlidingSyncRange).isNull() + Truth.assertThat(roomListService.latestSlidingSyncRange).isNull() // Test empty range loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(1, 0))) - Truth.assertThat(roomSummaryDataSource.latestSlidingSyncRange).isNull() + Truth.assertThat(roomListService.latestSlidingSyncRange).isNull() // Update visible range and check that range is transmitted to the SDK after computation loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(0, 0))) - Truth.assertThat(roomSummaryDataSource.latestSlidingSyncRange) + Truth.assertThat(roomListService.latestSlidingSyncRange) .isEqualTo(IntRange(0, 20)) loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(0, 1))) - Truth.assertThat(roomSummaryDataSource.latestSlidingSyncRange) + Truth.assertThat(roomListService.latestSlidingSyncRange) .isEqualTo(IntRange(0, 21)) loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(19, 29))) - Truth.assertThat(roomSummaryDataSource.latestSlidingSyncRange) + Truth.assertThat(roomListService.latestSlidingSyncRange) .isEqualTo(IntRange(0, 49)) loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(49, 59))) - Truth.assertThat(roomSummaryDataSource.latestSlidingSyncRange) + Truth.assertThat(roomListService.latestSlidingSyncRange) .isEqualTo(IntRange(29, 79)) loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(149, 159))) - Truth.assertThat(roomSummaryDataSource.latestSlidingSyncRange) + Truth.assertThat(roomListService.latestSlidingSyncRange) .isEqualTo(IntRange(129, 179)) loadedState.eventSink.invoke(RoomListEvents.UpdateVisibleRange(IntRange(149, 259))) - Truth.assertThat(roomSummaryDataSource.latestSlidingSyncRange) + Truth.assertThat(roomListService.latestSlidingSyncRange) .isEqualTo(IntRange(129, 279)) cancelAndIgnoreRemainingEvents() } @@ -200,9 +200,9 @@ class RoomListPresenterTests { @Test fun `present - handle DismissRequestVerificationPrompt`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource() + val roomListService = FakeRoomListService() val matrixClient = FakeMatrixClient( - roomSummaryDataSource = roomSummaryDataSource + roomListService = roomListService ) val presenter = createRoomListPresenter( client = matrixClient, @@ -317,7 +317,7 @@ class RoomListPresenterTests { inviteStateDataSource = inviteStateDataSource, leaveRoomPresenter = leaveRoomPresenter, roomListDataSource = RoomListDataSource( - client.roomSummaryDataSource, + client.roomListService, lastMessageTimestampFormatter, roomLastMessageFormatter, coroutineDispatchers = testCoroutineDispatchers() diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSourceTest.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSourceTest.kt index 88e69068fd..ce7bf685a8 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSourceTest.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/datasource/DefaultInviteStateDataSourceTest.kt @@ -25,8 +25,8 @@ import io.element.android.features.roomlist.impl.InvitesState import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_ID_2 import io.element.android.libraries.matrix.test.FakeMatrixClient -import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled +import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.test.runTest import org.junit.Test @@ -35,8 +35,8 @@ internal class DefaultInviteStateDataSourceTest { @Test fun `emits NoInvites state if invites list is empty`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource() - val client = FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) + val roomListService = FakeRoomListService() + val client = FakeMatrixClient(roomListService = roomListService) val seenStore = FakeSeenInvitesStore() val dataSource = DefaultInviteStateDataSource(client, seenStore, testCoroutineDispatchers()) @@ -49,9 +49,9 @@ internal class DefaultInviteStateDataSourceTest { @Test fun `emits NewInvites state if unseen invite exists`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource() - roomSummaryDataSource.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID))) - val client = FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) + val roomListService = FakeRoomListService() + roomListService.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID))) + val client = FakeMatrixClient(roomListService = roomListService) val seenStore = FakeSeenInvitesStore() val dataSource = DefaultInviteStateDataSource(client, seenStore, testCoroutineDispatchers()) @@ -65,9 +65,9 @@ internal class DefaultInviteStateDataSourceTest { @Test fun `emits NewInvites state if multiple invites exist and at least one is unseen`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource() - roomSummaryDataSource.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(roomId = A_ROOM_ID_2))) - val client = FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) + val roomListService = FakeRoomListService() + roomListService.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(roomId = A_ROOM_ID_2))) + val client = FakeMatrixClient(roomListService = roomListService) val seenStore = FakeSeenInvitesStore() seenStore.publishRoomIds(setOf(A_ROOM_ID)) val dataSource = DefaultInviteStateDataSource(client, seenStore, testCoroutineDispatchers(useUnconfinedTestDispatcher = true)) @@ -82,9 +82,9 @@ internal class DefaultInviteStateDataSourceTest { @Test fun `emits SeenInvites state if invite exists in seen store`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource() - roomSummaryDataSource.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID))) - val client = FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) + val roomListService = FakeRoomListService() + roomListService.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID))) + val client = FakeMatrixClient(roomListService = roomListService) val seenStore = FakeSeenInvitesStore() seenStore.publishRoomIds(setOf(A_ROOM_ID)) val dataSource = DefaultInviteStateDataSource(client, seenStore, testCoroutineDispatchers(useUnconfinedTestDispatcher = true)) @@ -100,8 +100,8 @@ internal class DefaultInviteStateDataSourceTest { @Test fun `emits new state in response to upstream events`() = runTest { - val roomSummaryDataSource = FakeRoomSummaryDataSource() - val client = FakeMatrixClient(roomSummaryDataSource = roomSummaryDataSource) + val roomListService = FakeRoomListService() + val client = FakeMatrixClient(roomListService = roomListService) val seenStore = FakeSeenInvitesStore() val dataSource = DefaultInviteStateDataSource(client, seenStore, testCoroutineDispatchers()) @@ -112,7 +112,7 @@ internal class DefaultInviteStateDataSourceTest { Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NoInvites) // When a single invite is received, state should be NewInvites - roomSummaryDataSource.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID))) + roomListService.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID))) skipItems(1) Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NewInvites) @@ -122,12 +122,12 @@ internal class DefaultInviteStateDataSourceTest { Truth.assertThat(awaitItem()).isEqualTo(InvitesState.SeenInvites) // Another new invite resets it to NewInvites - roomSummaryDataSource.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(roomId = A_ROOM_ID_2))) + roomListService.postInviteRooms(listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(roomId = A_ROOM_ID_2))) skipItems(1) Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NewInvites) // All of the invites going away reverts to NoInvites - roomSummaryDataSource.postInviteRooms(emptyList()) + roomListService.postInviteRooms(emptyList()) skipItems(1) Truth.assertThat(awaitItem()).isEqualTo(InvitesState.NoInvites) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index 747de5f554..67c0625a91 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -26,7 +26,7 @@ import io.element.android.libraries.matrix.api.notification.NotificationService import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomMembershipObserver -import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource +import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults import io.element.android.libraries.matrix.api.user.MatrixUser @@ -35,7 +35,7 @@ import java.io.Closeable interface MatrixClient : Closeable { val sessionId: SessionId - val roomSummaryDataSource: RoomSummaryDataSource + val roomListService: RoomListService val mediaLoader: MatrixMediaLoader suspend fun getRoom(roomId: RoomId): MatrixRoom? suspend fun findDM(userId: UserId): MatrixRoom? diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummaryDataSource.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt similarity index 65% rename from libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummaryDataSource.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt index d677d56ed9..8714bc2c5c 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummaryDataSource.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomList.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.api.room +package io.element.android.libraries.matrix.api.roomlist import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.flow.StateFlow @@ -23,25 +23,34 @@ import kotlinx.coroutines.withTimeout import timber.log.Timber import kotlin.time.Duration -interface RoomSummaryDataSource { - +/** + * Holds some flows related to a specific set of rooms. + * Can be retrieved from [RoomListService] methods. + */ +interface RoomList { sealed class LoadingState { object NotLoaded : LoadingState() data class Loaded(val numberOfRooms: Int) : LoadingState() } - fun updateAllRoomsVisibleRange(range: IntRange) - fun allRoomsLoadingState(): StateFlow - fun allRooms(): StateFlow> - fun inviteRooms(): StateFlow> + /** + * The list of room summaries as a flow. + */ + val summaries: StateFlow> + + /** + * The loading state of the room list as a flow. + * This is useful to know if a specific set of rooms is loaded or not. + */ + val loadingState: StateFlow } -suspend fun RoomSummaryDataSource.awaitAllRoomsAreLoaded(timeout: Duration = Duration.INFINITE) { +suspend fun RoomList.awaitLoaded(timeout: Duration = Duration.INFINITE) { try { Timber.d("awaitAllRoomsAreLoaded: wait") withTimeout(timeout) { - allRoomsLoadingState().firstOrNull { - it is RoomSummaryDataSource.LoadingState.Loaded + loadingState.firstOrNull { + it is RoomList.LoadingState.Loaded } } } catch (timeoutException: TimeoutCancellationException) { diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt new file mode 100644 index 0000000000..99381d0e74 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt @@ -0,0 +1,56 @@ +/* + * 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.libraries.matrix.api.roomlist + +import kotlinx.coroutines.flow.StateFlow + +/** + * Entry point for the room list api. + * This service will provide different sets of rooms (all, invites, etc.). + * It requires the SyncService to be started to receive updates. + */ +interface RoomListService { + + sealed class State { + object Idle : State() + object Running : State() + object Error : State() + object Terminated : State() + } + + /** + * returns a [RoomList] object of all rooms we want to display. + * This will exclude some rooms like the invites, or spaces. + */ + fun allRooms(): RoomList + + /** + * returns a [RoomList] object of all invites. + */ + fun invites(): RoomList + + /** + * Will set the visible range of all rooms. + * This is useful to load more data when the user scrolls down. + */ + fun updateAllRoomsVisibleRange(range: IntRange) + + /** + * The state of the service as a flow. + */ + val state: StateFlow +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummary.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt similarity index 92% rename from libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummary.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt index 7dedd86b63..87cf2139d6 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomSummary.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt @@ -14,9 +14,10 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.api.room +package io.element.android.libraries.matrix.api.roomlist import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.message.RoomMessage sealed interface RoomSummary { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 545fbe0e3f..8f5cfa496b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -32,8 +32,8 @@ import io.element.android.libraries.matrix.api.notification.NotificationService import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomMembershipObserver -import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource -import io.element.android.libraries.matrix.api.room.awaitAllRoomsAreLoaded +import io.element.android.libraries.matrix.api.roomlist.RoomListService +import io.element.android.libraries.matrix.api.roomlist.awaitLoaded import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults @@ -45,9 +45,8 @@ import io.element.android.libraries.matrix.impl.notification.RustNotificationSer import io.element.android.libraries.matrix.impl.pushers.RustPushersService import io.element.android.libraries.matrix.impl.room.RoomContentForwarder import io.element.android.libraries.matrix.impl.room.RustMatrixRoom -import io.element.android.libraries.matrix.impl.room.RustRoomSummaryDataSource -import io.element.android.libraries.matrix.impl.room.roomOrNull -import io.element.android.libraries.matrix.impl.room.stateFlow +import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService +import io.element.android.libraries.matrix.impl.roomlist.roomOrNull import io.element.android.libraries.matrix.impl.sync.RustSyncService import io.element.android.libraries.matrix.impl.usersearch.UserProfileMapper import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper @@ -90,11 +89,11 @@ class RustMatrixClient constructor( ) : MatrixClient { override val sessionId: UserId = UserId(client.userId()) - private val roomListService = syncService.roomListService() + private val innerRoomListService = syncService.roomListService() private val sessionDispatcher = dispatchers.io.limitedParallelism(64) private val sessionCoroutineScope = appCoroutineScope.childScope(dispatchers.main, "Session-${sessionId}") private val verificationService = RustSessionVerificationService() - private val rustSyncService = RustSyncService(syncService, roomListService.stateFlow(), sessionCoroutineScope) + private val rustSyncService = RustSyncService(syncService, sessionCoroutineScope) private val pushersService = RustPushersService( client = client, dispatchers = dispatchers, @@ -122,15 +121,15 @@ class RustMatrixClient constructor( } } - private val rustRoomSummaryDataSource: RustRoomSummaryDataSource = - RustRoomSummaryDataSource( - roomListService = roomListService, + private val rustRoomListService: RoomListService = + RustRoomListService( + innerRoomListService = innerRoomListService, sessionCoroutineScope = sessionCoroutineScope, dispatcher = sessionDispatcher, ) - override val roomSummaryDataSource: RoomSummaryDataSource - get() = rustRoomSummaryDataSource + override val roomListService: RoomListService + get() = rustRoomListService private val rustMediaLoader = RustMediaLoader(baseCacheDirectory, dispatchers, client) override val mediaLoader: MatrixMediaLoader @@ -138,7 +137,7 @@ class RustMatrixClient constructor( private val roomMembershipObserver = RoomMembershipObserver() - private val roomContentForwarder = RoomContentForwarder(roomListService) + private val roomContentForwarder = RoomContentForwarder(innerRoomListService) init { client.setDelegate(clientDelegate) @@ -156,7 +155,7 @@ class RustMatrixClient constructor( var cachedPairOfRoom = pairOfRoom(roomId) if (cachedPairOfRoom == null) { //... otherwise, lets wait for the SS to load all rooms and check again. - roomSummaryDataSource.awaitAllRoomsAreLoaded() + roomListService.allRooms().awaitLoaded() cachedPairOfRoom = pairOfRoom(roomId) } cachedPairOfRoom?.let { (roomListItem, fullRoom) -> @@ -174,7 +173,7 @@ class RustMatrixClient constructor( } private fun pairOfRoom(roomId: RoomId): Pair? { - val cachedRoomListItem = roomListService.roomOrNull(roomId.value) + val cachedRoomListItem = innerRoomListService.roomOrNull(roomId.value) val fullRoom = cachedRoomListItem?.fullRoom() return if (cachedRoomListItem == null || fullRoom == null) { Timber.d("No room cached for $roomId") @@ -225,7 +224,7 @@ class RustMatrixClient constructor( // Wait to receive the room back from the sync withTimeout(30_000L) { - roomSummaryDataSource.allRooms() + roomListService.allRooms().summaries .filter { roomSummaries -> roomSummaries.map { it.identifier() }.contains(roomId.value) } @@ -273,7 +272,7 @@ class RustMatrixClient constructor( client.setDelegate(null) verificationService.destroy() syncService.destroy() - roomListService.destroy() + innerRoomListService.destroy() notificationClient.destroy() client.destroy() } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt index bf260be6ec..dba1dbd0a3 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt @@ -23,7 +23,7 @@ import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.room.RoomMembershipObserver -import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource +import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.verification.SessionVerificationService @Module @@ -40,8 +40,8 @@ object SessionMatrixModule { } @Provides - fun provideRoomSummaryDataSource(matrixClient: MatrixClient): RoomSummaryDataSource { - return matrixClient.roomSummaryDataSource + fun providesRoomListService(matrixClient: MatrixClient): RoomListService { + return matrixClient.roomListService } @Provides diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt index c4f272797a..8ee0361ace 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt @@ -20,6 +20,7 @@ import io.element.android.libraries.core.coroutine.parallelMap import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.ForwardEventException +import io.element.android.libraries.matrix.impl.roomlist.roomOrNull import kotlinx.coroutines.CancellationException import kotlinx.coroutines.withTimeout import org.matrix.rustcomponents.sdk.Room diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt deleted file mode 100644 index 133bf508ce..0000000000 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomSummaryDataSource.kt +++ /dev/null @@ -1,123 +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.libraries.matrix.impl.room - -import io.element.android.libraries.matrix.api.room.RoomSummary -import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import org.matrix.rustcomponents.sdk.RoomList -import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate -import org.matrix.rustcomponents.sdk.RoomListException -import org.matrix.rustcomponents.sdk.RoomListInput -import org.matrix.rustcomponents.sdk.RoomListLoadingState -import org.matrix.rustcomponents.sdk.RoomListRange -import org.matrix.rustcomponents.sdk.RoomListService -import org.matrix.rustcomponents.sdk.RoomListServiceState -import timber.log.Timber - -internal class RustRoomSummaryDataSource( - private val roomListService: RoomListService, - private val sessionCoroutineScope: CoroutineScope, - dispatcher: CoroutineDispatcher, - roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(), -) : RoomSummaryDataSource { - - private val allRooms = MutableStateFlow>(emptyList()) - private val inviteRooms = MutableStateFlow>(emptyList()) - - private val allRoomsLoadingState: MutableStateFlow = MutableStateFlow(RoomSummaryDataSource.LoadingState.NotLoaded) - private val allRoomsListProcessor = RoomSummaryListProcessor(allRooms, roomListService, roomSummaryDetailsFactory, shouldFetchFullRoom = false) - private val inviteRoomsListProcessor = RoomSummaryListProcessor(inviteRooms, roomListService, roomSummaryDetailsFactory, shouldFetchFullRoom = true) - - init { - sessionCoroutineScope.launch(dispatcher) { - val allRooms = roomListService.allRooms() - allRooms - .observeEntriesWithProcessor(allRoomsListProcessor) - .launchIn(this) - - allRooms - .loadingStateFlow() - .map { it.toRoomSummaryDataSourceLoadingState() } - .onEach { - allRoomsLoadingState.value = it - } - .launchIn(this) - - launch { - // Wait until running, as invites is only available after that - roomListService.stateFlow().first { - it == RoomListServiceState.RUNNING - } - roomListService.invites() - .observeEntriesWithProcessor(inviteRoomsListProcessor) - .launchIn(this) - } - } - } - - override fun allRooms(): StateFlow> { - return allRooms - } - - override fun inviteRooms(): StateFlow> { - return inviteRooms - } - - override fun allRoomsLoadingState(): StateFlow { - return allRoomsLoadingState - } - - override fun updateAllRoomsVisibleRange(range: IntRange) { - Timber.v("setVisibleRange=$range") - sessionCoroutineScope.launch { - try { - val ranges = listOf(RoomListRange(range.first.toUInt(), range.last.toUInt())) - roomListService.applyInput( - RoomListInput.Viewport(ranges) - ) - } catch (exception: RoomListException) { - Timber.e(exception, "Failed updating visible range") - } - } - } -} - -private fun RoomListLoadingState.toRoomSummaryDataSourceLoadingState(): RoomSummaryDataSource.LoadingState { - return when (this) { - is RoomListLoadingState.Loaded -> RoomSummaryDataSource.LoadingState.Loaded(maximumNumberOfRooms?.toInt() ?: 0) - is RoomListLoadingState.NotLoaded -> RoomSummaryDataSource.LoadingState.NotLoaded - } -} - -private fun RoomList.observeEntriesWithProcessor(processor: RoomSummaryListProcessor): Flow> { - return entriesFlow { roomListEntries -> - processor.postEntries(roomListEntries) - }.onEach { update -> - processor.postUpdate(update) - } -} - diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt similarity index 98% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt index 8e7047aaa4..8d96990a9e 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomListExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.impl.room +package io.element.android.libraries.matrix.impl.roomlist import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.matrix.impl.util.mxCallbackFlow diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryDetailsFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt similarity index 89% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryDetailsFactory.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt index 7dd7bf4581..b57eb892e0 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryDetailsFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt @@ -14,10 +14,11 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.impl.room +package io.element.android.libraries.matrix.impl.roomlist import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.room.RoomSummaryDetails +import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails +import io.element.android.libraries.matrix.impl.room.RoomMemberMapper import io.element.android.libraries.matrix.impl.room.message.RoomMessageFactory import org.matrix.rustcomponents.sdk.Room import org.matrix.rustcomponents.sdk.RoomListItem diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryListProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt similarity index 97% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryListProcessor.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt index ef54a0604d..9a67ff1f30 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomSummaryListProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryListProcessor.kt @@ -14,10 +14,10 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.impl.room +package io.element.android.libraries.matrix.impl.roomlist import io.element.android.libraries.core.coroutine.parallelMap -import io.element.android.libraries.matrix.api.room.RoomSummary +import io.element.android.libraries.matrix.api.roomlist.RoomSummary import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.sync.Mutex diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomList.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomList.kt new file mode 100644 index 0000000000..481b38dd9b --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomList.kt @@ -0,0 +1,29 @@ +/* + * 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.libraries.matrix.impl.roomlist + +import io.element.android.libraries.matrix.api.roomlist.RoomList +import io.element.android.libraries.matrix.api.roomlist.RoomSummary +import kotlinx.coroutines.flow.StateFlow + +/** + * Simple implementation of [RoomList] where state flows are provided through constructor. + */ +class RustRoomList( + override val summaries: StateFlow>, + override val loadingState: StateFlow +) : RoomList diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt new file mode 100644 index 0000000000..bf66bade3b --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt @@ -0,0 +1,151 @@ +/* + * 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.libraries.matrix.impl.roomlist + +import io.element.android.libraries.matrix.api.roomlist.RoomList +import io.element.android.libraries.matrix.api.roomlist.RoomListService +import io.element.android.libraries.matrix.api.roomlist.RoomSummary +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate +import org.matrix.rustcomponents.sdk.RoomListException +import org.matrix.rustcomponents.sdk.RoomListInput +import org.matrix.rustcomponents.sdk.RoomListLoadingState +import org.matrix.rustcomponents.sdk.RoomListRange +import org.matrix.rustcomponents.sdk.RoomListServiceState +import timber.log.Timber +import org.matrix.rustcomponents.sdk.RoomListService as InnerRustRoomListService + +class RustRoomListService( + private val innerRoomListService: InnerRustRoomListService, + private val sessionCoroutineScope: CoroutineScope, + dispatcher: CoroutineDispatcher, + roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(), +) : RoomListService { + + private val allRooms = MutableStateFlow>(emptyList()) + private val inviteRooms = MutableStateFlow>(emptyList()) + + private val allRoomsLoadingState: MutableStateFlow = MutableStateFlow(RoomList.LoadingState.NotLoaded) + private val allRoomsListProcessor = RoomSummaryListProcessor(allRooms, innerRoomListService, roomSummaryDetailsFactory, shouldFetchFullRoom = false) + private val invitesLoadingState: MutableStateFlow = MutableStateFlow(RoomList.LoadingState.NotLoaded) + private val inviteRoomsListProcessor = RoomSummaryListProcessor(inviteRooms, innerRoomListService, roomSummaryDetailsFactory, shouldFetchFullRoom = true) + + init { + sessionCoroutineScope.launch(dispatcher) { + val allRooms = innerRoomListService.allRooms() + allRooms + .observeEntriesWithProcessor(allRoomsListProcessor) + .launchIn(this) + allRooms + .observeLoadingState(allRoomsLoadingState) + .launchIn(this) + + + launch { + // Wait until running, as invites is only available after that + innerRoomListService.stateFlow().first { + it == RoomListServiceState.RUNNING + } + val invites = innerRoomListService.invites() + invites + .observeEntriesWithProcessor(inviteRoomsListProcessor) + .launchIn(this) + invites + .observeLoadingState(invitesLoadingState) + .launchIn(this) + + } + } + } + + override fun allRooms(): RoomList { + return RustRoomList(allRooms, allRoomsLoadingState) + } + + override fun invites(): RoomList { + return RustRoomList(inviteRooms, invitesLoadingState) + } + + override fun updateAllRoomsVisibleRange(range: IntRange) { + Timber.v("setVisibleRange=$range") + sessionCoroutineScope.launch { + try { + val ranges = listOf(RoomListRange(range.first.toUInt(), range.last.toUInt())) + innerRoomListService.applyInput( + RoomListInput.Viewport(ranges) + ) + } catch (exception: RoomListException) { + Timber.e(exception, "Failed updating visible range") + } + } + } + + override val state: StateFlow = + innerRoomListService.stateFlow() + .map { it.toRoomListState() } + .onEach { state -> + Timber.d("RoomList state=$state") + } + .distinctUntilChanged() + .stateIn(sessionCoroutineScope, SharingStarted.Eagerly, RoomListService.State.Idle) +} + +private fun RoomListLoadingState.toLoadingState(): RoomList.LoadingState { + return when (this) { + is RoomListLoadingState.Loaded -> RoomList.LoadingState.Loaded(maximumNumberOfRooms?.toInt() ?: 0) + RoomListLoadingState.NotLoaded -> RoomList.LoadingState.NotLoaded + } +} + +private fun RoomListServiceState.toRoomListState(): RoomListService.State { + return when (this) { + RoomListServiceState.INIT, + RoomListServiceState.SETTING_UP -> RoomListService.State.Idle + RoomListServiceState.RUNNING -> RoomListService.State.Running + RoomListServiceState.ERROR -> RoomListService.State.Error + RoomListServiceState.TERMINATED -> RoomListService.State.Terminated + } +} + +private fun org.matrix.rustcomponents.sdk.RoomList.observeEntriesWithProcessor(processor: RoomSummaryListProcessor): Flow> { + return entriesFlow { roomListEntries -> + processor.postEntries(roomListEntries) + }.onEach { update -> + processor.postUpdate(update) + } +} + +private fun org.matrix.rustcomponents.sdk.RoomList.observeLoadingState(stateFlow: MutableStateFlow): Flow { + return loadingStateFlow() + .map { it.toLoadingState() } + .onEach { + stateFlow.value = it + } +} + diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/AppStateMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/AppStateMapper.kt index 51228231f9..ae90f9fc44 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/AppStateMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/AppStateMapper.kt @@ -17,19 +17,8 @@ package io.element.android.libraries.matrix.impl.sync import io.element.android.libraries.matrix.api.sync.SyncState -import org.matrix.rustcomponents.sdk.RoomListServiceState import org.matrix.rustcomponents.sdk.SyncServiceState -internal fun RoomListServiceState.toSyncState(): SyncState { - return when (this) { - RoomListServiceState.INIT, - RoomListServiceState.SETTING_UP -> SyncState.Idle - RoomListServiceState.RUNNING -> SyncState.Running - RoomListServiceState.ERROR -> SyncState.Error - RoomListServiceState.TERMINATED -> SyncState.Terminated - } -} - internal fun SyncServiceState.toSyncState(): SyncState { return when (this) { SyncServiceState.IDLE -> SyncState.Idle diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt index 2546614a78..87fa02edd2 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt @@ -19,36 +19,38 @@ package io.element.android.libraries.matrix.impl.sync import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.sync.SyncState import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn -import org.matrix.rustcomponents.sdk.RoomListServiceState import org.matrix.rustcomponents.sdk.SyncServiceInterface +import org.matrix.rustcomponents.sdk.SyncServiceState import timber.log.Timber class RustSyncService( private val innerSyncService: SyncServiceInterface, - roomListStateFlow: Flow, sessionCoroutineScope: CoroutineScope ) : SyncService { override suspend fun startSync() = runCatching { Timber.i("Start sync") innerSyncService.start() + }.onFailure { + Timber.d("Start sync failed: $it") } override fun stopSync() = runCatching { Timber.i("Stop sync") innerSyncService.pause() + }.onFailure { + Timber.d("Stop sync failed: $it") } override val syncState: StateFlow = - roomListStateFlow - .map(RoomListServiceState::toSyncState) + innerSyncService.stateFlow() + .map(SyncServiceState::toSyncState) .onEach { state -> Timber.i("Sync state=$state") } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SyncServiceExtension.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SyncServiceExtension.kt index a8e366ff7b..c9e38ec7d4 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SyncServiceExtension.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/SyncServiceExtension.kt @@ -22,11 +22,11 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.trySendBlocking import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.buffer -import org.matrix.rustcomponents.sdk.SyncService +import org.matrix.rustcomponents.sdk.SyncServiceInterface import org.matrix.rustcomponents.sdk.SyncServiceState import org.matrix.rustcomponents.sdk.SyncServiceStateObserver -fun SyncService.stateFlow(): Flow = +fun SyncServiceInterface.stateFlow(): Flow = mxCallbackFlow { val listener = object : SyncServiceStateObserver { override fun onUpdate(state: SyncServiceState) { diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 1a654ac8d4..1229836e30 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -27,7 +27,7 @@ import io.element.android.libraries.matrix.api.notification.NotificationService import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomMembershipObserver -import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource +import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.verification.SessionVerificationService @@ -35,7 +35,7 @@ import io.element.android.libraries.matrix.test.media.FakeMediaLoader import io.element.android.libraries.matrix.test.notification.FakeNotificationService import io.element.android.libraries.matrix.test.pushers.FakePushersService import io.element.android.libraries.matrix.test.room.FakeMatrixRoom -import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource +import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.libraries.matrix.test.sync.FakeSyncService import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService import io.element.android.tests.testutils.simulateLongTask @@ -45,7 +45,7 @@ class FakeMatrixClient( override val sessionId: SessionId = A_SESSION_ID, private val userDisplayName: Result = Result.success(A_USER_NAME), private val userAvatarURLString: Result = Result.success(AN_AVATAR_URL), - override val roomSummaryDataSource: RoomSummaryDataSource = FakeRoomSummaryDataSource(), + override val roomListService: RoomListService = FakeRoomListService(), override val mediaLoader: MatrixMediaLoader = FakeMediaLoader(), private val sessionVerificationService: FakeSessionVerificationService = FakeSessionVerificationService(), private val pushersService: FakePushersService = FakePushersService(), diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt index 4df815c54c..59bac7ad40 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt @@ -20,8 +20,8 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.api.room.RoomSummary -import io.element.android.libraries.matrix.api.room.RoomSummaryDetails +import io.element.android.libraries.matrix.api.roomlist.RoomSummary +import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails import io.element.android.libraries.matrix.api.room.message.RoomMessage import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.matrix.api.timeline.item.event.EventContent diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeRoomSummaryDataSource.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt similarity index 51% rename from libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeRoomSummaryDataSource.kt rename to libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt index cae36e14c8..fa2e347e3b 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeRoomSummaryDataSource.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt @@ -14,18 +14,21 @@ * limitations under the License. */ -package io.element.android.libraries.matrix.test.room +package io.element.android.libraries.matrix.test.roomlist -import io.element.android.libraries.matrix.api.room.RoomSummary -import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource +import io.element.android.libraries.matrix.api.roomlist.RoomList +import io.element.android.libraries.matrix.api.roomlist.RoomListService +import io.element.android.libraries.matrix.api.roomlist.RoomSummary import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -class FakeRoomSummaryDataSource : RoomSummaryDataSource { +class FakeRoomListService : RoomListService { private val allRoomSummariesFlow = MutableStateFlow>(emptyList()) private val inviteRoomSummariesFlow = MutableStateFlow>(emptyList()) - private val allRoomsLoadingStateFlow = MutableStateFlow(RoomSummaryDataSource.LoadingState.NotLoaded) + private val allRoomsLoadingStateFlow = MutableStateFlow(RoomList.LoadingState.NotLoaded) + private val inviteRoomsLoadingStateFlow = MutableStateFlow(RoomList.LoadingState.NotLoaded) + private val roomListStateFlow = MutableStateFlow(RoomListService.State.Idle) suspend fun postAllRooms(roomSummaries: List) { allRoomSummariesFlow.emit(roomSummaries) @@ -35,20 +38,16 @@ class FakeRoomSummaryDataSource : RoomSummaryDataSource { inviteRoomSummariesFlow.emit(roomSummaries) } - suspend fun postLoadingState(loadingState: RoomSummaryDataSource.LoadingState) { + suspend fun postAllRoomsLoadingState(loadingState: RoomList.LoadingState) { allRoomsLoadingStateFlow.emit(loadingState) } - override fun allRoomsLoadingState(): StateFlow { - return allRoomsLoadingStateFlow + suspend fun postInviteRoomsLoadingState(loadingState: RoomList.LoadingState) { + inviteRoomsLoadingStateFlow.emit(loadingState) } - override fun allRooms(): StateFlow> { - return allRoomSummariesFlow - } - - override fun inviteRooms(): StateFlow> { - return inviteRoomSummariesFlow + suspend fun postState(state: RoomListService.State) { + roomListStateFlow.emit(state) } var latestSlidingSyncRange: IntRange? = null @@ -57,4 +56,20 @@ class FakeRoomSummaryDataSource : RoomSummaryDataSource { override fun updateAllRoomsVisibleRange(range: IntRange) { latestSlidingSyncRange = range } + + override fun allRooms(): RoomList { + return SimpleRoomList( + summaries = allRoomSummariesFlow, + loadingState = allRoomsLoadingStateFlow + ) + } + + override fun invites(): RoomList { + return SimpleRoomList( + summaries = inviteRoomSummariesFlow, + loadingState = inviteRoomsLoadingStateFlow + ) + } + + override val state: StateFlow = roomListStateFlow } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimpleRoomList.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimpleRoomList.kt new file mode 100644 index 0000000000..28b04ae318 --- /dev/null +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimpleRoomList.kt @@ -0,0 +1,26 @@ +/* + * 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.libraries.matrix.test.roomlist + +import io.element.android.libraries.matrix.api.roomlist.RoomList +import io.element.android.libraries.matrix.api.roomlist.RoomSummary +import kotlinx.coroutines.flow.StateFlow + +data class SimpleRoomList( + override val summaries: StateFlow>, + override val loadingState: StateFlow +) : RoomList diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt index 2881235335..da6f69d830 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/SelectedRoom.kt @@ -46,7 +46,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.room.RoomSummaryDetails +import io.element.android.libraries.matrix.api.roomlist.RoomSummaryDetails import io.element.android.libraries.ui.strings.CommonStrings @Composable diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt index 41dbc8bfe2..b394e1c04d 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt @@ -68,7 +68,7 @@ class RoomListScreen( inviteStateDataSource = DefaultInviteStateDataSource(matrixClient, DefaultSeenInvitesStore(context), coroutineDispatchers), leaveRoomPresenter = LeaveRoomPresenterImpl(matrixClient, RoomMembershipObserver(), coroutineDispatchers), roomListDataSource = RoomListDataSource( - roomSummaryDataSource = matrixClient.roomSummaryDataSource, + roomListService = matrixClient.roomListService, lastMessageTimestampFormatter = DefaultLastMessageTimestampFormatter(dateTimeProvider, dateFormatters), roomLastMessageFormatter = DefaultRoomLastMessageFormatter( sp = stringProvider, From 2a1a6bcdd91f93aa436be73ff9bd54f2f3f1aed4 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 9 Aug 2023 20:59:04 +0200 Subject: [PATCH 237/251] Sync: remove distincUntilChanged which can cause sync to stay blocked (#1044) Co-authored-by: ganfra --- .../main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index ec8beb16c1..1355035577 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -72,7 +72,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import timber.log.Timber @@ -165,7 +164,6 @@ class LoggedInFlowNode @AssistedInject constructor( ) { syncState, networkStatus -> Pair(syncState, networkStatus) } - .distinctUntilChanged() .collect { (syncState, networkStatus) -> Timber.d("Sync state: $syncState, network status: $networkStatus") if (syncState != SyncState.Running && networkStatus == NetworkStatus.Online) { From 65f4d06153e3c623d31009ac5db5f9637131659f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 10 Aug 2023 10:25:49 +0200 Subject: [PATCH 238/251] Update dependency app.cash.molecule:molecule-runtime to v1.2.0 (#1047) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9f93252e74..7f3540a2e8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ android_gradle_plugin = "8.0.2" kotlin = "1.8.22" ksp = "1.8.22-1.0.11" -molecule = "1.1.0" +molecule = "1.2.0" # AndroidX material = "1.9.0" From 0773e9921674f24a8e81e47d064ac854b280ea0d Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Fri, 11 Aug 2023 12:01:18 +0200 Subject: [PATCH 239/251] [Compound] implement IconButton changes (#1049) * Compound: implement IconButton changes. * Use `LocalContentColor` for tinting * Update screenshots * Add changelog --------- Co-authored-by: ElementBot --- changelog.d/1049.misc | 1 + .../theme/components/IconButton.kt | 36 +++++++++++++++---- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +-- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +-- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +-- ...ewDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +-- ...ewDarkPreview_0_null_8,NEXUS_5,1.0,en].png | 4 +-- ...ckButtonPreview_0_null,NEXUS_5,1.0,en].png | 4 +-- ...onButtonPreview_0_null,NEXUS_5,1.0,en].png | 4 +-- 9 files changed, 45 insertions(+), 20 deletions(-) create mode 100644 changelog.d/1049.misc diff --git a/changelog.d/1049.misc b/changelog.d/1049.misc new file mode 100644 index 0000000000..2b80eafa4f --- /dev/null +++ b/changelog.d/1049.misc @@ -0,0 +1 @@ +Compound: customise `IconButton` component. diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconButton.kt index 14cc6b62ce..7063ab1eb1 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconButton.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconButton.kt @@ -17,16 +17,22 @@ package io.element.android.libraries.designsystem.theme.components import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.material3.IconButtonDefaults +import androidx.compose.material3.LocalContentColor import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.theme.ElementTheme + +// Figma designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=1182%3A48861&mode=design&t=Shlcvznm1oUyqGC2-1 @Composable fun IconButton( @@ -36,11 +42,15 @@ fun IconButton( interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, content: @Composable () -> Unit ) { + val colors = IconButtonDefaults.iconButtonColors( + contentColor = LocalContentColor.current, + disabledContentColor = ElementTheme.colors.iconDisabled, + ) androidx.compose.material3.IconButton( onClick = onClick, modifier = modifier, enabled = enabled, - colors = IconButtonDefaults.iconButtonColors(), + colors = colors, interactionSource = interactionSource, content = content, ) @@ -53,12 +63,26 @@ internal fun IconButtonPreview() = @Composable private fun ContentToPreview() { - Row { - IconButton(onClick = {}) { - Icon(imageVector = Icons.Filled.Close, contentDescription = "") + Column { + CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.iconPrimary) { + Row { + IconButton(onClick = {}) { + Icon(imageVector = Icons.Filled.Close, contentDescription = "") + } + IconButton(enabled = false, onClick = {}) { + Icon(imageVector = Icons.Filled.Close, contentDescription = "") + } + } } - IconButton(enabled = false, onClick = {}) { - Icon(imageVector = Icons.Filled.Close, contentDescription = "") + CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.iconSecondary) { + Row { + IconButton(onClick = {}) { + Icon(imageVector = Icons.Filled.Close, contentDescription = "") + } + IconButton(enabled = false, onClick = {}) { + Icon(imageVector = Icons.Filled.Close, contentDescription = "") + } + } } } } diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 93e9b663f7..0c6de663a2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3b5474c29a881c61032396785a5c62732fc06f1e551fd6fae8ab620782774ce -size 395492 +oid sha256:5a55394e59395b36f7a1e964bdce7d9f7d5f33252c463d0de7a87c213117231a +size 395169 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index bbc32091b8..f644a8c108 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cde41cd981bd9ed350b6c4439cfda793946e2b7c14ea701af8fd5bb528329661 -size 395494 +oid sha256:44acd9adb83a35d38a9da31f8055988740d238e459d7e77145c5c2ad53725ad6 +size 395169 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 93e9b663f7..0c6de663a2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3b5474c29a881c61032396785a5c62732fc06f1e551fd6fae8ab620782774ce -size 395492 +oid sha256:5a55394e59395b36f7a1e964bdce7d9f7d5f33252c463d0de7a87c213117231a +size 395169 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png index c6afff3c8f..890541aa37 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e11d4107dc8894408eac31625b4061a59e2b5eb955229d4abc64ebb1191cdc9 -size 15614 +oid sha256:78bb8fcf8275cd3568f6f254c4d7ef1a4e7d251162c8b21bf2c85baaf2ab6c8d +size 15257 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png index 8aea9ffda4..82352056a3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a1805563eccf45f507152722511cacfa3f0411d1ccb90dd8d539f9a4a697b56f -size 14459 +oid sha256:43d8d24a65ab1174f6e262563b898d3168ce1e38a1af8e53b1a5a8af205f70f5 +size 14103 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.button_null_Buttons_BackButtonPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.button_null_Buttons_BackButtonPreview_0_null,NEXUS_5,1.0,en].png index 3713f657e8..b7bbe45340 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.button_null_Buttons_BackButtonPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.button_null_Buttons_BackButtonPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0186b4f2d64220e1b0341f5007f0577d82a0c0424ad53937b6669e772fbecc4d -size 7711 +oid sha256:c37c5cb1394da3a0b28fdd749f356f8a5d132f5408a88bc6e367052452a828d0 +size 7607 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_IconButtonPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_IconButtonPreview_0_null,NEXUS_5,1.0,en].png index f6c9fc64dc..d25604d927 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_IconButtonPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_IconButtonPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19db35d98bc8f6e4525e297616b564c9c2acbc2e9f7422292aca4a5d485c4314 -size 7682 +oid sha256:0de49c41130b525fd6baf687ccd63b3d98997d9a31d23a0ea826d0dbe7c1998e +size 10404 From 37a4d49494f7d232b8630d3233776315be993e98 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Fri, 11 Aug 2023 14:44:40 +0200 Subject: [PATCH 240/251] [Compound] Implement dialogs (#1043) * Implement dialogs following Compound tokens * Update screenshots * Fix confirmation dialog preview * Update screenshots * Add changelog * Add Figma designs link --------- Co-authored-by: ElementBot --- changelog.d/1043.misc | 1 + .../designsystem/components/ProgressDialog.kt | 2 +- .../components/dialogs/ConfirmationDialog.kt | 35 +--- .../components/dialogs/ErrorDialog.kt | 32 +--- .../components/dialogs/RetryDialog.kt | 45 ++---- .../components}/AlertDialogContent.kt | 150 +++++++++++++----- .../designsystem/theme/components/Button.kt | 11 +- .../components/previews/DatePickerPreview.kt | 2 +- .../components/previews/TimePickerPreview.kt | 2 +- .../android/libraries/theme/ElementTheme.kt | 8 + ...otDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...otDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...otDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...tLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...tLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...tLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_7,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_7,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ViewDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...iewLightPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...tentDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...entLightPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +- ...stDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...tLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...culeDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...uleLightPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...onDialogPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...orDialogPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...ryDialogPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...ssDialogPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...ckerPreviewDark_0_null,NEXUS_5,1.0,en].png | 4 +- ...kerPreviewLight_0_null,NEXUS_5,1.0,en].png | 4 +- ...rizontalPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...icalPreviewDark_0_null,NEXUS_5,1.0,en].png | 4 +- ...calPreviewLight_0_null,NEXUS_5,1.0,en].png | 4 +- ...tonLargePreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...onMediumPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...sageandokbutton_0_null,NEXUS_5,1.0,en].png | 3 + ...iconandokbutton_0_null,NEXUS_5,1.0,en].png | 3 + ...itleandokbutton_0_null,NEXUS_5,1.0,en].png | 3 + ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ViewDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...iewLightPreview_0_null,NEXUS_5,1.0,en].png | 4 +- 134 files changed, 399 insertions(+), 382 deletions(-) create mode 100644 changelog.d/1043.misc rename libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/{components/dialogs => theme/components}/AlertDialogContent.kt (68%) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithonlymessageandokbutton_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithtitle,iconandokbutton_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithtitleandokbutton_0_null,NEXUS_5,1.0,en].png diff --git a/changelog.d/1043.misc b/changelog.d/1043.misc new file mode 100644 index 0000000000..32cc442bc3 --- /dev/null +++ b/changelog.d/1043.misc @@ -0,0 +1 @@ +Compound: implement dialogs. diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt index 717f326779..20589c89ee 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/ProgressDialog.kt @@ -35,7 +35,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties -import io.element.android.libraries.designsystem.components.dialogs.DialogPreview +import io.element.android.libraries.designsystem.theme.components.DialogPreview import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt index c5725456a3..0c2fca84d5 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt @@ -17,18 +17,15 @@ package io.element.android.libraries.designsystem.components.dialogs import androidx.compose.material3.AlertDialog -import androidx.compose.material3.AlertDialogDefaults import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Shape import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.designsystem.theme.components.DialogPreview +import io.element.android.libraries.designsystem.theme.components.SimpleAlertDialogContent import io.element.android.libraries.ui.strings.CommonStrings @OptIn(ExperimentalMaterial3Api::class) @@ -44,13 +41,6 @@ fun ConfirmationDialog( thirdButtonText: String? = null, onCancelClicked: () -> Unit = onDismiss, onThirdButtonClicked: () -> Unit = {}, - shape: Shape = AlertDialogDefaults.shape, - containerColor: Color = AlertDialogDefaults.containerColor, - iconContentColor: Color = AlertDialogDefaults.iconContentColor, - // According to the design team, `primary` should be used here instead of the default `onSurface` - titleContentColor: Color = MaterialTheme.colorScheme.primary, - textContentColor: Color = AlertDialogDefaults.textContentColor, - tonalElevation: Dp = AlertDialogDefaults.TonalElevation, ) { AlertDialog(modifier = modifier, onDismissRequest = onDismiss) { ConfirmationDialogContent( @@ -62,12 +52,6 @@ fun ConfirmationDialog( onSubmitClicked = onSubmitClicked, onCancelClicked = onCancelClicked, onThirdButtonClicked = onThirdButtonClicked, - shape = shape, - containerColor = containerColor, - iconContentColor = iconContentColor, - titleContentColor = titleContentColor, - textContentColor = textContentColor, - tonalElevation = tonalElevation, ) } } @@ -83,12 +67,6 @@ private fun ConfirmationDialogContent( title: String? = null, thirdButtonText: String? = null, onThirdButtonClicked: () -> Unit = {}, - shape: Shape = AlertDialogDefaults.shape, - containerColor: Color = AlertDialogDefaults.containerColor, - iconContentColor: Color = AlertDialogDefaults.iconContentColor, - titleContentColor: Color = AlertDialogDefaults.titleContentColor, - textContentColor: Color = AlertDialogDefaults.textContentColor, - tonalElevation: Dp = AlertDialogDefaults.TonalElevation, icon: @Composable (() -> Unit)? = null, ) { SimpleAlertDialogContent( @@ -101,12 +79,6 @@ private fun ConfirmationDialogContent( onCancelClicked = onCancelClicked, thirdButtonText = thirdButtonText, onThirdButtonClicked = onThirdButtonClicked, - shape = shape, - containerColor = containerColor, - iconContentColor = iconContentColor, - titleContentColor = titleContentColor, - textContentColor = textContentColor, - tonalElevation = tonalElevation, icon = icon, ) } @@ -114,7 +86,7 @@ private fun ConfirmationDialogContent( @Preview(group = PreviewGroup.Dialogs) @Composable internal fun ConfirmationDialogPreview() = - ElementThemedPreview { + ElementThemedPreview(showBackground = false) { DialogPreview { ConfirmationDialogContent( content = "Content", @@ -124,6 +96,7 @@ internal fun ConfirmationDialogPreview() = thirdButtonText = "Disable", onSubmitClicked = {}, onCancelClicked = {}, + onThirdButtonClicked = {}, ) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt index d69281b6e9..fb5c511bf4 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt @@ -17,17 +17,15 @@ package io.element.android.libraries.designsystem.components.dialogs import androidx.compose.material3.AlertDialog -import androidx.compose.material3.AlertDialogDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Shape import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.designsystem.theme.components.DialogPreview +import io.element.android.libraries.designsystem.theme.components.SimpleAlertDialogContent import io.element.android.libraries.ui.strings.CommonStrings @OptIn(ExperimentalMaterial3Api::class) @@ -38,12 +36,6 @@ fun ErrorDialog( title: String = ErrorDialogDefaults.title, submitText: String = ErrorDialogDefaults.submitText, onDismiss: () -> Unit = {}, - shape: Shape = AlertDialogDefaults.shape, - containerColor: Color = AlertDialogDefaults.containerColor, - iconContentColor: Color = AlertDialogDefaults.iconContentColor, - titleContentColor: Color = AlertDialogDefaults.titleContentColor, - textContentColor: Color = AlertDialogDefaults.textContentColor, - tonalElevation: Dp = AlertDialogDefaults.TonalElevation, ) { AlertDialog(modifier = modifier, onDismissRequest = onDismiss) { ErrorDialogContent( @@ -51,12 +43,6 @@ fun ErrorDialog( content = content, submitText = submitText, onSubmitText = onDismiss, - shape = shape, - containerColor = containerColor, - iconContentColor = iconContentColor, - titleContentColor = titleContentColor, - textContentColor = textContentColor, - tonalElevation = tonalElevation, ) } } @@ -68,12 +54,6 @@ private fun ErrorDialogContent( title: String = ErrorDialogDefaults.title, submitText: String = ErrorDialogDefaults.submitText, onSubmitText: () -> Unit = {}, - shape: Shape = AlertDialogDefaults.shape, - containerColor: Color = AlertDialogDefaults.containerColor, - iconContentColor: Color = AlertDialogDefaults.iconContentColor, - titleContentColor: Color = AlertDialogDefaults.titleContentColor, - textContentColor: Color = AlertDialogDefaults.textContentColor, - tonalElevation: Dp = AlertDialogDefaults.TonalElevation, ) { SimpleAlertDialogContent( modifier = modifier, @@ -81,12 +61,6 @@ private fun ErrorDialogContent( content = content, cancelText = submitText, onCancelClicked = onSubmitText, - shape = shape, - containerColor = containerColor, - iconContentColor = iconContentColor, - titleContentColor = titleContentColor, - textContentColor = textContentColor, - tonalElevation = tonalElevation, ) } @@ -98,7 +72,7 @@ object ErrorDialogDefaults { @Preview(group = PreviewGroup.Dialogs) @Composable internal fun ErrorDialogPreview() { - ElementThemedPreview { + ElementThemedPreview(showBackground = false) { DialogPreview { ErrorDialogContent( content = "Content", diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt index 5e680456cb..85447b940b 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt @@ -17,17 +17,15 @@ package io.element.android.libraries.designsystem.components.dialogs import androidx.compose.material3.AlertDialog -import androidx.compose.material3.AlertDialogDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Shape import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.designsystem.theme.components.DialogPreview +import io.element.android.libraries.designsystem.theme.components.SimpleAlertDialogContent import io.element.android.libraries.ui.strings.CommonStrings @OptIn(ExperimentalMaterial3Api::class) @@ -40,12 +38,6 @@ fun RetryDialog( dismissText: String = RetryDialogDefaults.dismissText, onRetry: () -> Unit = {}, onDismiss: () -> Unit = {}, - shape: Shape = AlertDialogDefaults.shape, - containerColor: Color = AlertDialogDefaults.containerColor, - iconContentColor: Color = AlertDialogDefaults.iconContentColor, - titleContentColor: Color = AlertDialogDefaults.titleContentColor, - textContentColor: Color = AlertDialogDefaults.textContentColor, - tonalElevation: Dp = AlertDialogDefaults.TonalElevation, ) { AlertDialog(modifier = modifier, onDismissRequest = onDismiss) { RetryDialogContent( @@ -55,12 +47,6 @@ fun RetryDialog( dismissText = dismissText, onRetry = onRetry, onDismiss = onDismiss, - shape = shape, - containerColor = containerColor, - iconContentColor = iconContentColor, - titleContentColor = titleContentColor, - textContentColor = textContentColor, - tonalElevation = tonalElevation, ) } } @@ -74,12 +60,6 @@ private fun RetryDialogContent( dismissText: String = RetryDialogDefaults.dismissText, onRetry: () -> Unit = {}, onDismiss: () -> Unit = {}, - shape: Shape = AlertDialogDefaults.shape, - containerColor: Color = AlertDialogDefaults.containerColor, - iconContentColor: Color = AlertDialogDefaults.iconContentColor, - titleContentColor: Color = AlertDialogDefaults.titleContentColor, - textContentColor: Color = AlertDialogDefaults.textContentColor, - tonalElevation: Dp = AlertDialogDefaults.TonalElevation, ) { SimpleAlertDialogContent( modifier = modifier, @@ -89,12 +69,6 @@ private fun RetryDialogContent( onSubmitClicked = onRetry, cancelText = dismissText, onCancelClicked = onDismiss, - shape = shape, - containerColor = containerColor, - iconContentColor = iconContentColor, - titleContentColor = titleContentColor, - textContentColor = textContentColor, - tonalElevation = tonalElevation, ) } @@ -106,13 +80,12 @@ object RetryDialogDefaults { @Preview(group = PreviewGroup.Dialogs) @Composable -internal fun RetryDialogPreview() = ElementThemedPreview { ContentToPreview() } - -@Composable -private fun ContentToPreview() { - DialogPreview { - RetryDialogContent( - content = "Content", - ) +internal fun RetryDialogPreview() { + ElementThemedPreview(showBackground = false) { + DialogPreview { + RetryDialogContent( + content = "Content", + ) + } } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/AlertDialogContent.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt similarity index 68% rename from libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/AlertDialogContent.kt rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt index 7b6179079e..67a30e501d 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/AlertDialogContent.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.designsystem.components.dialogs +package io.element.android.libraries.designsystem.theme.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -22,28 +22,32 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.sizeIn -import androidx.compose.material3.AlertDialogDefaults +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Notifications import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ProvideTextStyle import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.Placeable +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.theme.components.ButtonSize -import io.element.android.libraries.designsystem.theme.components.Button -import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.theme.components.TextButton +import io.element.android.libraries.designsystem.preview.ElementThemedPreview +import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.theme.ElementTheme import kotlin.math.max +// Figma designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=911%3A343492&mode=design&t=jeyd1bXKOOx8y10r-1 + @Composable internal fun SimpleAlertDialogContent( content: String, @@ -55,12 +59,6 @@ internal fun SimpleAlertDialogContent( onSubmitClicked: () -> Unit = {}, thirdButtonText: String? = null, onThirdButtonClicked: () -> Unit = {}, - shape: Shape = AlertDialogDefaults.shape, - containerColor: Color = AlertDialogDefaults.containerColor, - iconContentColor: Color = AlertDialogDefaults.iconContentColor, - titleContentColor: Color = AlertDialogDefaults.titleContentColor, - textContentColor: Color = AlertDialogDefaults.textContentColor, - tonalElevation: Dp = AlertDialogDefaults.TonalElevation, icon: @Composable (() -> Unit)? = null, ) { AlertDialogContent( @@ -93,26 +91,26 @@ internal fun SimpleAlertDialogContent( } }, modifier = modifier, - title = { - if (title != null) { + title = title?.let { titleText -> + @Composable { Text( - text = title, - style = ElementTheme.typography.fontHeadingSmRegular, + text = titleText, + style = ElementTheme.typography.fontHeadingSmMedium, ) } }, text = { Text( text = content, - style = ElementTheme.typography.fontBodyMdRegular, + style = ElementTheme.materialTypography.bodyMedium, ) }, - shape = shape, - containerColor = containerColor, - iconContentColor = iconContentColor, - titleContentColor = titleContentColor, - textContentColor = textContentColor, - tonalElevation = tonalElevation, + shape = DialogContentDefaults.shape, + containerColor = DialogContentDefaults.containerColor, + iconContentColor = DialogContentDefaults.iconContentColor, + titleContentColor = DialogContentDefaults.titleContentColor, + textContentColor = DialogContentDefaults.textContentColor, + tonalElevation = 0.dp, icon = icon, // Note that a button content color is provided here from the dialog's token, but in // most cases, TextButtons should be used for dismiss and confirm buttons. @@ -122,6 +120,9 @@ internal fun SimpleAlertDialogContent( ) } +/** + * Copy of M3's `AlertDialogContent` so we can use it for previews. + */ @Composable internal fun AlertDialogContent( buttons: @Composable () -> Unit, @@ -144,13 +145,13 @@ internal fun AlertDialogContent( tonalElevation = tonalElevation, ) { Column( - modifier = Modifier.padding(DialogPadding) + modifier = Modifier.padding(DialogContentDefaults.externalPadding) ) { icon?.let { CompositionLocalProvider(LocalContentColor provides iconContentColor) { Box( Modifier - .padding(IconPadding) + .padding(DialogContentDefaults.iconPadding) .align(Alignment.CenterHorizontally) ) { icon() @@ -164,7 +165,7 @@ internal fun AlertDialogContent( Box( // Align the title to the center when an icon is present. Modifier - .padding(TitlePadding) + .padding(DialogContentDefaults.titlePadding) .align( if (icon == null) { Alignment.Start @@ -186,7 +187,7 @@ internal fun AlertDialogContent( Box( Modifier .weight(weight = 1f, fill = false) - .padding(TextPadding) + .padding(DialogContentDefaults.textPadding) .align(Alignment.Start) ) { text() @@ -210,7 +211,7 @@ internal fun AlertDialogContent( * customization. */ @Composable -internal fun AlertDialogFlowRow( +private fun AlertDialogFlowRow( mainAxisSpacing: Dp, crossAxisSpacing: Dp, content: @Composable () -> Unit @@ -237,7 +238,8 @@ internal fun AlertDialogFlowRow( if (sequences.isNotEmpty()) { crossAxisSpace += crossAxisSpacing.roundToPx() } - sequences += currentSequence.toList() + // Ensures that confirming actions appear above dismissive actions. + sequences.add(0, currentSequence.toList()) crossAxisSizes += currentCrossAxisSize crossAxisPositions += crossAxisSpace @@ -281,12 +283,11 @@ internal fun AlertDialogFlowRow( placeables[j].width + if (j < placeables.lastIndex) mainAxisSpacing.roundToPx() else 0 } - val arrangement = Arrangement.Bottom - // TODO(soboleva): rtl support - // Handle vertical direction + val arrangement = Arrangement.End val mainAxisPositions = IntArray(childrenMainAxisSizes.size) { 0 } with(arrangement) { - arrange(mainAxisLayoutSize, childrenMainAxisSizes, mainAxisPositions) + arrange(mainAxisLayoutSize, childrenMainAxisSizes, + layoutDirection, mainAxisPositions) } placeables.forEachIndexed { j, placeable -> placeable.place( @@ -311,14 +312,87 @@ internal fun DialogPreview(content: @Composable () -> Unit) { } } -// Paddings for each of the dialog's parts. -private val DialogPadding = PaddingValues(all = 24.dp) -private val IconPadding = PaddingValues(bottom = 16.dp) -private val TitlePadding = PaddingValues(bottom = 16.dp) -private val TextPadding = PaddingValues(bottom = 24.dp) +internal object DialogContentDefaults { + val shape = RoundedCornerShape(12.dp) + val externalPadding = PaddingValues(all = 24.dp) + val titlePadding = PaddingValues(bottom = 16.dp) + val iconPadding = PaddingValues(bottom = 8.dp) + val textPadding = PaddingValues(bottom = 16.dp) + val containerColor: Color + @Composable + @ReadOnlyComposable + get()= ElementTheme.colors.bgCanvasDefault + + val textContentColor: Color + @Composable + @ReadOnlyComposable + get()= ElementTheme.materialColors.onSurfaceVariant + + val titleContentColor: Color + @Composable + @ReadOnlyComposable + get()= ElementTheme.materialColors.onSurface + + val iconContentColor: Color + @Composable + @ReadOnlyComposable + get()= ElementTheme.materialColors.primary +} + +// Paddings for each of the dialog's parts. Taken from M3 source code. internal val ButtonsMainAxisSpacing = 8.dp internal val ButtonsCrossAxisSpacing = 12.dp internal val DialogMinWidth = 280.dp internal val DialogMaxWidth = 560.dp + +@Preview(group = PreviewGroup.Dialogs, name = "Dialog with title, icon and ok button") +@Composable +@Suppress("MaxLineLength") +internal fun DialogWithTitleIconAndOkButtonPreview() { + ElementThemedPreview(showBackground = false) { + DialogPreview { + SimpleAlertDialogContent( + icon = { + Icon(imageVector = Icons.Default.Notifications, contentDescription = null) + }, + title = "Dialog Title", + content = "A dialog is a type of modal window that appears in front of app content to provide critical information, or prompt for a decision to be made. Learn more", + cancelText = "OK", + onCancelClicked = {}, + ) + } + } +} + +@Preview(group = PreviewGroup.Dialogs, name = "Dialog with title and ok button") +@Composable +@Suppress("MaxLineLength") +internal fun DialogWithTitleAndOkButtonPreview() { + ElementThemedPreview(showBackground = false) { + DialogPreview { + SimpleAlertDialogContent( + title = "Dialog Title", + content = "A dialog is a type of modal window that appears in front of app content to provide critical information, or prompt for a decision to be made. Learn more", + cancelText = "OK", + onCancelClicked = {}, + ) + } + } +} + +@Preview(group = PreviewGroup.Dialogs, name = "Dialog with only message and ok button") +@Composable +@Suppress("MaxLineLength") +internal fun DialogWithOnlyMessageAndOkButtonPreview() { + ElementThemedPreview(showBackground = false) { + DialogPreview { + SimpleAlertDialogContent( + content = "A dialog is a type of modal window that appears in front of app content to provide critical information, or prompt for a decision to be made. Learn more", + cancelText = "OK", + onCancelClicked = {}, + ) + } + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt index 59307bfb6b..aa1fb6abd9 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt @@ -103,7 +103,7 @@ private fun ButtonInternal( ButtonSize.Large -> 48.dp } - val paddingValues = when (size) { + val contentPadding = when (size) { ButtonSize.Medium -> { when (style) { ButtonStyle.Text -> PaddingValues(horizontal = 12.dp, vertical = 10.dp) @@ -151,6 +151,11 @@ private fun ButtonInternal( ButtonSize.Large -> ElementTheme.typography.fontBodyLgMedium } + val internalPadding = when { + style == ButtonStyle.Text -> if (leadingIcon != null) PaddingValues(start = 8.dp) else PaddingValues(0.dp) + else -> PaddingValues(horizontal = 8.dp) + } + androidx.compose.material3.Button( onClick = { if (!showProgress) { @@ -163,7 +168,7 @@ private fun ButtonInternal( colors = colors, elevation = null, border = border, - contentPadding = paddingValues, + contentPadding = contentPadding, interactionSource = remember { MutableInteractionSource() }, ) { when { @@ -191,7 +196,7 @@ private fun ButtonInternal( style = textStyle, maxLines = 1, overflow = TextOverflow.Ellipsis, - modifier = Modifier.padding(horizontal = 8.dp), + modifier = Modifier.padding(internalPadding), ) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt index 685fe40302..45b5eb39be 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/DatePickerPreview.kt @@ -23,7 +23,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.rememberDatePickerState import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview -import io.element.android.libraries.designsystem.components.dialogs.AlertDialogContent +import io.element.android.libraries.designsystem.theme.components.AlertDialogContent import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.PreviewGroup diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/TimePickerPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/TimePickerPreview.kt index 79f0fffbee..7aae42ed0e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/TimePickerPreview.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/TimePickerPreview.kt @@ -24,7 +24,7 @@ import androidx.compose.material3.TimePickerLayoutType import androidx.compose.material3.rememberTimePickerState import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview -import io.element.android.libraries.designsystem.components.dialogs.AlertDialogContent +import io.element.android.libraries.designsystem.theme.components.AlertDialogContent import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.ElementThemedPreview diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ElementTheme.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ElementTheme.kt index f273c2dd64..62c87b39bb 100644 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ElementTheme.kt +++ b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/ElementTheme.kt @@ -68,6 +68,14 @@ object ElementTheme { */ val typography: TypographyTokens = TypographyTokens + /** + * Material 3 [Typography] tokens. In Figma, these have the `M3 Typography/` prefix. + */ + val materialTypography: Typography + @Composable + @ReadOnlyComposable + get()= MaterialTheme.typography + /** * Returns whether the theme version used is the light or the dark one. */ diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_0,NEXUS_5,1.0,en].png index ef042a9cbc..d4098db0e0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:46791d70473aca73286b0e4474bd0b649c918005516e5bc9b6a3bbdb9a8f5fd2 -size 26694 +oid sha256:862bbfc81d7c4ccc16e6d8d1db4f81306c0219b45b3630cd2f73dcabf0550b3b +size 26007 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 6ced266412..9c873827d1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bcbe69c4bdba925ca2e5926dfb916d9f83e5c17717b8fc6d3665f87ceb32d23d -size 28738 +oid sha256:32f331dd755437c67fd5251e3034b63d1e099f1032043b86b6f6f989680e390f +size 28184 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 458c56746a..f92d333a61 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:67a4bb7f73aeb7e8f014cf33147c08b6b95fc2f15c8b843c9d83c9bf24fa150a -size 22026 +oid sha256:250a6ae8cb93c781d570c40054f34b21832434f9a692adf637cf9e2560c6a09e +size 21538 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_0,NEXUS_5,1.0,en].png index dc5da9d55a..735efd3e6c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4417dad9a43b1759acb558213bb87538535a7e9a2b87c7292b4531d77551d7c8 -size 27993 +oid sha256:087d4e2355b7b64f930c8e4c149f565b77304ba0f62fc16ce0ff99696dc51b5e +size 27966 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_1,NEXUS_5,1.0,en].png index 34177a4f02..0c885e9c4d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:92376ec0846541af886963486f25a1b6de3db6d318ca5088098b322d24255570 -size 30079 +oid sha256:702d61eabe690c6b7a259d3773ed7e25011b0a8a99c7a1109930c2ea78b234a2 +size 30106 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_2,NEXUS_5,1.0,en].png index eb1a23dd1a..05d952ea66 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.appnav.root_null_DefaultGroup_RootLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c46a163c5b47489334a8a9475fc2a39e4682081023e21a326f2cbc2e88f035e0 -size 22852 +oid sha256:85b0c428cf44532739245c8303cc2c86ab5e0c6ffc060871f1eef8e1bce9955b +size 23123 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 1389cf753d..bdd263e811 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae8be2032172fbda58c917e00401f59a4c6363dee0bb8304f6a0bba18abbb67d -size 14135 +oid sha256:7e62ff59a909d9fc8c255236756a6f7f9d7efbb85e222de92843251367e2f774 +size 14150 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 064c330088..7961ed97ba 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:427314c0d0749e03b8639b6b0e15f294a8090b392f297e6a5a1714b77d3b174c -size 28520 +oid sha256:a3b008e2364af89731ba0431b7a97998452aa4d0ab7b25cb3d5130737b017a9e +size 28525 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 7ef3674a20..4cfb07c3ec 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af55dec154a30df77baa8f2e8ebfe2787cc99ac0d717274e319ea89c3b46d6eb -size 15206 +oid sha256:92a09f9c5915b444e92853809d6d42b0b2b76152f9fa58a1ac9856b15ee15373 +size 15222 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 9affad216c..5ada26de58 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c69b2b87544bccb46590aafcdd638e8fa4c81c6c3ca53052cede42e1926301f3 -size 29275 +oid sha256:d5ac056ba00dd474b08e1a1de0886c5e6989c12ea97a106e379fff9394aa5b90 +size 29263 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 51c26f2a7b..c1299a57ea 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e5bdcb1947e4f2c40d48ce69d2d7218f3aed7a96706a36f62eb6ee8cf18123f1 -size 58121 +oid sha256:3f0c16ef939ce2625d8e6788c0ccba95a4bc85d96acf282bed01cd67db65a4de +size 58090 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index a703dff4dd..c8928c442f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ee9b90232d573c09c824cbf63673cfca3e0a6823cd86914b4ea798e13b3d860 -size 83688 +oid sha256:ab118e4c47a83d68eda51b8b077a3eb55f0b5ad829b670be56ddddb99545c2a3 +size 83648 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index cef9210e1c..49fc604d5f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5411b9f81d37d7834eb7c50c59cb2bc1686f5f706fe96952147515ef1c041a62 -size 61494 +oid sha256:d016b99c433497a620543a4cdb1eae7207ca94bcd434b712b81d4ff72a886296 +size 61485 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index fefba0ee23..1fd2f7aa17 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1395d6bfe4258c13cbc76711505bc58666064777f0219d8d89203ebb313c3bb0 -size 86882 +oid sha256:3cf250f67844c383047ca24b496f8539360467f8fb16c0623fce9c9fff942c1e +size 86880 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 4d54d1492d..deccc2399a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb391a409ce4174127ad8ad4961975092f92a24224381a0c8c5e9940eb555235 -size 27629 +oid sha256:1013f54744e6f5e31bbba2bf0567e2a9cfe080e6821f81153f31c8588c88a29e +size 27219 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index ae86a46363..2c6bddd66a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:97ffdf54dd223794d0ccc013b773b381d8e047f6854a927bf5b20b67b4c75015 -size 28863 +oid sha256:322b47618d1ac52742bd163d1e588ebb222e2d709ee92a6e0d7746efef975293 +size 29016 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index a16df32332..c4a75a4b80 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fa8aed2bf9fd7b52feeecee0b6a754f5d4d6ac7874812582af6f62d57e8386d -size 50351 +oid sha256:ef34ff577d23711414e082e08ad82548c307541ae384f695317232d66cd2e493 +size 50369 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index d5b81b4310..a1bee8e1d4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55f3f0dad422de2490c3685530684ef4b94e03600a9a6f477a7841f64caacd50 -size 51093 +oid sha256:9afbaa0b1fd0ae0e98db92688bba574e4279af51769bbe20e8c9bd2fe3cd6714 +size 51087 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 71cf000cdc..7089e6c668 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a01b90afcca784a2ebd08e7645cc6caf296c03c37ca6e2fa295ce8ca8b907d8b -size 41481 +oid sha256:9fc1fd77ddc0abc5b26343e552895156797b6a3afa62fdebb277d04fc75273ed +size 40830 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 71cf000cdc..7089e6c668 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a01b90afcca784a2ebd08e7645cc6caf296c03c37ca6e2fa295ce8ca8b907d8b -size 41481 +oid sha256:9fc1fd77ddc0abc5b26343e552895156797b6a3afa62fdebb277d04fc75273ed +size 40830 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index b6f3e9b164..28647b48d9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b877305884353e5cf2b95cdbaad90a60bec8508b98ca702ea63272dde221667c -size 51997 +oid sha256:57da53bc60d35949fc3c762e0c2e57a73552a065107543819791610ea3aea327 +size 52509 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index 0adcc297e5..e44fc33e53 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b8a1fbf13bdc64c63ddffab55137c7c313630e11bd6c20da07593d8cc5460cc -size 52593 +oid sha256:94b57a5760b3afb973af466a8eec26c40e8e70b5790061fbb343320e359e12d8 +size 53103 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index 8cb8c5a578..024b035f6d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0d554cf60b5c6c6a58e1793c1155605c4c17606a0dfc92c8f4e9d39bab5bc56 -size 43014 +oid sha256:c2e9ffe60a426543aacb68bdb73d7019d9271673762edd3e4011b0ac445fadc1 +size 43067 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index 8cb8c5a578..024b035f6d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.invitelist.impl_null_DefaultGroup_InviteListViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0d554cf60b5c6c6a58e1793c1155605c4c17606a0dfc92c8f4e9d39bab5bc56 -size 43014 +oid sha256:c2e9ffe60a426543aacb68bdb73d7019d9271673762edd3e4011b0ac445fadc1 +size 43067 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index cb0e85f506..feca962b5c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:129144d77e162fb3263884191e240a3cb3b259501f69d0b346a73bd5273b9f66 -size 21290 +oid sha256:8b4d93b1444cb814fe0d8471679dc492f10b78c2ba101884491bef16a567aba6 +size 20704 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index b41e16aeb7..ca5aaa3076 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20191d257eb9db133382d8ca3ed15980bf4950a3583947a4145e1393509a70ac -size 30395 +oid sha256:35e9b3412e3a64ec302899f44185d8a82680506be71bc4a8aa7b4705a1f9f6fb +size 29736 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index ea8435fda4..1e9705d92b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d9790e38cc340cbaf2db8fc72578f7f3ad4760b132acb3bee1e82c9cb41ccefd -size 32710 +oid sha256:a68d387ac86cadb030b44153ebb07864b59194d87465012fe5c45e5c5b6dd8cc +size 32227 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 4fcb063611..3337e0b854 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c57da4d726af9971ec746d01fb2791ce0ffbb7e8a643ea49fcdccf2c60c60579 -size 13350 +oid sha256:ba63eb801cfe25cc2ec7dab0b1277757b1f608eff5a596157a8c2c457e2bbdd0 +size 12568 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 930ac7b554..0f4c367ecf 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1287b345aec7c250f9f47722d3dc3ab6979f0311ee35916d013545f1675aad1d -size 20912 +oid sha256:aec6ed6095cbdf2eabc46db41e4a6d5513a7a1a8f4610a0a68dfdbc006fea354 +size 21016 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 014432348c..22146dd734 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3401af59f4fcdc85914f00080029114d56dd2d5808ee5ef0cd2f36e9e3977b6 -size 30649 +oid sha256:6dd7b26eca3d0c40bd38d02833c0c43c348608574997a5e35d91c61c19928b76 +size 30473 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index d76ee1dcce..f3933c1a22 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd82812f25b2a11e280cab9606b88f48dc88ec45a2067742582ba82d9426d738 -size 33345 +oid sha256:fa450e9ad1db65fee62ad2fad4de118e1b1f335fce7b2e685d25063006521fb3 +size 33305 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index ca129e97f3..ff0b013334 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.leaveroom.api_null_DefaultGroup_LeaveRoomViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6445a52dfcf1c34488fc60a30eec00e467a300dc5d4766498456a7a3b8ff93ea -size 12576 +oid sha256:b25270512a267079055363bdb6d54cb5444bbfcad4e60f2ca0e0466452b28ae4 +size 12238 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png index 4cd0b5b3f2..5032df6d49 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64a4a845c409c02afb7944a0e60566ed3f8ab5a2a6c9ad2dbf4f068df9ed1a7d -size 37965 +oid sha256:d430ee7f8f505a7fb42bb4f1ac795fa339ff9048837ee9f4c4166c4df78004fb +size 37584 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png index 7f826958be..8c58e965e3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-D-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d436ecedf88500e967d39adf4ee1878c956efc068c0ac5dd8b4a327cd208821e -size 36509 +oid sha256:b7ffd042ba4e4a6b10f6c566765bf3fe4c992513f09169ca89a263983f162d79 +size 35994 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png index 9ce6529df8..7259132ad6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ba54e1084b5eeec31a86c8ed77e0e60dee7696e910eee3d03a794b6771b5c6be -size 35443 +oid sha256:3cb01d6f6f798c3cf2528db2b6d974b1007a7520ade43370082a1479365c41a7 +size 34934 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png index f5d9f7d328..c9f3716e82 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.send_null_DefaultGroup_SendLocationViewPreview-N-0_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22c9cb863c50b76ee4d95fe7def73c9cbf15d3754fc708fb8e85078e31fdd722 -size 33936 +oid sha256:8862e4c3a185ffe807c1357c6a24aac4a9d6eac6405a73aa24b4772de67170dd +size 33419 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index b8278d25aa..d3dc127f68 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:890331a5e2d87f5a326815a2729edb3fc709a509d17dfd3e33ca5d5f9dd98a3e -size 148093 +oid sha256:d40dd7970069fb798b738a1775b5843522cc28a581d904f44974d7ceefdbf3c9 +size 148087 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index e94b044001..8227daecf3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae596d57381c529975d11e32c3ff3e64ec8de4eba7c52aa69fcf04e2c6854b6f -size 148784 +oid sha256:47f20f3dc4e4bdbb6641f26b6e03d0dba0f2c7e0fe22fce927870d14f1472066 +size 148796 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 3c80137baf..b729e6d346 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e75dc8c50d9173a803c4dc8cee3b35f9bf3905bc6a19dd9479fe2d51c7f266c -size 64148 +oid sha256:71b2a1c6dba0b373e2abfe77e8327ac2e95c7286eeb53f83e5d802d10bbff96f +size 64975 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index b8278d25aa..d3dc127f68 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:890331a5e2d87f5a326815a2729edb3fc709a509d17dfd3e33ca5d5f9dd98a3e -size 148093 +oid sha256:d40dd7970069fb798b738a1775b5843522cc28a581d904f44974d7ceefdbf3c9 +size 148087 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index b8278d25aa..d3dc127f68 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:890331a5e2d87f5a326815a2729edb3fc709a509d17dfd3e33ca5d5f9dd98a3e -size 148093 +oid sha256:d40dd7970069fb798b738a1775b5843522cc28a581d904f44974d7ceefdbf3c9 +size 148087 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index e94b044001..8227daecf3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae596d57381c529975d11e32c3ff3e64ec8de4eba7c52aa69fcf04e2c6854b6f -size 148784 +oid sha256:47f20f3dc4e4bdbb6641f26b6e03d0dba0f2c7e0fe22fce927870d14f1472066 +size 148796 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index fd02412ab9..bf66c83a75 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cdf49456caeab58f7761581ed37ba283f63ca16cccfb173b127e9218f28bb87c -size 65084 +oid sha256:1016b14f6d975828470aa8d6786177d6dde909cc727f39b2c66742a101735e8f +size 66240 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index b8278d25aa..d3dc127f68 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:890331a5e2d87f5a326815a2729edb3fc709a509d17dfd3e33ca5d5f9dd98a3e -size 148093 +oid sha256:d40dd7970069fb798b738a1775b5843522cc28a581d904f44974d7ceefdbf3c9 +size 148087 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 07a0f8f685..f4eb37f23a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31d1194224f676b4700f963ec4d4c6c013e6f6eca0d6c1852d4d7e3f7c099eef -size 396102 +oid sha256:285570b82885f1ae522c8ae79f6802028b53e76ebc08da4ee29bccc648001935 +size 396039 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index e7a7c8b9fd..260f750e5c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b9cbd731a497489af3daf7bf74ef44b0955a060fd322f26d9a4711fa464e549e -size 16451 +oid sha256:e8ea95bde8ba6ed5a069b2a6de2231267b6ea9a4e5d5c4a6efac166e0c563cff +size 16388 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 65b36c5ea6..62b6486890 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b38a09834ee743825ce2d47bd62819c87b59777595029261b4ee6bd341d1644 -size 185141 +oid sha256:3ce8ff927a9c9e414e2a37da8296b5a880a043d2e162f7169d58161c209adcae +size 185108 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 4940569821..8392cf201a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e0d53c92cd4f5bd41449324e10ae978b431358b3e1a467684c9f09c69851c82 -size 101210 +oid sha256:3da137c4945b32fbebc70f4adb3bb2944d2f8d63aa0d30a1b10c52a9e8c233f5 +size 104677 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 363747c718..80f87a5187 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c33b0191730e4d499f6e89b073cf8da6b60ffae77ea821a0cb514aca1684ca3 -size 13691 +oid sha256:c479bc8fd2ba3cf33cc7a531fe31cea97df4939101f3b1f4cc200a29fa2fd2d4 +size 13736 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 01187f262b..9a1d495247 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6fd448acd5377cb28dfeb61ca58b1320b0bfed16475eb0e6ffc1867206e6f50a -size 13242 +oid sha256:1de437e6f10abe8d703ee0596e625aa63c8f93d60c36fa7f30de9fbc53dcc60d +size 13292 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 7721bc70f4..0c4058c10e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5300c5ca063458064bd305fd00062d307c09caed86ed95640cc4104310480b53 -size 26877 +oid sha256:a0c631199a337c8e057c2ec1c460bfb3fe2d0e078fc70d13fee96e057edc33b3 +size 26909 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 0651b1469b..c065adbebf 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5d48d17f097b49c70fed6ad640d3310d3eb6608ed97830345c1e561ee8e5d16 -size 26480 +oid sha256:d170e350cce0f19d5635e59124a476cef4b4e58e723c13518f39249ca2144428 +size 26513 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index b707751d97..62789f5fd4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bfbeb57a1b19d25d37a3fb6e50221528d368e99ce48124cb1944a3973aa6f6c2 -size 26481 +oid sha256:9077659f30e368e2e5f6cb78ccd1b04c03df01c14d2e94e3c4dace8554b0d2a4 +size 26502 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index b707751d97..62789f5fd4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bfbeb57a1b19d25d37a3fb6e50221528d368e99ce48124cb1944a3973aa6f6c2 -size 26481 +oid sha256:9077659f30e368e2e5f6cb78ccd1b04c03df01c14d2e94e3c4dace8554b0d2a4 +size 26502 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png index b707751d97..62789f5fd4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bfbeb57a1b19d25d37a3fb6e50221528d368e99ce48124cb1944a3973aa6f6c2 -size 26481 +oid sha256:9077659f30e368e2e5f6cb78ccd1b04c03df01c14d2e94e3c4dace8554b0d2a4 +size 26502 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 239fd9659d..806a6406be 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:65937f0bf26349bc168ec709c9a560f48b3ddb336e5ccc39cf80eb3bfef71c30 -size 14747 +oid sha256:7171960e43482db855e1f997d19604ca84958dd57caf35c39d8d34bd4dd803a9 +size 14791 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 84b632c77d..6ed27f37fb 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89cca739d912bfb3b78e589c323f11e8c12c8df506dc7a650e3fb452ec1b7973 -size 14136 +oid sha256:860b7040447ebba7fcefbdf7ed33db2de3d756ca9f67000b845d547d443cd23e +size 14185 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 21a8770743..564db946e2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a0ea82c7d990a967689b9c362ed1bc4af29651c7e7431fa5c4d831ed07a90776 -size 28635 +oid sha256:f59f7e98b77c9e4c82e1a10c24a439733bab9ba26472aa7ca13da8bb4f4052bf +size 28694 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index 13083c21f5..edbd888d35 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec1dd51fd31366ac1baa9e249fec51229b497543db1b0487222020e2295cd89d -size 28032 +oid sha256:69cd17722c03402809b2a85ae18c02e3e7f934dd6526f18a40c64654a51d8d99 +size 28092 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index 92f7208067..91573e4536 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:072e97b246b48d090a3ab9f42528cfd714dde2f0ca928dbdfd315cadfc110dc8 -size 28093 +oid sha256:9d322e159a4c5e39a1bdce0628093bdc9ae372ccfd9d1fa2088a3e3094a09048 +size 28096 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index 92f7208067..91573e4536 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:072e97b246b48d090a3ab9f42528cfd714dde2f0ca928dbdfd315cadfc110dc8 -size 28093 +oid sha256:9d322e159a4c5e39a1bdce0628093bdc9ae372ccfd9d1fa2088a3e3094a09048 +size 28096 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_7,NEXUS_5,1.0,en].png index 92f7208067..91573e4536 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.forward_null_DefaultGroup_ForwardMessagesViewLightPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:072e97b246b48d090a3ab9f42528cfd714dde2f0ca928dbdfd315cadfc110dc8 -size 28093 +oid sha256:9d322e159a4c5e39a1bdce0628093bdc9ae372ccfd9d1fa2088a3e3094a09048 +size 28096 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index ca7a4913ba..381ab955b3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb5749e3df13b1f7c178c7754e6c0726ee95b5b9b76563fffa6a37d317baf121 -size 35401 +oid sha256:430ad21d6faa861bda2a774b8e58891beced9a7a442a4518455f06c2cf6ea11f +size 35061 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index a9608936b3..626414fc5b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.report_null_DefaultGroup_ReportMessageViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b07653056ee30fb780009ce9b333c7cf3ce8eefae46374d3c9fb940557c87e48 -size 36774 +oid sha256:1e48ec2f2e8a992be7dd3c12ae3780d3bb110ee66758f72586d4f3aee39d18e8 +size 36935 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 04875a5d63..cffa085a04 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8b51e48deaf036b33200c41adbc732ec90208812f7aca533645bd2bf781637e9 -size 51599 +oid sha256:9272791f4ed5afde353ea893988fd1140bb6b090f19901c0bbfb25ba37e07e52 +size 51100 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index fe2be30344..d33576a149 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:02bf9db04c6b88bb28ca4941d25abfa1dc563c494693c8f9db93e658633b8f46 -size 53496 +oid sha256:4d6f69846650213dc6b218c97e2eb8319b5fccaa9451822b23c6c2cf9d46b339 +size 53653 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewDarkPreview_0_null,NEXUS_5,1.0,en].png index 679e2d22b9..1bc1883b8c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:472f27ff67cf4b822b272ad78e5933026e25b8da02830a75a9898a1c2b59459c -size 25349 +oid sha256:27410489a8b575564662f64fe4149ebe08a9b9b3136f9a3047acca100616830e +size 24618 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewLightPreview_0_null,NEXUS_5,1.0,en].png index 9274fb7079..7419093421 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.crash_null_DefaultGroup_CrashDetectionViewLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3cc91235bda4aa191101eb0485cd90abdc94060f31685a01b8d134043c2edbfb -size 26374 +oid sha256:75507bd9d98b762bb67b732f47f6409476e4886239b2450df4d2e54309c52cf2 +size 26303 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentDarkPreview_0_null,NEXUS_5,1.0,en].png index 6a40cded49..3524f4eaa1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:24c0af07f894fc4220366c11dbd5b3d02e204cd3c9d6788d26d7dc29451e3791 -size 27297 +oid sha256:e5ecc29cd0aaa41be1652d8b035951d287adf1e28bb9c825c99d79a5f8fbac34 +size 26661 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentLightPreview_0_null,NEXUS_5,1.0,en].png index 06b65974f0..893e73d603 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.api.detection_null_DefaultGroup_RageshakeDialogContentLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:268bbe371de1fb7b72376d3844e394bf4bd00cf1112b8d96d9404cf409543f32 -size 28488 +oid sha256:c47014cbad6ac7446bc4dc1c21560f8a0b107fc8da25b8c85b89fb2591733efa +size 28466 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index c1c10133e1..6e81844720 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e2185d93e6f642c095cf63c9c42cdcf8dc035cbcb81ca0e9f70217e7b688596 -size 29671 +oid sha256:99181f0a4a721479e0c2e2eaae91f4637a7a01054159c5b2b3c9173ebdf6a1dd +size 29649 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 9635b285ba..aa92897b18 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8543706025ae8c983e95b20f62d8433dbcc03ba66e4aecb3f8c4dbfbd8c7405d -size 23390 +oid sha256:ac2fff68f6580426fd3273f084ec375123fabaab4a706e7cd153332d2947173a +size 23369 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 82a1740d64..883d55bf0b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6fb7373b0e6d3396eb134b8e589f64efedfea9066be7daabaf6b29d56bdc8343 -size 54130 +oid sha256:9e490123827e4275fd69bbc21990d8f10575da224432fb9aa3c93bcc51078bb0 +size 54114 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 44f9d7a47a..bfd46c52f3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3cdca75d40ed26c3cf33bf2df54a53e93f72a72331ff1dd9bb289769e0ee4138 -size 28618 +oid sha256:e8eec289c3e262da582a77de0229d818f061ae40818556461d0a31ac3065dc2f +size 28650 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index e66e45dcb2..a7210f34cc 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d711ae9a79869cdd5b5fd6dbfdc23a3d77761368257de84b9c7181185e6bf40 -size 28186 +oid sha256:fef45ba584f1edcc9ea0aa27c3e13678bbe3c834ff88a71a91ca19545b94b280 +size 28227 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 33bd173990..e01891c06d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c19435d814b5a06e6e93e4549f76568235c0c018e10a7fdeb05d947620f67b7 -size 28931 +oid sha256:d3c1fb883194c1137e4696b1fcf02b87aab42814f1e13f07383585159de615a7 +size 28903 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png index 7715deafc5..54dd5050d6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f835ef43c56477c85b65eb5cb0ce6a601148a8adac2491e6597a74099e5cb06d -size 25356 +oid sha256:ab9225695d5c1c7a42da36cd78378713e7056808de3dea01e2cef92e9972c64e +size 24875 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index cf00e4c5e7..a5c3fe25c9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d4963634d3c2b6da6e05bf4cd00b255f0bd204bf572320cde153b7f079a10613 -size 30709 +oid sha256:5dda2d8f3a0fce6d47e25cbbd862a740d0f06c7b6c9ae9590210cf07ab840d5d +size 30769 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 075d730052..20c39d6876 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aff84e6d8ed41ac483896a7551f55a2943a1ceb5945abdc19818de2838da74b4 -size 24178 +oid sha256:57048d99d5de2857fed428c8f9d99d6a59c9856c78a1c6d21967d12297b1bd16 +size 24231 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 170443ddad..bfb9ec187a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c305179405a43cbd48b76a3531fd9437526bbf833a0be150744d8925faf738fe -size 55605 +oid sha256:04af2a4120f7bda8b883d68327f0a2f5d0ac4072a86d120354f2c3bfbde2ff27 +size 55652 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index 4bf960778d..0e6eaae2f4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7aa9b2ce8f4f0afebaff4283f83bd15c016453421796b3057a041e957e447d2 -size 30612 +oid sha256:f81e2754d4152b993f3572bfe0505ea5f88019e4133bc379c9e0eff28fcfac74 +size 30596 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index 47b62b7c8c..f4667da358 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e803a1d9dbb1445edf48e8373b4ff87ae2c2c341d049eff96310cc336eef8d57 -size 29340 +oid sha256:2aa961419706ae0409f7903f54634bea33342a35884f6ddf970279c4e0fdd512 +size 29325 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index 45d21256f6..400b59412c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c999ef4fd12d2703933f221e04c40c95982477d1b9731735f0419496b05aa85 -size 29381 +oid sha256:5841e8de48a2ccac2f3cec3d228b2136fc8c876687e971869cdb4c8aa83b524b +size 29413 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_6,NEXUS_5,1.0,en].png index 48f3e48564..4bf226735e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00c3ef64f46f20dc460fe096c7aaff42a5335f3c0f31caf795cd489b517619ca -size 26330 +oid sha256:090bfe363dbe3ab64533549bd37db81b54962af57062e2ea09c45d5e5efec362 +size 26496 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 983013c965..5659fbd79d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be77c7e5d2380e02916a095fb8e848e6ed48b89651022c89e781a6bfb7cbd3d7 -size 14413 +oid sha256:3c11260ddfc0a62f6d530096ab38f9d372d68383ba62f5edd081df776c300abc +size 14454 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_1,NEXUS_5,1.0,en].png index fe7e5e228f..1e9422aff0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d2b60d0486efd7fd5f23f5d91aa20c10c080aacdfb7b224bcd89c7d5265d4ca -size 28593 +oid sha256:79150e1801fc222685c31f2997476b68b41f3b0d99f16abf027aae373144d39a +size 28585 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 0d51af3915..dae86440d4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:698c99b9853d7b9859488f0a5e8d267f078b182da5a86a9fd8d26fe047c8f413 -size 11910 +oid sha256:db0f0208e41db2043aca84b138bdb114377a56764f8b548e35410d45c8d9fa2d +size 11950 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 46da1c22cd..05c06cc679 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9ce615462c5f6f608c5ccd9fd2bf625d73fc512d7684db6358eff46228f9892 -size 26593 +oid sha256:9473eed03afdfca91ba2f869767bdfa9cb426a2309517d9ed475cc765da005cc +size 26623 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 0c6a30f778..2ae3ae37f7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:153421bc23ccdab25a935d23599e37fa7614da878f83e93762c69695e35a2244 -size 13942 +oid sha256:f7d7e82e1ca64dc7e18a6499e83d1deb1a1d7f55da70d0690f7c5f2bf2ae3675 +size 13976 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 05266e3a11..3959b492c7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:27202c3c88c6eb67ee082592ecc564cd79fad5bbb669cb9f9a92a8b20880f144 -size 45532 +oid sha256:d6a9233053173c4e85792c665df900ce4823508820e504cb76c2185f193a122e +size 45527 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png index 38994ec87e..171d9ed71d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c6fb457bab92987c715938b89a2dda81ae4f58599f84526e102c786370c37c74 -size 38647 +oid sha256:64970dcd4086509e796cd67fe862053d4c5b1eb14234647ccba6abbb978b2aed +size 38643 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_0,NEXUS_5,1.0,en].png index 6ca9645a30..d6d69dfc15 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:75fda6e4e92b021ad0fa927bb6736841de01ecf8c1b7ff1a355bf8bec01dd2dc -size 15340 +oid sha256:72f6737a9e2612f6b3ca309150d7cfdb57e0a28793a4d9bbbc2b3ae280fd8205 +size 15397 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_1,NEXUS_5,1.0,en].png index a4e250b6d6..301449fde7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f6f6e4f7785d35847a2bc38db7090d882435365cfdc3f7a14d8f457d1acf03c2 -size 29429 +oid sha256:0cbb9fa12c3856df22741ecc4bcf9bba25fe113b8ca9f4bb0a7bf3e21232a0d4 +size 29491 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_2,NEXUS_5,1.0,en].png index 225513dea3..7efb05c9f8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:947a7f8e7faa0e40e47c1939cf255a9063e91c4e4f5787099d46ddc3c8abff68 -size 12657 +oid sha256:7efaf993aaf2c43ed4ce4bbace39c6e19d4551bac1488f0efad1e9251090c457 +size 12715 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_3,NEXUS_5,1.0,en].png index fd375990d4..ff73b1eef3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15b8a86cf3fbc4e851a42557b9da9a84ddf34c5964d5dbedc14a77d7c90ef482 -size 27439 +oid sha256:cc38ae9ae3ce7d632c6599d600e6095be2453804837d556653c8ef3fc5d407e3 +size 27470 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_4,NEXUS_5,1.0,en].png index 54756514d1..4a64d0aefa 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35ae824d0ed03be1f0ec4068c5972d56b80637baf497ad4700a24f9a62a7173c -size 14737 +oid sha256:492e4ba89d4033242f4b22da255a241f86330ee3eeb244ef5102c9d4319e80b6 +size 14793 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png index 2ac4ea2741..676e76b3d5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ed1acfdc4a74864d8c54577e3038017f3ecb1791144d6a8c62e825d458db16c -size 46995 +oid sha256:7911d3f2ce4cf00f97de32e7ef3775abc354352555428dfbdb7761fa2a4d79fb +size 47055 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png index 7740716b68..4771d083c4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e53678bab7f61340db9ddb6266857d664027cc83e803cf993b1b64fe0cfde44 -size 40777 +oid sha256:cf6c53f13773361c289f05141935c591a96d0f91dec16504a3ba74cac7436862 +size 40831 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 0f1091ee6a..508f583f10 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:791760246c638a89757995a3e5772150a9071f55bef1f35ba0f5ef8705d2b173 -size 13107 +oid sha256:d9bc5229a0ea1f347f5030b2bb41e656a999c001958e44ec1aaa1b70bd612017 +size 13104 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_2,NEXUS_5,1.0,en].png index 526629ae68..f5dba656cf 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:010fb88e2a8c3d8ebd5ed2180b2dcf8cc207e9ade5a49414be9f6dbd70cd2e16 -size 14008 +oid sha256:93a44977870bc32a5f4c742b508ddb4c884e1a65701c085eece2e576c61f26a6 +size 14053 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png index d89f7c3143..26fd28c090 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07bc5d9f73124dc246c8eb96ba4e2cd7657b5ed397d668a43cc7cf1872a03762 -size 7660 +oid sha256:483bc7356a610804ec07c2794108850dafe04b4c86b653b0199fe0f5b8b85567 +size 7743 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png index e678873b95..8923f6297f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.atomic.molecules_null_DefaultGroup_ButtonRowMoleculeLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78d81c9074ee16f257a301a067a902961c8cf5502d1b41b22a1fe952eef1566c -size 7771 +oid sha256:5fd715882bbac80af31176491c97c2af711a4454a88832a086a55e5ece04e255 +size 7821 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null,NEXUS_5,1.0,en].png index 70041ad838..1fd95a27de 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ConfirmationDialogPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b81b47c80113bee5cd295e4425736f1abf6236350a582d0cfee304b056644cb -size 22850 +oid sha256:2c66c42d9c687bb41743117b83c65e348f5fb921865070afcf3352b618f79937 +size 23759 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ErrorDialogPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ErrorDialogPreview_0_null,NEXUS_5,1.0,en].png index 99b294bc91..42ee6936cb 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ErrorDialogPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_ErrorDialogPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e847e0af0865d01abc77eb27d503581912d95b6d86d82cc310fcb085567b2834 -size 17830 +oid sha256:0e42b60613f27563e3d6921854fb9feccdd17ba7ce332a40e954b4b3bafb969f +size 17468 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_RetryDialogPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_RetryDialogPreview_0_null,NEXUS_5,1.0,en].png index d664efd253..b57806c97e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_RetryDialogPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.dialogs_null_Dialogs_RetryDialogPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:494a2d83511814b6d954f2e05b445e9e546f2a52ff31f10b330238ea15868299 -size 23546 +oid sha256:b939ee5229bd4c0f1d5533b492cbb24741a98e20cd8a848aca3ab02527cfc818 +size 23228 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_Dialogs_ProgressDialogPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_Dialogs_ProgressDialogPreview_0_null,NEXUS_5,1.0,en].png index 71196ad117..29f5373201 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_Dialogs_ProgressDialogPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_Dialogs_ProgressDialogPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15513c18852cbfe860e32f5cc74f21cc3c7d15a70eee50c7710e195cfec0c8ac -size 21567 +oid sha256:fcd06348851e32f3c08bc30e774006c493d9dc91402840af8edd057377a27acd +size 21618 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewDark_0_null,NEXUS_5,1.0,en].png index 22efc21bb1..bb199bf0cd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewDark_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewDark_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8c539176a004d4664f8f0eba24cdc00950b42284dcab249ef3bc87b0347df85d -size 32949 +oid sha256:0192cbadf07b54899608c57e094fd30d86a9856a4354b9e540a1ce05d4da393d +size 33460 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewLight_0_null,NEXUS_5,1.0,en].png index c1cdbcc5b5..f26aa74393 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewLight_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_DatePickerPreviewLight_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee41ce428593fc4d8f329e623314c2200a55cb99950e6896f8df17bef5f7f3a1 -size 34187 +oid sha256:6fe1c01d5b8b16c1c6d6a8ff427a6050ce9347676ce87523fc93d5a69ae244bf +size 34582 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerHorizontalPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerHorizontalPreview_0_null,NEXUS_5,1.0,en].png index 5fbf7f31c1..3fc3b275fe 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerHorizontalPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerHorizontalPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:899b8d8bcb319ccf50867ce0e6f1333eddbcb232427098ea15fcacd37bdd6f43 -size 35981 +oid sha256:5ce469fe16d6b06c0425082ae37bde3c1931a69e390dd85a6f05ac7619a0e001 +size 36525 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewDark_0_null,NEXUS_5,1.0,en].png index 51138d72e5..8e81c65ca3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewDark_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewDark_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ab443d66a938f968fab3ce8c26d7b567dab72ecc3c505ab1fa9f87c30822057c -size 25451 +oid sha256:ae68c7bef1afc22da25dd84ff9068119a1c8e77ea5a6688e930d8f633f9ca654 +size 25548 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewLight_0_null,NEXUS_5,1.0,en].png index e17d053854..f3f354976e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewLight_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components.previews_null_DateTimepickers_TimePickerVerticalPreviewLight_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c4db502fdc68ba5c1d391bc7b14b9a5c852154f0c1ee48788ebfddaa8bac21e -size 26441 +oid sha256:9107edd0eec824d18b2cc1d4a8c3263979a946fbc1f919772f7f6e65f7a91d0e +size 26601 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonLargePreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonLargePreview_0_null,NEXUS_5,1.0,en].png index 00615a4f8c..b64970ee0e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonLargePreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonLargePreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:14b7f637d843a604237650f1b72220e7a581871b6ca1cbb6be184c9d5d6127d6 -size 32874 +oid sha256:b57120ea3e2b2ca243a738000f89aa3bbc6d9bc87d76d597b594165a5d4c489d +size 32489 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonMediumPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonMediumPreview_0_null,NEXUS_5,1.0,en].png index bcec196c90..afa63e44bd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonMediumPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Buttons_TextButtonMediumPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15de13a449591d15573d36f894e208530cb60b5e80f7e1779279e5147fb7934e -size 30692 +oid sha256:2fb4d0d2ce2c59edf9f55c30e9221b192dd421cb83e56acb7c1ae7218ceff761 +size 30776 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithonlymessageandokbutton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithonlymessageandokbutton_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f9ba6a7ce2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithonlymessageandokbutton_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dc702968f26dcbc042d1c44de84cf200cd1b08e6c160bf454ea8ec5ee002b63a +size 50858 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithtitle,iconandokbutton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithtitle,iconandokbutton_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ab9564627c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithtitle,iconandokbutton_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9bd1e84d97db0588e6bea339acab2aec499c99730e93381f8a019d6201e129f7 +size 56998 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithtitleandokbutton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithtitleandokbutton_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6665e0a034 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Dialogs_Dialogwithtitleandokbutton_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18d9b58bdba48472cc32110e4d032b7b4a4cbc80cdf9e8ee44964fdbdde5e976 +size 55508 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 8859798647..22750d6377 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9806655e512a275706e948825ef7d2faa6ba8fc25aef833648ede20952304f6a -size 24755 +oid sha256:922897d98edecf37a08b818edf88ca5ab2a62b810d3b8d1d70fdff611ec30e4f +size 24272 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index ffe720d68e..1ba3aa364c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2adb397be7481de953a8059f81629c7f3b2bcbd5f010fb9988a7188a03cce95 -size 34095 +oid sha256:e1a418d5bad67aba70ffba07e6d7d44b19962ea8433f4aa050b58784874a83c4 +size 33567 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 94d65a2e4b..dfbfd18cfa 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b102ab3bacac5c2241c2f0a4df8e82d24bb9799584049ea743cbaba8958bd58c -size 28522 +oid sha256:0920fdd5ec77504fb5b94e3e77dd1afade7f917282b2fecab2fc30d986a34ca1 +size 27885 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 27345b0d8e..8f982d38ac 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:831f4350c9fa4749db0c7d86f20ab043d053efca870346102d359962eb5825e2 -size 25953 +oid sha256:9525fb2cb0576268b8af4eed06d882d623a8f78e8581f6c9a63bf74403c88909 +size 26018 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index adc547eb6b..06367fb9b9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f769f814da0a304e93ec614c305098b35b6d85f1abe40c54a82f5aff18cf603 -size 35893 +oid sha256:bca874faf9c1d1525744d8a4942df1d4f019a71aa4faf6ec9a8d4d368483a1ef +size 35855 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index f4b0fda846..d860328261 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.permissions.api_null_DefaultGroup_PermissionsViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:57e338a521660fc912e111e9b9358c7ca7c33ce4d05cf96dc24b202d33d4d332 -size 30017 +oid sha256:e1b3a38d1d502cf9fb54517f82afeaa7c47a0bb9968273a1e0a76ea60e181798 +size 29711 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewDarkPreview_0_null,NEXUS_5,1.0,en].png index b290efe8b7..a8f9374795 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e680c33d3185285e0ee1dc42a9b0d49576889f794255a1cdc769f53f2a5efa05 -size 20743 +oid sha256:3cd64fd3c8647a179c8cccb46878a3495bf2a41173c62d0a378c8ff003dfe7bc +size 20148 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewLightPreview_0_null,NEXUS_5,1.0,en].png index e388f0113f..a5b012cc36 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.services.apperror.impl_null_DefaultGroup_AppErrorViewLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1dc8170afe756b20b0d3fcae2e2cfe0dac0e9b843be5733579563b6deeb9f6b -size 21437 +oid sha256:f458452cceef79d94ab3875c67e1664c60f13b77eb3ede7c0cdc04538bed9298 +size 21730 From 53e80e6b5ddcdc3d8e94e506cf04419ace87a7e9 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Fri, 11 Aug 2023 15:09:51 +0200 Subject: [PATCH 241/251] [Compound] Implement DropdownMenu customisations. (#1050) * Compound: implement `DropdownMenu` customisations. * Update screenshots * Add changelog * Address review comments --------- Co-authored-by: ElementBot --- changelog.d/1050.misc | 1 + .../roomdetails/impl/RoomDetailsView.kt | 3 +- .../impl/components/RoomListTopBar.kt | 5 +- .../theme/components/DropdownMenu.kt | 12 ++- .../theme/components/DropdownMenuItem.kt | 81 +++++++++++++------ .../theme/components/previews/MenuPreview.kt | 4 +- ...MenuItemPreview_0_null,NEXUS_5,1.0,en].png | 4 +- 7 files changed, 71 insertions(+), 39 deletions(-) create mode 100644 changelog.d/1050.misc diff --git a/changelog.d/1050.misc b/changelog.d/1050.misc new file mode 100644 index 0000000000..5a32d00d15 --- /dev/null +++ b/changelog.d/1050.misc @@ -0,0 +1 @@ +Compound: implement `DropdownMenu` customisations. diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index 3ec597e525..51754ca6de 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -67,7 +67,6 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.LargeHeightPreview import io.element.android.libraries.designsystem.theme.components.DropdownMenu import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem -import io.element.android.libraries.designsystem.theme.components.DropdownMenuItemText import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Scaffold @@ -195,7 +194,7 @@ internal fun RoomDetailsTopBar( onDismissRequest = { showMenu = false }, ) { DropdownMenuItem( - text = { DropdownMenuItemText(stringResource(id = CommonStrings.action_edit)) }, + text = { Text(stringResource(id = CommonStrings.action_edit)) }, onClick = { // Explicitly close the menu before handling the action, as otherwise it stays open during the // transition and renders really badly. diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt index c144ef8671..001c048e4e 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt @@ -48,7 +48,6 @@ import io.element.android.libraries.designsystem.text.toSp import io.element.android.libraries.designsystem.theme.aliasScreenTitle import io.element.android.libraries.designsystem.theme.components.DropdownMenu import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem -import io.element.android.libraries.designsystem.theme.components.DropdownMenuItemText import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.MediumTopAppBar @@ -169,7 +168,7 @@ private fun DefaultRoomListTopBar( showMenu = false onMenuActionClicked(RoomListMenuAction.InviteFriends) }, - text = { DropdownMenuItemText(stringResource(id = CommonStrings.action_invite)) }, + text = { Text(stringResource(id = CommonStrings.action_invite)) }, leadingIcon = { Icon( Icons.Outlined.Share, @@ -183,7 +182,7 @@ private fun DefaultRoomListTopBar( showMenu = false onMenuActionClicked(RoomListMenuAction.ReportBug) }, - text = { DropdownMenuItemText(stringResource(id = CommonStrings.common_report_a_bug)) }, + text = { Text(stringResource(id = CommonStrings.common_report_a_bug)) }, leadingIcon = { Icon( Icons.Outlined.BugReport, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenu.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenu.kt index 175c0fb402..c0160bb7f8 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenu.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenu.kt @@ -26,7 +26,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.PopupProperties import io.element.android.libraries.theme.ElementTheme -private val minMenuWidth = 200.dp +// Figma designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=1032%3A44063&mode=design&t=rsNegTbEVLYAXL76-1 @Composable fun DropdownMenu( @@ -38,19 +38,17 @@ fun DropdownMenu( properties: PopupProperties = PopupProperties(focusable = true), content: @Composable ColumnScope.() -> Unit ) { - val bgColor = if (ElementTheme.isLightTheme) { - ElementTheme.materialColors.background - } else { - ElementTheme.colors.bgSubtlePrimary - } + // Note: the internal shape corner radius should be 8dp, but there is a 4p value hardcoded in the internal Surface component androidx.compose.material3.DropdownMenu( expanded = expanded, onDismissRequest = onDismissRequest, modifier = modifier - .background(color = bgColor) + .background(color = ElementTheme.colors.bgCanvasDefault) .widthIn(min = minMenuWidth), offset = offset, properties = properties, content = content ) } + +private val minMenuWidth = 200.dp diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenuItem.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenuItem.kt index b8c18f99c6..f8e0fc2b78 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenuItem.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/DropdownMenuItem.kt @@ -17,20 +17,26 @@ package io.element.android.libraries.designsystem.theme.components import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowRight import androidx.compose.material.icons.filled.BugReport -import androidx.compose.material.icons.filled.Share +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MenuDefaults -import androidx.compose.material3.MenuItemColors import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.theme.ElementTheme +// Figma designs: https://www.figma.com/file/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?type=design&node-id=1032%3A44063&mode=design&t=rsNegTbEVLYAXL76-1 + @Composable fun DropdownMenuItem( text: @Composable () -> Unit, @@ -39,34 +45,37 @@ fun DropdownMenuItem( leadingIcon: @Composable (() -> Unit)? = null, trailingIcon: @Composable (() -> Unit)? = null, enabled: Boolean = true, - colors: MenuItemColors = MenuDefaults.itemColors(), - contentPadding: PaddingValues = MenuDefaults.DropdownMenuItemContentPadding, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, ) { androidx.compose.material3.DropdownMenuItem( - text = text, + text = { + CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.bodyLarge) { + text() + } + }, onClick = onClick, modifier = modifier, leadingIcon = leadingIcon, trailingIcon = trailingIcon, enabled = enabled, - colors = colors, - contentPadding = contentPadding, + colors = DropDownMenuItemDefaults.colors(), + contentPadding = DropDownMenuItemDefaults.contentPadding, interactionSource = interactionSource ) } -@Composable -fun DropdownMenuItemText( - text: String, - modifier: Modifier = Modifier, -) { - Text( - text = text, - color = ElementTheme.materialColors.primary, - style = ElementTheme.typography.fontBodyLgRegular, - modifier = modifier, +internal object DropDownMenuItemDefaults { + @Composable + fun colors() = MenuDefaults.itemColors( + textColor = ElementTheme.colors.textPrimary, + leadingIconColor = ElementTheme.colors.iconPrimary, + trailingIconColor = ElementTheme.colors.iconSecondary, + disabledTextColor = ElementTheme.colors.textDisabled, + disabledLeadingIconColor = ElementTheme.colors.iconDisabled, + disabledTrailingIconColor = ElementTheme.colors.iconDisabled, ) + + val contentPadding = PaddingValues(all = 12.dp) } @Preview(group = PreviewGroup.Menus) @@ -75,10 +84,36 @@ internal fun DropdownMenuItemPreview() = ElementThemedPreview { ContentToPreview @Composable private fun ContentToPreview() { - DropdownMenuItem( - text = { DropdownMenuItemText(text = "Item") }, - onClick = {}, - leadingIcon = { Icon(Icons.Default.BugReport, contentDescription = null) }, - trailingIcon = { Icon(Icons.Default.Share, contentDescription = null) }, - ) + Column { + DropdownMenuItem( + text = { Text(text = "Item") }, + onClick = {}, + trailingIcon = { Icon(Icons.Default.ArrowRight, contentDescription = null) }, + ) + Divider() + DropdownMenuItem( + text = { Text(text = "Item") }, + onClick = {}, + leadingIcon = { Icon(Icons.Default.BugReport, contentDescription = null) }, + ) + DropdownMenuItem( + text = { Text(text = "Item") }, + onClick = {}, + leadingIcon = { Icon(Icons.Default.BugReport, contentDescription = null) }, + trailingIcon = { Icon(Icons.Default.ArrowRight, contentDescription = null) }, + ) + DropdownMenuItem( + text = { Text(text = "Item") }, + onClick = {}, + enabled = false, + leadingIcon = { Icon(Icons.Default.BugReport, contentDescription = null) }, + trailingIcon = { Icon(Icons.Default.ArrowRight, contentDescription = null) }, + ) + Divider() + DropdownMenuItem( + text = { Text(text = "Multiline\nItem") }, + onClick = {}, + trailingIcon = { Icon(Icons.Default.ArrowRight, contentDescription = null) }, + ) + } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt index 8e7da6926e..2ca04f1008 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/previews/MenuPreview.kt @@ -30,8 +30,8 @@ import io.element.android.libraries.designsystem.preview.PreviewGroup import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.DropdownMenu import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem -import io.element.android.libraries.designsystem.theme.components.DropdownMenuItemText import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.Text @Preview(group = PreviewGroup.Menus) @Composable @@ -57,7 +57,7 @@ internal fun MenuPreview() { null } DropdownMenuItem( - text = { DropdownMenuItemText(text = "Item $i") }, + text = { Text(text = "Item $i") }, onClick = { isExpanded = false }, leadingIcon = leadingIcon, trailingIcon = trailingIcon, diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Menus_DropdownMenuItemPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Menus_DropdownMenuItemPreview_0_null,NEXUS_5,1.0,en].png index 4c91805426..305257631f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Menus_DropdownMenuItemPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Menus_DropdownMenuItemPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1765f4842cbd6e3c1b7daa9a9cc13fc0f4c47ff73c2cb1fdd7f3d2377eea0553 -size 9053 +oid sha256:e425d58f766655f1003c595637ab7387cdf74ba7ae7862bf2f137f3ffd33f3f3 +size 21202 From 5ecafeb49f11879b939d2d814290fa74df58ece8 Mon Sep 17 00:00:00 2001 From: ElementBot <110224175+ElementBot@users.noreply.github.com> Date: Fri, 11 Aug 2023 16:34:05 +0200 Subject: [PATCH 242/251] Sync Strings from Localazy (#1032) Co-authored-by: Florian14 --- .../src/main/res/values-ru/translations.xml | 10 + .../main/res/values-zh-rTW/translations.xml | 4 + .../src/main/res/values-fr/translations.xml | 2 +- .../src/main/res/values-ru/translations.xml | 15 ++ .../main/res/values-zh-rTW/translations.xml | 7 + .../src/main/res/values-ru/translations.xml | 9 + .../src/main/res/values-sk/translations.xml | 2 +- .../main/res/values-zh-rTW/translations.xml | 4 + .../src/main/res/values-ru/translations.xml | 9 + .../src/main/res/values-ru/translations.xml | 47 ++++ .../main/res/values-zh-rTW/translations.xml | 16 ++ .../src/main/res/values-ru/translations.xml | 8 + .../main/res/values-zh-rTW/translations.xml | 8 + .../src/main/res/values-ru/translations.xml | 39 ++++ .../main/res/values-zh-rTW/translations.xml | 26 +++ .../impl/src/main/res/values/localazy.xml | 1 + .../src/main/res/values-ru/translations.xml | 10 + .../main/res/values-zh-rTW/translations.xml | 8 + .../src/main/res/values-ru/translations.xml | 5 + .../src/main/res/values-ru/translations.xml | 15 ++ .../main/res/values-zh-rTW/translations.xml | 7 + .../src/main/res/values-ru/translations.xml | 38 ++++ .../main/res/values-zh-rTW/translations.xml | 27 +++ .../impl/src/main/res/values/localazy.xml | 2 +- .../src/main/res/values-ru/translations.xml | 9 + .../src/main/res/values-sk/translations.xml | 2 + .../main/res/values-zh-rTW/translations.xml | 4 + .../impl/src/main/res/values/localazy.xml | 2 + .../src/main/res/values-ru/translations.xml | 19 ++ .../main/res/values-zh-rTW/translations.xml | 9 + .../src/main/res/values-ru/translations.xml | 4 + .../src/main/res/values-ru/translations.xml | 57 +++++ .../main/res/values-zh-rTW/translations.xml | 37 +++ .../src/main/res/values-ru/translations.xml | 58 +++++ .../main/res/values-zh-rTW/translations.xml | 33 +++ .../src/main/res/values-ru/translations.xml | 18 ++ .../main/res/values-zh-rTW/translations.xml | 17 ++ .../src/main/res/values-ru/translations.xml | 213 ++++++++++++++++++ .../src/main/res/values-sk/translations.xml | 24 +- .../main/res/values-zh-rTW/translations.xml | 169 ++++++++++++++ .../src/main/res/values/localazy.xml | 8 +- 41 files changed, 996 insertions(+), 6 deletions(-) create mode 100644 features/analytics/impl/src/main/res/values-ru/translations.xml create mode 100644 features/analytics/impl/src/main/res/values-zh-rTW/translations.xml create mode 100644 features/createroom/impl/src/main/res/values-ru/translations.xml create mode 100644 features/createroom/impl/src/main/res/values-zh-rTW/translations.xml create mode 100644 features/ftue/impl/src/main/res/values-ru/translations.xml create mode 100644 features/ftue/impl/src/main/res/values-zh-rTW/translations.xml create mode 100644 features/invitelist/impl/src/main/res/values-ru/translations.xml create mode 100644 features/login/impl/src/main/res/values-ru/translations.xml create mode 100644 features/login/impl/src/main/res/values-zh-rTW/translations.xml create mode 100644 features/logout/api/src/main/res/values-ru/translations.xml create mode 100644 features/logout/api/src/main/res/values-zh-rTW/translations.xml create mode 100644 features/messages/impl/src/main/res/values-ru/translations.xml create mode 100644 features/messages/impl/src/main/res/values-zh-rTW/translations.xml create mode 100644 features/onboarding/impl/src/main/res/values-ru/translations.xml create mode 100644 features/onboarding/impl/src/main/res/values-zh-rTW/translations.xml create mode 100644 features/rageshake/api/src/main/res/values-ru/translations.xml create mode 100644 features/rageshake/impl/src/main/res/values-ru/translations.xml create mode 100644 features/rageshake/impl/src/main/res/values-zh-rTW/translations.xml create mode 100644 features/roomdetails/impl/src/main/res/values-ru/translations.xml create mode 100644 features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml create mode 100644 features/roomlist/impl/src/main/res/values-ru/translations.xml create mode 100644 features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml create mode 100644 features/verifysession/impl/src/main/res/values-ru/translations.xml create mode 100644 features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml create mode 100644 libraries/androidutils/src/main/res/values-ru/translations.xml create mode 100644 libraries/eventformatter/impl/src/main/res/values-ru/translations.xml create mode 100644 libraries/eventformatter/impl/src/main/res/values-zh-rTW/translations.xml create mode 100644 libraries/push/impl/src/main/res/values-ru/translations.xml create mode 100644 libraries/push/impl/src/main/res/values-zh-rTW/translations.xml create mode 100644 libraries/textcomposer/src/main/res/values-ru/translations.xml create mode 100644 libraries/textcomposer/src/main/res/values-zh-rTW/translations.xml create mode 100644 libraries/ui-strings/src/main/res/values-ru/translations.xml create mode 100644 libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml diff --git a/features/analytics/impl/src/main/res/values-ru/translations.xml b/features/analytics/impl/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..805cba6bf2 --- /dev/null +++ b/features/analytics/impl/src/main/res/values-ru/translations.xml @@ -0,0 +1,10 @@ + + + "Мы не будем записывать или профилировать какие-либо персональные данные" + "Предоставлять анонимные данные об использовании, чтобы помочь нам выявить проблемы." + "Вы можете ознакомиться со всеми нашими условиями %1$s." + "здесь" + "Вы можете отключить эту функцию в любое время" + "Мы не будем передавать ваши данные третьим лицам" + "Помогите улучшить %1$s" + diff --git a/features/analytics/impl/src/main/res/values-zh-rTW/translations.xml b/features/analytics/impl/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..3259b10fbd --- /dev/null +++ b/features/analytics/impl/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,4 @@ + + + "您可以在任何時候關閉它" + diff --git a/features/createroom/impl/src/main/res/values-fr/translations.xml b/features/createroom/impl/src/main/res/values-fr/translations.xml index 6be7345c97..e37a30c457 100644 --- a/features/createroom/impl/src/main/res/values-fr/translations.xml +++ b/features/createroom/impl/src/main/res/values-fr/translations.xml @@ -4,7 +4,7 @@ "Inviter des amis sur Element" "Inviter des personnes" "Une erreur s\'est produite lors de la création du salon" - "Les messages dans ce salon sont chiffrés. Une fopis activé, le chiffrement ne peut pas être désactivé." + "Les messages dans ce salon sont chiffrés. Une fois activé, le chiffrement ne peut pas être désactivé." "Salon privé (sur invitation uniquement)" "Les messages ne sont pas chiffrés et n\'importe qui peut les lire. Vous pouvez activer le chiffrement ultérieurement." "Salon public (n’importe qui)" diff --git a/features/createroom/impl/src/main/res/values-ru/translations.xml b/features/createroom/impl/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..7837f9e7de --- /dev/null +++ b/features/createroom/impl/src/main/res/values-ru/translations.xml @@ -0,0 +1,15 @@ + + + "Новая комната" + "Пригласите друзей в Element" + "Пригласить людей" + "Произошла ошибка при создании комнаты" + "Сообщения в этой комнате зашифрованы. Отключить шифрование впоследствии невозможно." + "Приватная комната (только по приглашению)" + "Сообщения не зашифрованы, и каждый может их прочитать. Вы можете включить шифрование позже." + "Публичная комната (любой)" + "Название комнаты" + "Тема (необязательно)" + "Произошла ошибка при попытке открытия комнаты" + "Создать комнату" + diff --git a/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml b/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..dd8afaf2e7 --- /dev/null +++ b/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,7 @@ + + + "邀請朋友使用 Element" + "聊天室名稱" + "主題(非必填)" + "建立聊天室" + diff --git a/features/ftue/impl/src/main/res/values-ru/translations.xml b/features/ftue/impl/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..db4fcd21fc --- /dev/null +++ b/features/ftue/impl/src/main/res/values-ru/translations.xml @@ -0,0 +1,9 @@ + + + "Звонки, опросы, поиск и многое другое будут добавлены позже в этом году." + "История сообщений для зашифрованных комнат в этом обновлении будет недоступна." + "Мы будем рады услышать ваше мнение, сообщите нам об этом через страницу настроек." + "Поехали!" + "Вот что вам необходимо знать:" + "Добро пожаловать в %1$s!" + diff --git a/features/ftue/impl/src/main/res/values-sk/translations.xml b/features/ftue/impl/src/main/res/values-sk/translations.xml index 1c22039a26..2438518334 100644 --- a/features/ftue/impl/src/main/res/values-sk/translations.xml +++ b/features/ftue/impl/src/main/res/values-sk/translations.xml @@ -1,6 +1,6 @@ - "Hovory, zdieľanie polohy, vyhľadávanie a ďalšie funkcie pribudnú neskôr v tomto roku." + "Hovory, ankety, vyhľadávanie a ďalšie funkcie pribudnú neskôr v tomto roku." "História správ pre zašifrované miestnosti nebude v tejto aktualizácii k dispozícii." "Radi by sme od vás počuli, dajte nám vedieť, čo si myslíte, prostredníctvom stránky nastavení." "Poďme na to!" diff --git a/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml b/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..6c5d482cb8 --- /dev/null +++ b/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,4 @@ + + + "開始吧!" + diff --git a/features/invitelist/impl/src/main/res/values-ru/translations.xml b/features/invitelist/impl/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..0f1c30cb09 --- /dev/null +++ b/features/invitelist/impl/src/main/res/values-ru/translations.xml @@ -0,0 +1,9 @@ + + + "Вы уверены, что хотите отклонить приглашение в %1$s?" + "Отклонить приглашение" + "Вы уверены, что хотите отказаться от приватного общения с %1$s?" + "Отклонить чат" + "Нет приглашений" + "%1$s (%2$s) пригласил вас" + diff --git a/features/login/impl/src/main/res/values-ru/translations.xml b/features/login/impl/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..33514e9f09 --- /dev/null +++ b/features/login/impl/src/main/res/values-ru/translations.xml @@ -0,0 +1,47 @@ + + + "Переключить аккаунт" + "Продолжить" + "Адрес домашнего сервера" + "Введите поисковый запрос или адрес домена." + "Поиск компании, сообщества или частного сервера." + "Поиск сервера учетной записи" + "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем." + "Вы собираетесь войти в %s" + "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем." + "Вы собираетесь создать учетную запись на %s" + "Matrix.org — это открытая сеть для безопасной децентрализованной связи." + "Другое" + "Используйте другого поставщика учетных записей, например, собственный частный сервер или рабочую учетную запись." + "Сменить поставщика учетной записи" + "Нам не удалось связаться с этим домашним сервером. Убедитесь, что вы правильно ввели URL-адрес домашнего сервера. Если URL-адрес указан правильно, обратитесь к администратору домашнего сервера за дополнительной помощью." + "В настоящее время этот сервер не поддерживает скользящую синхронизацию." + "URL-адрес домашнего сервера" + "Вы можете подключиться только к существующему серверу, поддерживающему скользящую синхронизацию. Администратору домашнего сервера потребуется настроить его. %1$s" + "Какой адрес у вашего сервера?" + "Данная учетная запись была деактивирована." + "Неверное имя пользователя и/или пароль" + "Это не корректный идентификатор пользователя. Ожидаемый формат: \'@user:homeserver.org\'" + "Выбранный домашний сервер не поддерживает пароль или логин OIDC. Пожалуйста, свяжитесь с администратором или выберите другой домашний сервер." + "Введите сведения о себе" + "Рады видеть вас снова!" + "Войти в %1$s" + "Сменить учетную запись" + "Частный сервер для сотрудников Element." + "Matrix — это открытая сеть для безопасной децентрализованной связи." + "Здесь будут храниться ваши разговоры - точно так же, как вы используете почтового провайдера для хранения своих писем." + "Вы собираетесь войти в %1$s" + "Вы собираетесь создать учетную запись на %1$s" + "В настоящее время существует высокий спрос на %1$s на %2$s. Вернитесь в приложение через несколько дней и попробуйте снова. + +Спасибо за терпение!" + "Добро пожаловать в %1$s!" + "Почти готово!" + "Вы зарегистрированы!" + "Продолжить" + "Выберите свой сервер" + "Пароль" + "Продолжить" + "Matrix — это открытая сеть для безопасной децентрализованной связи." + "Имя пользователя" + diff --git a/features/login/impl/src/main/res/values-zh-rTW/translations.xml b/features/login/impl/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..ae2ccae3f5 --- /dev/null +++ b/features/login/impl/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,16 @@ + + + "繼續" + "您即將登入%s" + "您即將在 %s 建立帳號" + "其他" + "歡迎回來!" + "您即將登入 %1$s" + "您即將在 %1$s 建立帳號" + "歡迎使用 %1$s!" + "繼續" + "選擇您的伺服器" + "密碼" + "繼續" + "使用者名稱" + diff --git a/features/logout/api/src/main/res/values-ru/translations.xml b/features/logout/api/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..3991e27251 --- /dev/null +++ b/features/logout/api/src/main/res/values-ru/translations.xml @@ -0,0 +1,8 @@ + + + "Вы уверены, что вы хотите выйти?" + "Выйти" + "Выполняется выход…" + "Выйти" + "Выйти" + diff --git a/features/logout/api/src/main/res/values-zh-rTW/translations.xml b/features/logout/api/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..df722a9467 --- /dev/null +++ b/features/logout/api/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,8 @@ + + + "您確定要登出嗎?" + "登出" + "正在登出…" + "登出" + "登出" + diff --git a/features/messages/impl/src/main/res/values-ru/translations.xml b/features/messages/impl/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..7870cbc331 --- /dev/null +++ b/features/messages/impl/src/main/res/values-ru/translations.xml @@ -0,0 +1,39 @@ + + + + "%1$d изменение в комнате" + "%1$d изменения в комнате" + "%1$d изменений в комнате" + + "Камера" + "Сделать фото" + "Записать видео" + "Вложение" + "Фото и Видео библиотека" + "Местоположение" + "В настоящее время история сообщений недоступна в этой комнате" + "Не удалось получить данные о пользователе" + "Хотите пригласить их снова?" + "Вы одни в этой комнате" + "Сообщение скопировано" + "У вас нет разрешения публиковать сообщения в этой комнате" + "Разрешить пользовательские настройки" + "Включение этого параметра отменяет настройки по умолчанию" + "Уведомить меня в этом чате" + "Вы можете изменить его в своем %1$s." + "Основные Настройки" + "Настройка по умолчанию" + "Произошла ошибка при загрузке настроек уведомлений." + "Не удалось восстановить режим по умолчанию, попробуйте еще раз." + "Не удалось настроить режим, попробуйте еще раз." + "Все сообщения" + "Только упоминания и ключевые слова" + "Показать меньше" + "Показать больше" + "Отправить снова" + "Не удалось отправить ваше сообщение" + "Добавить эмодзи" + "Показать меньше" + "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." + "Удалить" + diff --git a/features/messages/impl/src/main/res/values-zh-rTW/translations.xml b/features/messages/impl/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..d7344e54ca --- /dev/null +++ b/features/messages/impl/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,26 @@ + + + + "%1$d 個聊天室變更" + + + "還有 %1$d 個" + + "照相機" + "拍照" + "錄影" + "附件" + "位置" + "此聊天室只有您一個人" + "訊息已複製" + "您沒有權限在此聊天室傳送訊息" + "全域設定" + "預設" + "無法重設為預設模式,請再試一次。" + "無法設定模式,請再試一次。" + "所有訊息" + "只限提及與關鍵字" + "重傳" + "無法傳送您的訊息" + "移除" + diff --git a/features/messages/impl/src/main/res/values/localazy.xml b/features/messages/impl/src/main/res/values/localazy.xml index 6303589aa2..c88beff81a 100644 --- a/features/messages/impl/src/main/res/values/localazy.xml +++ b/features/messages/impl/src/main/res/values/localazy.xml @@ -13,6 +13,7 @@ "Attachment" "Photo & Video Library" "Location" + "Poll" "Message history is currently unavailable in this room" "Could not retrieve user details" "Would you like to invite them back?" diff --git a/features/onboarding/impl/src/main/res/values-ru/translations.xml b/features/onboarding/impl/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..5c8c12c2b0 --- /dev/null +++ b/features/onboarding/impl/src/main/res/values-ru/translations.xml @@ -0,0 +1,10 @@ + + + "Вход в систему вручную" + "Войти с помощью QR-кода" + "Создать учетную запись" + "Безопасное общение и совместная работа" + "Добро пожаловать в самый быстрый Element. Преимущество в скорости и простоте." + "Добро пожаловать в %1$s. Supercharged — это скорость и простота." + "Будь в своей стихии" + diff --git a/features/onboarding/impl/src/main/res/values-zh-rTW/translations.xml b/features/onboarding/impl/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..5150b50c9a --- /dev/null +++ b/features/onboarding/impl/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,8 @@ + + + "手動登入" + "使用 QR code 登入" + "建立帳號" + "歡迎使用有史以來最快的 Element。速度超快,操作簡便。" + "得心應手" + diff --git a/features/rageshake/api/src/main/res/values-ru/translations.xml b/features/rageshake/api/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..6cb17a3401 --- /dev/null +++ b/features/rageshake/api/src/main/res/values-ru/translations.xml @@ -0,0 +1,5 @@ + + + "При последнем использовании %1$s произошел сбой. Хотите поделиться отчетом о сбое?" + "Кажется, вы трясли телефон. Хотите открыть экран отчета об ошибке?" + diff --git a/features/rageshake/impl/src/main/res/values-ru/translations.xml b/features/rageshake/impl/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..8f05a3148d --- /dev/null +++ b/features/rageshake/impl/src/main/res/values-ru/translations.xml @@ -0,0 +1,15 @@ + + + "Приложить снимок экрана" + "Вы можете связаться со мной, если у Вас возникнут какие-либо дополнительные вопросы." + "Связаться со мной" + "Редактировать снимок экрана" + "Пожалуйста, опишите ошибку. Что вы сделали? Что вы ожидали, что произойдет? Что произошло на самом деле. Пожалуйста, опишите все как можно подробнее." + "Опишите ошибку…" + "Если возможно, пожалуйста, напишите описание на английском языке." + "Отправка журналов сбоев" + "Разрешить ведение журналов" + "Отправить снимок экрана" + "Чтобы убедиться, что все работает правильно, в сообщение будут включены журналы. Чтобы отправить сообщение без журналов, отключите эту настройку." + "При последнем использовании %1$s произошел сбой. Хотите поделиться отчетом о сбое?" + diff --git a/features/rageshake/impl/src/main/res/values-zh-rTW/translations.xml b/features/rageshake/impl/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..6e9eaabed3 --- /dev/null +++ b/features/rageshake/impl/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,7 @@ + + + "附上螢幕截圖" + "聯絡我" + "編輯螢幕截圖" + "傳送螢幕截圖" + diff --git a/features/roomdetails/impl/src/main/res/values-ru/translations.xml b/features/roomdetails/impl/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..4d2664ab30 --- /dev/null +++ b/features/roomdetails/impl/src/main/res/values-ru/translations.xml @@ -0,0 +1,38 @@ + + + + "%1$d пользователь" + "%1$d пользователя" + "%1$d пользователей" + + "Добавить тему" + "Уже зарегистрирован" + "Уже приглашены" + "Редактировать комнату" + "Произошла неизвестная ошибка, и информацию нельзя было изменить." + "Не удалось обновить комнату" + "Сообщения зашифрованы. Только у вас и у получателей есть уникальные ключи для их разблокировки." + "Шифрование сообщений включено" + "При загрузке настроек уведомлений произошла ошибка." + "Не удалось отключить звук в этой комнате, попробуйте еще раз." + "Не удалось включить звук в эту комнату, попробуйте еще раз." + "Пригласить участника" + "Пользовательский" + "По умолчанию" + "Уведомления" + "Название комнаты" + "Поделиться комнатой" + "Обновление комнаты…" + "В ожидании" + "Участники комнаты" + "Заблокировать" + "Заблокированные пользователи не смогут отправлять вам сообщения, а все их сообщения будут скрыты. Вы можете разблокировать их в любое время." + "Заблокировать пользователя" + "Разблокировать" + "Вы снова сможете увидеть все сообщения." + "Разблокировать пользователя" + "Покинуть комнату" + "Пользователи" + "Безопасность" + "Тема" + diff --git a/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml b/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..cbac73f938 --- /dev/null +++ b/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,27 @@ + + + + "%1$d 位夥伴" + + "新增主題" + "已是成員" + "已邀請" + "編輯聊天室" + "訊息已加密" + "邀請夥伴" + "自訂" + "預設" + "通知" + "聊天室名稱" + "分享聊天室" + "正在更新聊天室…" + "待定" + "聊天室成員" + "封鎖" + "封鎖使用者" + "解除封鎖" + "解除封鎖使用者" + "離開聊天室" + "夥伴" + "主題" + diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index 158ba386b4..b1f67dab1e 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -1,7 +1,7 @@ - "1 person" + "%1$d person" "%1$d people" "Add topic" diff --git a/features/roomlist/impl/src/main/res/values-ru/translations.xml b/features/roomlist/impl/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..fcd91c56e3 --- /dev/null +++ b/features/roomlist/impl/src/main/res/values-ru/translations.xml @@ -0,0 +1,9 @@ + + + "Создайте новую беседу или комнату" + "Начните переписку с отправки сообщения." + "Пока нет доступных чатов." + "Все чаты" + "Похоже, вы используете новое устройство. Чтобы получить доступ к зашифрованным сообщениям в дальнейшем, проверьте их на другом устройстве." + "Подтвердите, что это вы" + diff --git a/features/roomlist/impl/src/main/res/values-sk/translations.xml b/features/roomlist/impl/src/main/res/values-sk/translations.xml index 250822f4c9..0a1879a484 100644 --- a/features/roomlist/impl/src/main/res/values-sk/translations.xml +++ b/features/roomlist/impl/src/main/res/values-sk/translations.xml @@ -1,6 +1,8 @@ "Vytvorte novú konverzáciu alebo miestnosť" + "Začnite tým, že niekomu pošlete správu." + "Zatiaľ žiadne konverzácie." "Všetky konverzácie" "Vyzerá to tak, že používate nové zariadenie. Overte svoj prístup k zašifrovaným správam pomocou vášho druhého zariadenia." "Overte, že ste to vy" diff --git a/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml b/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..b4d22c5b26 --- /dev/null +++ b/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,4 @@ + + + "建立新的對話或聊天室" + diff --git a/features/roomlist/impl/src/main/res/values/localazy.xml b/features/roomlist/impl/src/main/res/values/localazy.xml index 3a1c3cbad6..d63f96d07c 100644 --- a/features/roomlist/impl/src/main/res/values/localazy.xml +++ b/features/roomlist/impl/src/main/res/values/localazy.xml @@ -1,6 +1,8 @@ "Create a new conversation or room" + "Get started by messaging someone." + "No chats yet." "All Chats" "Looks like you’re using a new device. Verify with another device to access your encrypted messages moving forwards." "Verify it’s you" diff --git a/features/verifysession/impl/src/main/res/values-ru/translations.xml b/features/verifysession/impl/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..552204fd7a --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-ru/translations.xml @@ -0,0 +1,19 @@ + + + "Кажется, что-то не так. Время ожидания запроса истекло, либо запрос был отклонен." + "Убедитесь, что приведенные ниже смайлики совпадают со смайликами, показанными во время другого сеанса." + "Сравните смайлики" + "Ваш новый сеанс подтвержден. У него есть доступ к вашим зашифрованным сообщениям, и другие пользователи увидят его как доверенное." + "Чтобы получить доступ к зашифрованной истории сообщений, докажите, что это вы." + "Открыть существующий сеанс" + "Повторить проверку" + "Я готов" + "Ожидание соответствия" + "Сравните уникальные смайлики, убедившись, что они расположены в том же порядке." + "Они не совпадают" + "Они совпадают" + "Для продолжения работы примите запрос на запуск процесса проверки в другом сеансе." + "Ожидание принятия запроса" + "Проверка отменена" + "Начать" + diff --git a/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml b/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..fc59911a93 --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,9 @@ + + + "我準備好了" + "等待比對" + "不相符" + "相符" + "驗證已取消" + "開始" + diff --git a/libraries/androidutils/src/main/res/values-ru/translations.xml b/libraries/androidutils/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..bb236dc893 --- /dev/null +++ b/libraries/androidutils/src/main/res/values-ru/translations.xml @@ -0,0 +1,4 @@ + + + "Не найдено совместимое приложение для обработки этого действия." + diff --git a/libraries/eventformatter/impl/src/main/res/values-ru/translations.xml b/libraries/eventformatter/impl/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..4c1defbb68 --- /dev/null +++ b/libraries/eventformatter/impl/src/main/res/values-ru/translations.xml @@ -0,0 +1,57 @@ + + + "(аватар тоже был изменен)" + "%1$s сменили свой аватар" + "Вы сменили аватар" + "%1$s изменил свое отображаемое имя с %2$s на %3$s" + "Вы изменили свое отображаемое имя с %1$s на %2$s" + "%1$s удалил свое отображаемое имя (оно было %2$s)" + "Вы удалили свое отображаемое имя (оно было %1$s)" + "%1$s установили свое отображаемое имя на %2$s" + "Вы установили отображаемое имя на %1$s" + "%1$s изменил аватар комнаты" + "Вы изменили аватар комнаты" + "%1$s удалил аватар комнаты" + "Вы удалили аватар комнаты" + "%1$s заблокирован %2$s" + "Вы заблокировали %1$s" + "%1$s создал комнату" + "Вы создали комнату" + "%1$s пригласил %2$s" + "%1$s принял приглашение" + "Вы приняли приглашение" + "Вы пригласили %1$s" + "Пользователь %1$s пригласил вас" + "%1$s присоединился к комнате" + "Вы вошли в комнату" + "%1$s запросил присоединение" + "%1$s разрешил %2$s присоединиться" + "%1$s разрешил вам присоединиться" + "Вы запросили присоединение" + "%1$s отклонил запрос %2$s на присоединение" + "Вы отклонили запрос %1$s на присоединение" + "%1$s отклонил ваш запрос на присоединение" + "%1$s больше не заинтересован в присоединении" + "Вы отменили запрос на присоединение" + "%1$s покинул комнату" + "Вы вышли из комнаты" + "%1$s изменил название комнаты на: %2$s" + "Вы изменили название комнаты на: %1$s" + "%1$s удалил название комнаты" + "Вы удалили название комнаты" + "%1$s отклонил приглашение" + "Вы отклонили приглашение" + "%1$s удалил %2$s" + "Вы удалили %1$s" + "%1$s отправила приглашение %2$s присоединиться к комнате" + "Вы отправили приглашение присоединиться к комнате %1$s" + "%1$s отозвал приглашение %2$s присоединиться к комнате" + "Вы отозвали приглашение %1$s присоединиться к комнате" + "%1$s изменил тему на: %2$s" + "Вы изменили тему на: %1$s" + "%1$s удалил тему комнаты" + "Вы удалили тему комнаты" + "%1$s разблокирован %2$s" + "Вы разблокировали %1$s" + "%1$s внес неизвестное изменение в составе" + diff --git a/libraries/eventformatter/impl/src/main/res/values-zh-rTW/translations.xml b/libraries/eventformatter/impl/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..45ab0acee1 --- /dev/null +++ b/libraries/eventformatter/impl/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,37 @@ + + + "%1$s將他的顯示名稱從%2$s變更為%3$s" + "您將您的顯示名稱從%1$s1變更為%2$s" + "%1$s的顯示名稱已被本人移除(原為%2$s)" + "您的顯示名稱已被您移除(原為%1$s)" + "%1$s將他的顯示名稱設為%2$s" + "您將您的顯示名稱設為%1$s" + "%1$s建立此聊天室" + "您建立此聊天室" + "%1$s邀請%2$s" + "%1$s接受邀請" + "您接受邀請" + "您邀請%1$s" + "%1$s邀請您" + "%1$s加入聊天室" + "您加入聊天室" + "%1$s請求加入" + "您請求加入" + "%1$s拒絕%2$s的加入請求" + "您拒絕%1$s的加入請求" + "%1$s拒絕您的加入請求" + "%1$s離開聊天室" + "您離開聊天室" + "%1$s將聊天室名稱變更為%2$s" + "您將聊天室名稱變更為%1$s" + "聊天室名稱已被%1$s移除" + "聊天室名稱已被您移除" + "%2$s已被%1$s移除" + "%1$s已被您移除" + "%1$s邀請%2$s加入聊天室" + "您邀請%1$s加入聊天室" + "%1$s將主題變更為%2$s" + "您將主題變更為%1$s" + "聊天室主題已被%1$s移除" + "聊天室主題已被您移除" + diff --git a/libraries/push/impl/src/main/res/values-ru/translations.xml b/libraries/push/impl/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..697a0f01d8 --- /dev/null +++ b/libraries/push/impl/src/main/res/values-ru/translations.xml @@ -0,0 +1,58 @@ + + + "Позвонить" + "Прослушивание событий" + "Шумные уведомления" + "Бесшумные уведомления" + "** Не удалось отправить - пожалуйста, откройте комнату" + "Присоединиться" + "Отклонить" + "Пригласил вас в чат" + "Новые сообщения" + "Отреагировал на %1$s" + "Отметить как прочитанное" + "Пригласил вас в комнату" + "Я" + "Вы просматриваете уведомление! Нажмите на меня!" + "%1$s: %2$s" + "%1$s: %2$s %3$s" + "%1$s и %2$s" + "%1$s в %2$s" + "%1$s в %2$s и %3$s" + + "%1$s: %2$d сообщение" + "%1$s: %2$d сообщения" + "%1$s: %2$d сообщений" + + + "%d уведомление" + "%d уведомления" + "%d уведомлений" + + + "%d приглашение" + "%d приглашения" + "%d приглашений" + + + "%d новое сообщение" + "%d новых сообщения" + "%d новых сообщений" + + + "%d непрочитанное уведомление" + "%d непрочитанных уведомления" + "%d непрочитанных уведомлений" + + + "%d комната" + "%d комнаты" + "%d комнат" + + "Выберите способ получения уведомлений" + "Фоновая синхронизация" + "Сервисы Google" + "Не найдены действующие службы Google Play. Уведомления могут работать некорректно." + "Уведомление" + "Быстрый ответ" + diff --git a/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml b/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..248fae8b0b --- /dev/null +++ b/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,33 @@ + + + "通話" + "無聲通知" + "加入" + "拒絕" + "邀請您聊天" + "新訊息" + "標示為已讀" + "邀請您加入聊天室" + "我" + "您正在查看通知!點我!" + + "%1$s:%2$d 則訊息" + + + "%d 個通知" + + + "%d 個邀請" + + + "%d 則新訊息" + + + "%d 個聊天室" + + "選擇接收通知的機制" + "背景同步" + "Google 服務" + "通知" + "快速回覆" + diff --git a/libraries/textcomposer/src/main/res/values-ru/translations.xml b/libraries/textcomposer/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..9f7324f086 --- /dev/null +++ b/libraries/textcomposer/src/main/res/values-ru/translations.xml @@ -0,0 +1,18 @@ + + + "Прикрепить файл" + "Переключить список маркеров" + "Переключить блок кода" + "Сообщение" + "Применить жирный шрифт" + "Применить курсивный формат" + "Применить формат зачеркивания" + "Применить формат подчеркивания" + "Переключение полноэкранного режима" + "Отступ" + "Применить встроенный формат кода" + "Установить ссылку" + "Переключить нумерованный список" + "Переключить цитату" + "Без отступа" + diff --git a/libraries/textcomposer/src/main/res/values-zh-rTW/translations.xml b/libraries/textcomposer/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..93777d4ca5 --- /dev/null +++ b/libraries/textcomposer/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,17 @@ + + + "新增附件" + "切換項目編號" + "切換程式碼區塊" + "訊息" + "套用粗體" + "套用斜體" + "套用刪除線" + "套用底線" + "切換全螢幕模式" + "增加縮排" + "設定連結" + "切換數字編號" + "切換引用" + "減少縮排" + diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..d4830e9127 --- /dev/null +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -0,0 +1,213 @@ + + + "Скрыть пароль" + "Отправить файлы" + "Показать пароль" + "Меню пользователя" + "Разрешить" + "Назад" + "Отмена" + "Выбрать фото" + "Очистить" + "Закрыть" + "Полная проверка" + "Подтвердить" + "Продолжить" + "Копировать" + "Скопировать ссылку" + "Скопировать ссылку в сообщение" + "Создать" + "Создать комнату" + "Отклонить" + "Отключить" + "Готово" + "Редактировать" + "Включить" + "Забыли пароль?" + "Переслать" + "Пригласить" + "Пригласить друзей" + "Пригласить друзей в %1$s" + "Пригласите пользователей в %1$s" + "Приглашения" + "Подробнее" + "Выйти" + "Покинуть комнату" + "Далее" + "Нет" + "Не сейчас" + "Ок" + "Открыть с помощью" + "Быстрый ответ" + "Цитата" + "Реакция" + "Удалить" + "Ответить" + "Сообщить об ошибке" + "Пожаловаться на содержание" + "Повторить" + "Повторите расшифровку" + "Сохранить" + "Поиск" + "Отправить" + "Отправить сообщение" + "Поделиться" + "Поделиться ссылкой" + "Пропустить" + "Начать" + "Начать чат " + "Начать подтверждение" + "Нажмите, чтобы загрузить карту" + "Сделать фото" + "Показать источник" + "Да" + "О приложении" + "Политика допустимого использования" + "Аналитика" + "Аудио" + "Пузыри" + "Авторское право" + "Создание комнаты…" + "Покинул комнату" + "Ошибка расшифровки" + "Для разработчика" + "(изменено)" + "Редактирование" + "%1$s%2$s" + "Шифрование включено" + "Ошибка" + "Файл" + "Файл сохранен в «Загрузки»" + "Переслать сообщение" + "GIF" + "Изображения" + "Идентификатор Matrix ID не найден, приглашение может быть не получено." + "Покинуть комнату" + "Ссылка скопирована в буфер обмена" + "Загрузка…" + "Сообщение" + "Оформление сообщений" + "Сообщение удалено" + "Современный" + "Без звука" + "Ничего не найдено" + "Не в сети" + "Пароль" + "Пользователи" + "Постоянная ссылка" + "Политика конфиденциальности" + "Реакции" + "Обновление…" + "Отвечает на %1$s" + "Сообщить об ошибке" + "Отчет отправлен" + "Название комнаты" + "например, название вашего проекта" + "Поиск человека" + "Результаты поиска" + "Безопасность" + "Выберите свой сервер" + "Отправка…" + "Сервер не поддерживается" + "Адрес сервера" + "Настройки" + "Делится местонахождением" + "Начало чата…" + "Стикер" + "Успешно" + "Рекомендации" + "Синхронизация" + "Уведомление о третьей стороне" + "Тема" + "О чем эта комната?" + "Невозможно расшифровать" + "Не удалось отправить приглашения одному или нескольким пользователям." + "Не удалось отправить приглашение(я)" + "Включить звук" + "Неподдерживаемое событие" + "Имя пользователя" + "Проверка отменена" + "Проверка завершена" + "Видео" + "Ожидание…" + "Подтверждение" + "Предупреждение" + "Деятельность" + "Флаги" + "Еда и напитки" + "Животные и природа" + "Объекты" + "Смайлы и люди" + "Путешествия и места" + "Символы" + "Не удалось создать постоянную ссылку" + "Не удалось загрузить карту %1$s. Пожалуйста, повторите попытку позже." + "Не удалось загрузить сообщения" + "%1$s не удалось получить доступ к вашему местоположению. Пожалуйста, повторите попытку позже." + "У %1$s нет разрешения на доступ к вашему местоположению. Вы можете разрешить доступ в Настройках." + "У %1$s нет разрешения на доступ к вашему местоположению. Разрешите доступ ниже." + "Некоторые сообщения не были отправлены" + "Извините, произошла ошибка" + "🔐️ Присоединяйтесь ко мне в %1$s" + "Привет, поговори со мной по %1$s: %2$s" + "Вы уверены, что хотите покинуть эту комнату? Вы здесь единственный человек. Если вы уйдете, никто не сможет присоединиться в будущем, включая вас." + "Вы уверены, что хотите покинуть эту комнату? Эта комната не является публичной, и Вы не сможете присоединиться к ней без приглашения." + "Вы уверены, что хотите покинуть комнату?" + "%1$s Android" + + "%1$d участник" + "%1$d участников" + "%1$d участников" + + + "%d голос" + "%d голоса" + "%d голосов" + + "Rageshake сообщит об ошибке" + "Кажется, вы трясли телефон. Хотите открыть экран отчета об ошибке?" + "Это сообщение будет передано администратору вашего домашнего сервера. Они не смогут прочитать зашифрованные сообщения." + "Причина, по которой вы пожаловались на этот контент" + "Это начало %1$s." + "Это начало разговора." + "Новый" + "Делитесь данными аналитики" + "Не удалось выбрать носитель, попробуйте еще раз." + "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." + "Не удалось загрузить медиафайлы, попробуйте еще раз." + "Это одноразовый процесс, спасибо, что подождали." + "Настройка учетной записи." + "Дополнительные параметры" + "Аудио и видео звонки" + "Прямые чаты" + "Включить уведомления на данном устройстве" + "Групповые чаты" + "Упоминания" + "Все" + "Упоминания" + "Уведомить меня" + "Уведомить меня в @room" + "Чтобы получать уведомления, измените свой %1$s." + "Настройки системы" + "Системные уведомления выключены" + "Уведомления" + "Отметьте, хотите ли вы скрыть все текущие и будущие сообщения от этого пользователя" + "Поделиться местоположением" + "Поделиться моим местоположением" + "Открыть в Apple Maps" + "Открыть в Google Картах" + "Открыть в OpenStreetMap" + "Поделиться этим местоположением" + "Местоположение" + "Rageshake" + "Порог обнаружения" + "Основные" + "Версия: %1$s (%2$s)" + "en" + "Ошибка" + "Успешно" + "Предоставлять анонимные данные об использовании, чтобы помочь нам выявить проблемы." + "Вы можете ознакомиться со всеми нашими условиями %1$s." + "здесь" + "Заблокировать пользователя" + diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index 726d1b1505..14dd5626c8 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -40,6 +40,7 @@ "Otvoriť pomocou" "Rýchla odpoveď" "Citovať" + "Reagovať" "Odstrániť" "Odpovedať" "Nahlásiť chybu" @@ -94,6 +95,9 @@ "Heslo" "Ľudia" "Trvalý odkaz" + "Výsledné hlasovanie: %1$s" + "Celkový počet hlasov: %1$s" + "Výsledky sa zobrazia po ukončení ankety" "Zásady ochrany osobných údajov" "Reakcie" "Obnovuje sa…" @@ -143,8 +147,8 @@ "%1$s nedokázal načítať mapu. Skúste to prosím neskôr." "Načítanie správ zlyhalo" "%1$s nemohol získať prístup k vašej polohe. Skúste to prosím neskôr." - "Ak chcete odoslať polohu, povoľte %1$s prístup k vašej polohe z obrazovky nastavení." - "Ak chcete odoslať polohu, povoľte %1$s prístup k vašej polohe v nasledujúcom dialógovom okne." + "%1$s nemá povolenie na prístup k vašej polohe. Prístup môžete zapnúť v Nastaveniach." + "%1$s nemá povolenie na prístup k vašej polohe. Povoľte prístup nižšie." "Niektoré správy neboli odoslané" "Prepáčte, vyskytla sa chyba" "🔐️ Pripojte sa ku mne na %1$s" @@ -158,6 +162,11 @@ "%1$d členovia" "%1$d členov" + + "1 hlas" + "%d hlasy" + "%d hlasov" + "Zúrivo potriasť pre nahlásenie chyby" "Zdá sa, že zúrivo trasiete telefónom. Chcete otvoriť obrazovku s nahlásením chýb?" "Táto správa bude nahlásená správcovi vášho domovského servera. Nebude môcť prečítať žiadne šifrované správy." @@ -171,7 +180,18 @@ "Nepodarilo sa nahrať médiá, skúste to prosím znova." "Ide o jednorazový proces, ďakujeme za trpezlivosť." "Nastavenie vášho účtu." + "Ďalšie nastavenia" + "Audio a video hovory" + "Priame konverzácie" + "Pri priamych rozhovoroch ma upozorniť na" + "Pri skupinových rozhovoroch ma upozorniť na" "Povoliť oznámenia na tomto zariadení" + "Skupinové rozhovory" + "Zmienky" + "Všetky" + "Zmienky" + "Upozorniť ma na" + "Upozorniť ma na @miestnosť" "Ak chcete dostávať oznámenia, zmeňte prosím svoje %1$s." "nastavenia systému" "Systémové oznámenia sú vypnuté" diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..701a6243ac --- /dev/null +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,169 @@ + + + "隱藏密碼" + "傳送檔案" + "顯示密碼" + "使用者選單" + "接受" + "返回" + "取消" + "選擇照片" + "清除" + "關閉" + "完成驗證" + "確認" + "繼續" + "複製" + "複製連結" + "建立" + "建立聊天室" + "停用" + "完成" + "編輯" + "啟用" + "忘記密碼?" + "轉寄" + "邀請" + "邀請朋友" + "邀請朋友使用%1$s" + "邀請夥伴使用%1$s" + "邀請" + "了解更多" + "離開" + "離開聊天室" + "下一個" + "否" + "以後再說" + "OK" + "用其他方式開啟" + "快速回覆" + "引用" + "回應" + "移除" + "回覆" + "檢舉內容" + "再試一次" + "再次嘗試解密" + "儲存" + "搜尋" + "傳送" + "傳送訊息" + "分享" + "分享連結" + "跳過" + "開始" + "開始聊天" + "開始驗證" + "點擊以載入地圖" + "拍照" + "檢視原始碼" + "是" + "關於" + "分析" + "音訊" + "著作權" + "正在建立聊天室…" + "離開聊天室" + "解密錯誤" + "開發者選項" + "(已編輯)" + "編輯中" + "已啟用加密" + "錯誤" + "檔案" + "檔案已儲存至 Downloads" + "訊息轉寄" + "GIF" + "圖片" + "找不到此 Matrix ID,因此可能沒有人會收到邀請。" + "正在離開聊天室" + "連結已複製到剪貼簿" + "載入中…" + "訊息" + "訊息布局" + "訊息已移除" + "現代" + "關閉通知" + "查無結果" + "離線" + "密碼" + "夥伴" + "永久連結" + "結果將在投票結束後公佈" + "隱私權政策" + "回應" + "重新整理…" + "正在回覆%1$s" + "聊天室名稱" + "範例:您的計畫名稱" + "搜尋結果" + "選擇您的伺服器" + "傳送中…" + "伺服器 URL" + "設定" + "貼圖" + "成功" + "建議" + "同步中" + "主題" + "無法解密" + "無法發送邀請給一或多個使用者。" + "無法發送邀請" + "開啟通知" + "使用者名稱" + "驗證已取消" + "驗證完成" + "影片" + "等待中…" + "確認" + "警告" + "活動" + "旗幟" + "食物與飲料" + "動物與大自然" + "物品" + "表情與人物" + "旅行與景點" + "標誌" + "無法建立永久連結" + "%1$s無法載入地圖。請稍後再試。" + "無法載入訊息" + "%1$s無法取得您的位置。請稍後再試。" + "有些訊息尚未傳送" + "您確定要離開聊天室嗎?這裡只有您一個人。如果您離開了,包含您在內的所有人都無法再進入此聊天室。" + "您確定要離開聊天室嗎?此聊天室不是公開的,如果沒有收到邀請,您無法重新加入。" + "您確定要離開聊天室嗎?" + "%1$s Android" + + "%1$d 位成員" + + + "%d 票" + + "檢舉這個內容的原因" + "新訊息" + "無法上傳媒體檔案,請稍後再試。" + "設定您的帳號" + "其他設定" + "私訊" + "在這個裝置上開啟通知" + "群組聊天" + "提及" + "提及" + "系統設定" + "已關閉系統通知" + "通知" + "分享位置" + "分享我的位置" + "在 Apple 地圖中開啟" + "在 Google 地圖中開啟" + "在開放街圖(OpenStreetMap) 中開啟" + "分享這個位置" + "位置" + "一般" + "版本:%1$s(%2$s)" + "zh-tw" + "錯誤" + "成功" + "封鎖使用者" + diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index f8ad46d447..3b4c305ffc 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -95,6 +95,9 @@ "Password" "People" "Permalink" + "Final votes: %1$s" + "Total votes: %1$s" + "Results will show after the poll has ended" "Privacy policy" "Reactions" "Refreshing…" @@ -159,7 +162,7 @@ "%1$d members" - "1 vote" + "%d vote" "%d votes" "Rageshake to report bug" @@ -178,6 +181,9 @@ "Additional settings" "Audio and video calls" "Direct chats" + "An error occurred while updating the notification setting." + "On direct chats, notify me for" + "On group chats, notify me for" "Enable notifications on this device" "Group chats" "Mentions" From a04cb2963c0047c509af242495cc781e804a8280 Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 11 Aug 2023 16:47:06 +0100 Subject: [PATCH 243/251] Show selected reactions on the emoji picker. (#1014) * Show selected reactions on the emoji picker. * Unused import * Update screenshots * Use ImmutableSet * Fix lint issues. --------- Co-authored-by: ElementBot --- .../messages/impl/MessagesStateProvider.kt | 2 ++ .../features/messages/impl/MessagesView.kt | 4 ++-- .../impl/timeline/components/EmojiPicker.kt | 18 +++++++++++++++-- .../CustomReactionBottomSheet.kt | 3 ++- .../customreaction/CustomReactionEvents.kt | 4 ++-- .../customreaction/CustomReactionPresenter.kt | 10 ++++++---- .../customreaction/CustomReactionState.kt | 2 ++ .../CustomReactionPresenterTests.kt | 20 ++++++++++++++++++- ...ckerDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- ...kerLightPreview_0_null,NEXUS_5,1.0,en].png | 4 ++-- 10 files changed, 55 insertions(+), 16 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index 7da67d468c..9b3f5073a1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -30,6 +30,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.textcomposer.MessageComposerMode +import kotlinx.collections.immutable.persistentSetOf open class MessagesStateProvider : PreviewParameterProvider { override val values: Sequence @@ -68,6 +69,7 @@ fun aMessagesState() = MessagesState( customReactionState = CustomReactionState( selectedEventId = null, eventSink = {}, + selectedEmoji = persistentSetOf(), ), reactionSummaryState = ReactionSummaryState( target = null, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 34ac14af57..116480479f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -135,7 +135,7 @@ fun MessagesView( } fun onMoreReactionsClicked(event: TimelineItem.Event) { - state.customReactionState.eventSink(CustomReactionEvents.UpdateSelectedEvent(event.eventId)) + state.customReactionState.eventSink(CustomReactionEvents.UpdateSelectedEvent(event)) } Scaffold( @@ -187,7 +187,7 @@ fun MessagesView( state = state.actionListState, onActionSelected = ::onActionSelected, onCustomReactionClicked = { event -> - state.customReactionState.eventSink(CustomReactionEvents.UpdateSelectedEvent(event.eventId)) + state.customReactionState.eventSink(CustomReactionEvents.UpdateSelectedEvent(event)) }, onEmojiReactionClicked = ::onEmojiReactionClicked, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/EmojiPicker.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/EmojiPicker.kt index 6e121685f2..effd7f23f0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/EmojiPicker.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/EmojiPicker.kt @@ -17,6 +17,7 @@ package io.element.android.features.messages.impl.timeline.components import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement @@ -31,6 +32,7 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.Tab import androidx.compose.material3.TabRow @@ -39,6 +41,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.vanniktech.emoji.Emoji @@ -48,12 +51,15 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.theme.ElementTheme +import kotlinx.collections.immutable.ImmutableSet +import kotlinx.collections.immutable.persistentSetOf import kotlinx.coroutines.launch @OptIn(ExperimentalFoundationApi::class) @Composable fun EmojiPicker( onEmojiSelected: (Emoji) -> Unit, + selectedEmojis: ImmutableSet, modifier: Modifier = Modifier, ) { val coroutineScope = rememberCoroutineScope() @@ -91,12 +97,19 @@ fun EmojiPicker( modifier = Modifier.fillMaxSize(), columns = GridCells.Adaptive(minSize = 40.dp), contentPadding = PaddingValues(vertical = 10.dp, horizontal = 16.dp), - horizontalArrangement = Arrangement.SpaceEvenly, + horizontalArrangement = Arrangement.spacedBy(8.dp), ) { items(category.emojis, key = { it.unicode }) { item -> + val backgroundColor = if (selectedEmojis.contains(item.unicode)) { + ElementTheme.colors.bgActionPrimaryRest + } else { + Color.Transparent + } + Box( modifier = Modifier .size(40.dp) + .background(backgroundColor, CircleShape) .clickable( enabled = true, onClick = { onEmojiSelected(item) }, @@ -132,6 +145,7 @@ internal fun EmojiPickerDarkPreview() { private fun ContentToPreview() { EmojiPicker( onEmojiSelected = {}, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), + selectedEmojis = persistentSetOf("😀", "😄", "😃") ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt index 70c2a7dc10..d817ec0cd4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionBottomSheet.kt @@ -57,7 +57,8 @@ fun CustomReactionBottomSheet( ) { EmojiPicker( onEmojiSelected = ::onEmojiSelectedDismiss, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), + selectedEmojis = state.selectedEmoji, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionEvents.kt index b7c210553e..a0d69df372 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionEvents.kt @@ -16,8 +16,8 @@ package io.element.android.features.messages.impl.timeline.components.customreaction -import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.features.messages.impl.timeline.model.TimelineItem sealed interface CustomReactionEvents { - data class UpdateSelectedEvent(val eventId: EventId?) : CustomReactionEvents + data class UpdateSelectedEvent(val event: TimelineItem.Event?) : CustomReactionEvents } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt index 0a23d42085..f094f2dbc6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt @@ -21,22 +21,24 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember 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.core.EventId +import kotlinx.collections.immutable.toImmutableSet import javax.inject.Inject class CustomReactionPresenter @Inject constructor() : Presenter { @Composable override fun present(): CustomReactionState { - var selectedEventId by remember { mutableStateOf(null) } + var selectedEvent by remember { mutableStateOf(null) } fun handleEvents(event: CustomReactionEvents) { when (event) { - is CustomReactionEvents.UpdateSelectedEvent -> selectedEventId = event.eventId + is CustomReactionEvents.UpdateSelectedEvent -> selectedEvent = event.event } } - return CustomReactionState(selectedEventId = selectedEventId, eventSink = ::handleEvents) + val selectedEmoji = selectedEvent?.reactionsState?.reactions?.mapNotNull { if(it.isHighlighted) it.key else null }.orEmpty().toImmutableSet() + return CustomReactionState(selectedEventId = selectedEvent?.eventId, selectedEmoji = selectedEmoji, eventSink = ::handleEvents) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionState.kt index 6c0c7f3599..9de1642dff 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionState.kt @@ -17,8 +17,10 @@ package io.element.android.features.messages.impl.timeline.components.customreaction import io.element.android.libraries.matrix.api.core.EventId +import kotlinx.collections.immutable.ImmutableSet data class CustomReactionState( val selectedEventId: EventId?, + val selectedEmoji: ImmutableSet, val eventSink: (CustomReactionEvents) -> Unit, ) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt index 1c40483ffe..84628cedae 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/customreaction/CustomReactionPresenterTests.kt @@ -20,6 +20,8 @@ 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.features.messages.impl.timeline.aTimelineItemReactions import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter import io.element.android.libraries.matrix.test.AN_EVENT_ID @@ -38,11 +40,27 @@ class CustomReactionPresenterTests { val initialState = awaitItem() assertThat(initialState.selectedEventId).isNull() - initialState.eventSink(CustomReactionEvents.UpdateSelectedEvent(AN_EVENT_ID)) + initialState.eventSink(CustomReactionEvents.UpdateSelectedEvent(aTimelineItemEvent(eventId = AN_EVENT_ID))) assertThat(awaitItem().selectedEventId).isEqualTo(AN_EVENT_ID) initialState.eventSink(CustomReactionEvents.UpdateSelectedEvent(null)) assertThat(awaitItem().selectedEventId).isNull() } } + + @Test + fun `present - handle selected emojis`() = runTest { + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.selectedEventId).isNull() + val reactions = aTimelineItemReactions(count = 1, isHighlighted = true) + val key = reactions.reactions.first().key + initialState.eventSink(CustomReactionEvents.UpdateSelectedEvent(aTimelineItemEvent(eventId = AN_EVENT_ID, timelineItemReactions = reactions))) + val stateWithSelectedEmojis = awaitItem() + assertThat(stateWithSelectedEmojis.selectedEventId).isEqualTo(AN_EVENT_ID) + assertThat(stateWithSelectedEmojis.selectedEmoji).contains(key) + } + } } diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_EmojiPickerDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_EmojiPickerDarkPreview_0_null,NEXUS_5,1.0,en].png index e10b00e85b..d6abcd7838 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_EmojiPickerDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_EmojiPickerDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dedb3940216fd18804157f708340a535008cc753a579ced3ac4153f99bf01218 -size 175737 +oid sha256:c5650ec2689d4ce0cf0785e12d5cb899c1bdfdad14c1f75106be1068df662f53 +size 188815 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_EmojiPickerLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_EmojiPickerLightPreview_0_null,NEXUS_5,1.0,en].png index e3804f7073..5898fc6fa7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_EmojiPickerLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_EmojiPickerLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19d8a7e0d477cc7db1bea85407b71516347b158777db2e3889734f2129b69de3 -size 175762 +oid sha256:2e5e11d9c1e7d4b950a0d080e8104f08b26fac56332d243502430dbfdd02c60b +size 188259 From c15483828d03b7b112d1e9737665952ce5400ad0 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 11 Aug 2023 23:32:31 +0200 Subject: [PATCH 244/251] Update rust sdk to 0.1.42 (changes in tracing and sync apis) (#1055) * Update rust sdk to 0.1.42 (changes in tracing and sync apis) * Fix sample compilation --------- Co-authored-by: ganfra --- .../android/appnav/LoggedInFlowNode.kt | 4 +++- gradle/libs.versions.toml | 2 +- .../libraries/matrix/api/sync/SyncService.kt | 2 +- .../matrix/impl/sync/RustSyncService.kt | 4 ++-- .../matrix/impl/tracing/LogEventLocation.kt | 8 +++----- .../matrix/impl/tracing/RustTracingService.kt | 7 ++++--- .../matrix/impl/tracing/RustTracingTree.kt | 9 ++++++--- .../matrix/test/sync/FakeSyncService.kt | 2 +- .../android/samples/minimal/RoomListScreen.kt | 4 +++- .../android/samples/minimal/Singleton.kt | 19 ++++++++++++++++++- 10 files changed, 42 insertions(+), 19 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 1355035577..7943151a5e 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -141,7 +141,9 @@ class LoggedInFlowNode @AssistedInject constructor( }, onStop = { //Counterpart startSync is done in observeSyncStateAndNetworkStatus method. - syncService.stopSync() + coroutineScope.launch { + syncService.stopSync() + } }, onDestroy = { plugins().forEach { it.onFlowReleased(id, inputs.matrixClient) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7f3540a2e8..97717de05d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -145,7 +145,7 @@ jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" } timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.41" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.42" sqldelight-driver-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" } sqldelight-driver-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" } sqldelight-coroutines = { module = "com.squareup.sqldelight:coroutines-extensions", version.ref = "sqldelight" } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncService.kt index 5271ec9bc0..994b35edc4 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/sync/SyncService.kt @@ -27,7 +27,7 @@ interface SyncService { /** * Tries to stop the sync. If service is not syncing it has no effect. */ - fun stopSync(): Result + suspend fun stopSync(): Result /** * Flow of [SyncState]. Will be updated as soon as the current [SyncState] changes. diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt index 87fa02edd2..932da42afb 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/sync/RustSyncService.kt @@ -41,9 +41,9 @@ class RustSyncService( Timber.d("Start sync failed: $it") } - override fun stopSync() = runCatching { + override suspend fun stopSync() = runCatching { Timber.i("Stop sync") - innerSyncService.pause() + innerSyncService.stop() }.onFailure { Timber.d("Stop sync failed: $it") } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/LogEventLocation.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/LogEventLocation.kt index 51b8923cc8..712735649c 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/LogEventLocation.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/LogEventLocation.kt @@ -22,8 +22,7 @@ package io.element.android.libraries.matrix.impl.tracing */ data class LogEventLocation( val file: String, - val line: UInt, - val column: UInt, + val line: UInt?, ) { companion object { @@ -32,9 +31,8 @@ data class LogEventLocation( */ fun from(stackTraceElement: StackTraceElement): LogEventLocation { return LogEventLocation( - file = stackTraceElement.fileName, - line = stackTraceElement.lineNumber.toUInt(), - column = 0u, + file = stackTraceElement.fileName ?: "", + line = stackTraceElement.lineNumber.takeIf { it >= 0 }?.toUInt() ) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt index d3bd53bd10..c211f48c05 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt @@ -17,6 +17,7 @@ package io.element.android.libraries.matrix.impl.tracing import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.api.tracing.TracingConfiguration import io.element.android.libraries.matrix.api.tracing.TracingService @@ -26,13 +27,13 @@ import timber.log.Timber import javax.inject.Inject @ContributesBinding(AppScope::class) -class RustTracingService @Inject constructor() : TracingService { +class RustTracingService @Inject constructor(private val buildMeta: BuildMeta) : TracingService { override fun setupTracing(tracingConfiguration: TracingConfiguration) { val filter = tracingConfiguration.filterConfiguration val rustTracingConfiguration = org.matrix.rustcomponents.sdk.TracingConfiguration( filter = tracingConfiguration.filterConfiguration.filter, - writeToStdoutOrSystem = tracingConfiguration.writesToLogcat, + writeToStdoutOrSystem = tracingConfiguration.writesToLogcat, writeToFiles = when (val writeToFilesConfiguration = tracingConfiguration.writesToFilesConfiguration) { is WriteToFilesConfiguration.Disabled -> null is WriteToFilesConfiguration.Enabled -> TracingFileConfiguration( @@ -46,6 +47,6 @@ class RustTracingService @Inject constructor() : TracingService { } override fun createTimberTree(): Timber.Tree { - return RustTracingTree() + return RustTracingTree(retrieveFromStackTrace = buildMeta.isDebuggable) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingTree.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingTree.kt index 2131bb83ba..275994081d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingTree.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingTree.kt @@ -35,15 +35,18 @@ private val fqcnIgnore = listOf( /** * A Timber tree that passes logs to the Rust SDK. */ -internal class RustTracingTree : Timber.Tree() { +internal class RustTracingTree(private val retrieveFromStackTrace: Boolean) : Timber.Tree() { override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { - val location = getLogEventLocationFromStackTrace() + val location = if (retrieveFromStackTrace) { + getLogEventLocationFromStackTrace() + } else { + LogEventLocation("", null) + } val logLevel = priority.toLogLevel() logEvent( file = location.file, line = location.line, - column = location.column, level = logLevel, target = Target.ELEMENT.filter, message = message, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/sync/FakeSyncService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/sync/FakeSyncService.kt index dd653a76ec..4e618deb9a 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/sync/FakeSyncService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/sync/FakeSyncService.kt @@ -34,7 +34,7 @@ class FakeSyncService : SyncService { return Result.success(Unit) } - override fun stopSync(): Result { + override suspend fun stopSync(): Result { syncStateFlow.value = SyncState.Terminated return Result.success(Unit) } diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt index b394e1c04d..faaccc9b8e 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt @@ -113,7 +113,9 @@ class RoomListScreen( } onDispose { Timber.w("Stop sync!") - matrixClient.syncService().stopSync() + runBlocking { + matrixClient.syncService().stopSync() + } } } } diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt index 77057d2e45..027da552fa 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/Singleton.kt @@ -17,6 +17,8 @@ package io.element.android.samples.minimal import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.matrix.api.tracing.TracingConfiguration import io.element.android.libraries.matrix.api.tracing.TracingFilterConfigurations import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration @@ -28,13 +30,28 @@ import kotlinx.coroutines.plus object Singleton { + private val buildMeta = BuildMeta( + isDebuggable = true, + buildType = BuildType.DEBUG, + applicationName = "EAX-Minimal", + applicationId = "io.element.android.samples.minimal", + lowPrivacyLoggingEnabled = false, + versionName = "0.1.0", + versionCode = 1, + gitRevision = "TODO", // BuildConfig.GIT_REVISION, + gitRevisionDate = "TODO", // BuildConfig.GIT_REVISION_DATE, + gitBranchName = "TODO", // BuildConfig.GIT_BRANCH_NAME, + flavorDescription = "TODO", // BuildConfig.FLAVOR_DESCRIPTION, + flavorShortDescription = "TODO", // BuildConfig.SHORT_FLAVOR_DESCRIPTION, + ) + init { val tracingConfiguration = TracingConfiguration( filterConfiguration = TracingFilterConfigurations.debug, writesToLogcat = true, writesToFilesConfiguration = WriteToFilesConfiguration.Disabled ) - RustTracingService().setupTracing(tracingConfiguration) + RustTracingService(buildMeta).setupTracing(tracingConfiguration) } val appScope = MainScope() + CoroutineName("Minimal Scope") From 086e5bbd913c0817f2e1c3f82a673207df8aeddb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 16 Aug 2023 08:53:23 +0200 Subject: [PATCH 245/251] Update dependency io.mockk:mockk to v1.13.7 (#1059) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 97717de05d..e7a352e2e2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -123,7 +123,7 @@ test_junit = "junit:junit:4.13.2" test_runner = "androidx.test:runner:1.5.2" test_uiautomator = "androidx.test.uiautomator:uiautomator:2.2.0" test_junitext = "androidx.test.ext:junit:1.1.5" -test_mockk = "io.mockk:mockk:1.13.5" +test_mockk = "io.mockk:mockk:1.13.7" test_barista = "com.adevinta.android:barista:4.3.0" test_hamcrest = "org.hamcrest:hamcrest:2.2" test_orchestrator = "androidx.test:orchestrator:1.4.2" From e2489209090372ef502f05d6c1559f20bdeddda8 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 16 Aug 2023 12:38:28 +0200 Subject: [PATCH 246/251] [Compound] Implement Snackbars based on designs (#1054) * Make `InternalButton` internal instead of private so it can be customised. Also, change the `ButtonColors.contentColor` for text buttons to `LocalContentColor.current` by default. * Add temporary color for Snackbar action label * Implement `Snackbar` component based on Compound * Propagate changes to all other components * Use right Preview annotation config * Move `ButtonVisuals` to their own file * Update screenshots * Make previews internal * Update screenshots * Set a custom token for contentColor in AppBars * Change 'Label' to 'Action' in the previews * Add changelog * Update screenshots --------- Co-authored-by: ElementBot --- changelog.d/1054.misc | 1 + .../analytics/impl/AnalyticsOptInView.kt | 2 +- .../impl/components/InviteSummaryRow.kt | 4 +- .../features/messages/impl/MessagesView.kt | 2 +- .../impl/media/viewer/MediaViewerView.kt | 14 +- .../impl/root/PreferencesRootView.kt | 11 +- .../features/roomlist/impl/RoomListView.kt | 11 +- .../components/RequestVerificationHeader.kt | 2 +- .../components/button/ButtonVisuals.kt | 53 +++++++ .../designsystem/preview/PreviewGroup.kt | 1 + .../designsystem/ruler/WithRulers.kt | 2 +- .../theme/components/AlertDialogContent.kt | 6 +- .../designsystem/theme/components/Button.kt | 143 ++++++++++------- .../theme/components/MediumTopAppBar.kt | 23 ++- .../designsystem/theme/components/Snackbar.kt | 147 ++++++++++++++++++ .../theme/components/TopAppBar.kt | 23 ++- .../libraries/designsystem/utils/Snackbar.kt | 19 +++ .../android/libraries/theme/LegacyColors.kt | 5 + ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_7,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_9,NEXUS_5,1.0,en].png | 4 +- ...stDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...tLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...arkPreview--1_1_null_8,NEXUS_5,1.0,en].png | 4 +- ...ghtPreview--0_0_null_8,NEXUS_5,1.0,en].png | 4 +- ...opAppBarPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...opAppBarPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...ckbars_Snackbar_0_null,NEXUS_5,1.0,en].png | 3 + ...ckbarwithaction_0_null,NEXUS_5,1.0,en].png | 3 + ...nandclosebutton_0_null,NEXUS_5,1.0,en].png | 3 + ...buttononnewline_0_null,NEXUS_5,1.0,en].png | 3 + ...actiononnewline_0_null,NEXUS_5,1.0,en].png | 3 + 49 files changed, 441 insertions(+), 147 deletions(-) create mode 100644 changelog.d/1054.misc create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonVisuals.kt create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbar_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithaction_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactiononnewline_0_null,NEXUS_5,1.0,en].png diff --git a/changelog.d/1054.misc b/changelog.d/1054.misc new file mode 100644 index 0000000000..7a0a477d76 --- /dev/null +++ b/changelog.d/1054.misc @@ -0,0 +1 @@ +Compound: implement Snackbar component. diff --git a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt index 817bf9a600..54b9add785 100644 --- a/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt +++ b/features/analytics/impl/src/main/kotlin/io/element/android/features/analytics/impl/AnalyticsOptInView.kt @@ -195,7 +195,7 @@ private fun AnalyticsOptInFooter( ) TextButton( text = stringResource(id = CommonStrings.action_not_now), - buttonSize = ButtonSize.Medium, + size = ButtonSize.Medium, onClick = onTermsDeclined, modifier = Modifier.fillMaxWidth(), ) diff --git a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt index a45c3c4808..c677fe158f 100644 --- a/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt +++ b/features/invitelist/impl/src/main/kotlin/io/element/android/features/invitelist/impl/components/InviteSummaryRow.kt @@ -135,7 +135,7 @@ internal fun DefaultInviteSummaryRow( text = stringResource(CommonStrings.action_decline), onClick = onDeclineClicked, modifier = Modifier.weight(1f), - buttonSize = ButtonSize.Medium, + size = ButtonSize.Medium, ) Spacer(modifier = Modifier.width(12.dp)) @@ -144,7 +144,7 @@ internal fun DefaultInviteSummaryRow( text = stringResource(CommonStrings.action_accept), onClick = onAcceptClicked, modifier = Modifier.weight(1f), - buttonSize = ButtonSize.Medium, + size = ButtonSize.Medium, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 116480479f..b007d59e36 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -35,7 +35,6 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SnackbarHost import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment @@ -78,6 +77,7 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.utils.LogCompositions +import io.element.android.libraries.designsystem.utils.SnackbarHost import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt index ff2e4d49f4..66f15225f7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/viewer/MediaViewerView.kt @@ -34,9 +34,6 @@ import androidx.compose.material.icons.filled.OpenInNew import androidx.compose.material.icons.filled.Share import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.LinearProgressIndicator -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Snackbar -import androidx.compose.material3.SnackbarHost import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -64,6 +61,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.TopAppBar +import io.element.android.libraries.designsystem.utils.SnackbarHost import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.ui.media.MediaRequestData @@ -99,15 +97,7 @@ fun MediaViewerView( eventSink = state.eventSink ) }, - snackbarHost = { - SnackbarHost(snackbarHostState) { data -> - Snackbar( - snackbarData = data, - containerColor = MaterialTheme.colorScheme.surfaceVariant, - contentColor = MaterialTheme.colorScheme.primary - ) - } - }, + snackbarHost = { SnackbarHost(snackbarHostState) }, ) { Column( modifier = Modifier diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt index ba8dc05f35..556d940664 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt @@ -24,8 +24,6 @@ import androidx.compose.material.icons.outlined.DeveloperMode import androidx.compose.material.icons.outlined.Help import androidx.compose.material.icons.outlined.InsertChart import androidx.compose.material.icons.outlined.VerifiedUser -import androidx.compose.material3.Snackbar -import androidx.compose.material3.SnackbarHost import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -41,6 +39,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.LargeHeightPreview import io.element.android.libraries.designsystem.theme.components.Divider import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.utils.SnackbarHost import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.MatrixUserProvider @@ -65,13 +64,7 @@ fun PreferencesRootView( modifier = modifier, onBackPressed = onBackPressed, title = stringResource(id = CommonStrings.common_settings), - snackbarHost = { - SnackbarHost(snackbarHostState) { data -> - Snackbar( - snackbarData = data, - ) - } - } + snackbarHost = { SnackbarHost(snackbarHostState) } ) { UserPreferences(state.myUser) if (state.showCompleteVerification) { diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt index 657648df42..02a72306dc 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt @@ -28,8 +28,6 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Snackbar -import androidx.compose.material3.SnackbarHost import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable @@ -59,6 +57,7 @@ import io.element.android.libraries.designsystem.theme.components.FloatingAction import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.utils.LogCompositions +import io.element.android.libraries.designsystem.utils.SnackbarHost import io.element.android.libraries.designsystem.utils.rememberSnackbarHostState import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.designsystem.R as DrawableR @@ -227,13 +226,7 @@ fun RoomListContent( ) } }, - snackbarHost = { - SnackbarHost(snackbarHostState) { data -> - Snackbar( - snackbarData = data, - ) - } - }, + snackbarHost = { SnackbarHost(snackbarHostState) }, ) } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt index 8920efdeff..9d6b54366a 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RequestVerificationHeader.kt @@ -83,7 +83,7 @@ internal fun RequestVerificationHeader( Spacer(modifier = Modifier.height(12.dp)) Button( text = stringResource(CommonStrings.action_continue), - buttonSize = ButtonSize.Medium, + size = ButtonSize.Medium, modifier = Modifier.fillMaxWidth(), onClick = onVerifyClicked, ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonVisuals.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonVisuals.kt new file mode 100644 index 0000000000..24f3989f66 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/ButtonVisuals.kt @@ -0,0 +1,53 @@ +/* + * 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.libraries.designsystem.components.button + +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import io.element.android.libraries.designsystem.theme.components.Button +import io.element.android.libraries.designsystem.theme.components.IconButton +import io.element.android.libraries.designsystem.theme.components.IconSource +import io.element.android.libraries.designsystem.theme.components.TextButton + +/** + * A sealed class that represents the different visual styles that a button can have. + */ +sealed interface ButtonVisuals { + + val action: () -> Unit + + /** + * Creates a [Button] composable based on the visual state. + */ + @Composable + fun Composable() + + data class Text(val text: String, override val action: () -> Unit) : ButtonVisuals { + @Composable + override fun Composable() { + TextButton(text = text, onClick = action) + } + } + data class Icon(val iconSource: IconSource, override val action: () -> Unit) : ButtonVisuals { + @Composable + override fun Composable() { + IconButton(onClick = action) { + Icon(iconSource.getPainter(), iconSource.contentDescription) + } + } + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewGroup.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewGroup.kt index aaff9a49f9..b704d0a26e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewGroup.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/preview/PreviewGroup.kt @@ -30,6 +30,7 @@ object PreviewGroup { const val Preferences = "Preferences" const val Progress = "Progress Indicators" const val Search = "Search views" + const val Snackbars = "Snackbars" const val Sliders = "Sliders" const val Text = "Text" const val TextFields = "TextFields" diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt index e413dff1f2..58c9a44709 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/ruler/WithRulers.kt @@ -78,7 +78,7 @@ private fun ContentToPreview() { WithRulers(xRulersOffset = 20.dp, yRulersOffset = 15.dp) { OutlinedButton( text = "A Button with rulers on it!", - buttonSize = ButtonSize.Medium, + size = ButtonSize.Medium, onClick = {}, ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt index 67a30e501d..a3c7274c45 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/AlertDialogContent.kt @@ -72,19 +72,19 @@ internal fun SimpleAlertDialogContent( // Having this 3rd action is discouraged, see https://m3.material.io/components/dialogs/guidelines#e13b68f5-e367-4275-ad6f-c552ee8e358f TextButton( text = thirdButtonText, - buttonSize = ButtonSize.Medium, + size = ButtonSize.Medium, onClick = onThirdButtonClicked, ) } TextButton( text = cancelText, - buttonSize = ButtonSize.Medium, + size = ButtonSize.Medium, onClick = onCancelClicked, ) if (submitText != null) { Button( text = submitText, - buttonSize = ButtonSize.Medium, + size = ButtonSize.Medium, onClick = onSubmitClicked, ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt index aa1fb6abd9..8c5d96c400 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Button.kt @@ -32,6 +32,7 @@ import androidx.compose.foundation.progressSemantics import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Share +import androidx.compose.material3.ButtonColors import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme @@ -41,6 +42,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.isSpecified import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.rememberVectorPainter @@ -60,10 +62,19 @@ fun Button( onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, - buttonSize: ButtonSize = ButtonSize.Large, + size: ButtonSize = ButtonSize.Large, showProgress: Boolean = false, leadingIcon: IconSource? = null, -) = ButtonInternal(text, onClick, ButtonStyle.Filled, modifier, enabled, buttonSize, showProgress, leadingIcon) +) = ButtonInternal( + text = text, + onClick = onClick, + style = ButtonStyle.Filled, + modifier = modifier, + enabled = enabled, + size = size, + showProgress = showProgress, + leadingIcon = leadingIcon +) @Composable fun OutlinedButton( @@ -71,10 +82,19 @@ fun OutlinedButton( onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, - buttonSize: ButtonSize = ButtonSize.Large, + size: ButtonSize = ButtonSize.Large, showProgress: Boolean = false, leadingIcon: IconSource? = null, -) = ButtonInternal(text, onClick, ButtonStyle.Outlined, modifier, enabled, buttonSize, showProgress, leadingIcon) +) = ButtonInternal( + text = text, + onClick = onClick, + style = ButtonStyle.Outlined, + modifier = modifier, + enabled = enabled, + size = size, + showProgress = showProgress, + leadingIcon = leadingIcon +) @Composable fun TextButton( @@ -82,17 +102,27 @@ fun TextButton( onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, - buttonSize: ButtonSize = ButtonSize.Large, + size: ButtonSize = ButtonSize.Large, showProgress: Boolean = false, leadingIcon: IconSource? = null, -) = ButtonInternal(text, onClick, ButtonStyle.Text, modifier, enabled, buttonSize, showProgress, leadingIcon) +) = ButtonInternal( + text = text, + onClick = onClick, + style = ButtonStyle.Text, + modifier = modifier, + enabled = enabled, + size = size, + showProgress = showProgress, + leadingIcon = leadingIcon +) @Composable -private fun ButtonInternal( +internal fun ButtonInternal( text: String, onClick: () -> Unit, style: ButtonStyle, modifier: Modifier = Modifier, + colors: ButtonColors = style.getColors(), enabled: Boolean = true, size: ButtonSize = ButtonSize.Large, showProgress: Boolean = false, @@ -123,21 +153,6 @@ private fun ButtonInternal( ButtonStyle.Text -> RectangleShape } - val colors = when (style) { - ButtonStyle.Filled -> ButtonDefaults.buttonColors( - containerColor = ElementTheme.materialColors.primary, - contentColor = ElementTheme.materialColors.onPrimary, - disabledContainerColor = ElementTheme.colors.bgActionPrimaryDisabled, - disabledContentColor = ElementTheme.colors.textOnSolidPrimary - ) - ButtonStyle.Outlined, ButtonStyle.Text -> ButtonDefaults.buttonColors( - containerColor = Color.Transparent, - contentColor = ElementTheme.materialColors.primary, - disabledContainerColor = Color.Transparent, - disabledContentColor = ElementTheme.colors.textDisabled, - ) - } - val border = when (style) { ButtonStyle.Filled, ButtonStyle.Text -> null ButtonStyle.Outlined -> BorderStroke( @@ -202,8 +217,10 @@ private fun ButtonInternal( } sealed interface IconSource { - data class Resource(val id: Int) : IconSource - data class Vector(val vector: ImageVector) : IconSource + val contentDescription: String? + + data class Resource(val id: Int, override val contentDescription: String? = null) : IconSource + data class Vector(val vector: ImageVector, override val contentDescription: String? = null) : IconSource @Composable fun getPainter(): Painter = when (this) { @@ -216,16 +233,38 @@ enum class ButtonSize { Medium, Large } -private enum class ButtonStyle { - Filled, Outlined, Text +internal enum class ButtonStyle { + Filled, Outlined, Text; + + @Composable + fun getColors(): ButtonColors = when (this) { + Filled -> ButtonDefaults.buttonColors( + containerColor = ElementTheme.materialColors.primary, + contentColor = ElementTheme.materialColors.onPrimary, + disabledContainerColor = ElementTheme.colors.bgActionPrimaryDisabled, + disabledContentColor = ElementTheme.colors.textOnSolidPrimary + ) + Outlined -> ButtonDefaults.buttonColors( + containerColor = Color.Transparent, + contentColor = ElementTheme.materialColors.primary, + disabledContainerColor = Color.Transparent, + disabledContentColor = ElementTheme.colors.textDisabled, + ) + Text -> ButtonDefaults.buttonColors( + containerColor = Color.Transparent, + contentColor = if (LocalContentColor.current.isSpecified) LocalContentColor.current else ElementTheme.materialColors.primary, + disabledContainerColor = Color.Transparent, + disabledContentColor = ElementTheme.colors.textDisabled, + ) + } } @Preview(group = PreviewGroup.Buttons) @Composable internal fun FilledButtonMediumPreview() { ButtonCombinationPreview( - buttonStyle = ButtonStyle.Filled, - buttonSize = ButtonSize.Medium, + style = ButtonStyle.Filled, + size = ButtonSize.Medium, ) } @@ -233,8 +272,8 @@ internal fun FilledButtonMediumPreview() { @Composable internal fun FilledButtonLargePreview() { ButtonCombinationPreview( - buttonStyle = ButtonStyle.Filled, - buttonSize = ButtonSize.Large, + style = ButtonStyle.Filled, + size = ButtonSize.Large, ) } @@ -242,8 +281,8 @@ internal fun FilledButtonLargePreview() { @Composable internal fun OutlinedButtonMediumPreview() { ButtonCombinationPreview( - buttonStyle = ButtonStyle.Outlined, - buttonSize = ButtonSize.Medium, + style = ButtonStyle.Outlined, + size = ButtonSize.Medium, ) } @@ -251,8 +290,8 @@ internal fun OutlinedButtonMediumPreview() { @Composable internal fun OutlinedButtonLargePreview() { ButtonCombinationPreview( - buttonStyle = ButtonStyle.Outlined, - buttonSize = ButtonSize.Large, + style = ButtonStyle.Outlined, + size = ButtonSize.Large, ) } @@ -260,8 +299,8 @@ internal fun OutlinedButtonLargePreview() { @Composable internal fun TextButtonMediumPreview() { ButtonCombinationPreview( - buttonStyle = ButtonStyle.Text, - buttonSize = ButtonSize.Medium, + style = ButtonStyle.Text, + size = ButtonSize.Medium, ) } @@ -269,15 +308,15 @@ internal fun TextButtonMediumPreview() { @Composable internal fun TextButtonLargePreview() { ButtonCombinationPreview( - buttonStyle = ButtonStyle.Text, - buttonSize = ButtonSize.Large, + style = ButtonStyle.Text, + size = ButtonSize.Large, ) } @Composable private fun ButtonCombinationPreview( - buttonStyle: ButtonStyle, - buttonSize: ButtonSize, + style: ButtonStyle, + size: ButtonSize, modifier: Modifier = Modifier, ) { ElementThemedPreview { @@ -290,24 +329,24 @@ private fun ButtonCombinationPreview( // Normal ButtonRowPreview( modifier = Modifier.then(modifier), - buttonStyle = buttonStyle, - buttonSize = buttonSize, + style = style, + size = size, ) // With icon ButtonRowPreview( modifier = Modifier.then(modifier), leadingIcon = IconSource.Vector(Icons.Outlined.Share), - buttonStyle = buttonStyle, - buttonSize = buttonSize, + style = style, + size = size, ) // With progress ButtonRowPreview( modifier = Modifier.then(modifier), showProgress = true, - buttonStyle = buttonStyle, - buttonSize = buttonSize, + style = style, + size = size, ) } } @@ -315,8 +354,8 @@ private fun ButtonCombinationPreview( @Composable private fun ButtonRowPreview( - buttonStyle: ButtonStyle, - buttonSize: ButtonSize, + style: ButtonStyle, + size: ButtonSize, modifier: Modifier = Modifier, leadingIcon: IconSource? = null, showProgress: Boolean = false, @@ -326,8 +365,8 @@ private fun ButtonRowPreview( text = "A button", showProgress = showProgress, onClick = {}, - style = buttonStyle, - size = buttonSize, + style = style, + size = size, leadingIcon = leadingIcon, modifier = Modifier.then(modifier), ) @@ -336,8 +375,8 @@ private fun ButtonRowPreview( showProgress = showProgress, enabled = false, onClick = {}, - style = buttonStyle, - size = buttonSize, + style = style, + size = size, leadingIcon = leadingIcon, modifier = Modifier.then(modifier), ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt index d3cd2fee07..d868f49965 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/MediumTopAppBar.kt @@ -18,15 +18,21 @@ package io.element.android.libraries.designsystem.theme.components import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Share import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.LocalContentColor import androidx.compose.material3.TopAppBarColors import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.theme.ElementTheme @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -43,7 +49,11 @@ fun MediumTopAppBar( title = title, modifier = modifier, navigationIcon = navigationIcon, - actions = actions, + actions = { + CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.textActionPrimary) { + actions() + } + }, windowInsets = windowInsets, colors = colors, scrollBehavior = scrollBehavior, @@ -58,5 +68,14 @@ internal fun MediumTopAppBarPreview() = @OptIn(ExperimentalMaterial3Api::class) @Composable private fun ContentToPreview() { - MediumTopAppBar(title = { Text(text = "Title") }) + MediumTopAppBar( + title = { Text(text = "Title") }, + navigationIcon = { BackButton(onClick = {}) }, + actions = { + TextButton(text = "Action", onClick = {}) + IconButton(onClick = {}) { + Icon(imageVector = Icons.Default.Share, contentDescription = null) + } + } + ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt new file mode 100644 index 0000000000..d2969fc3e9 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/Snackbar.kt @@ -0,0 +1,147 @@ +/* + * 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.libraries.designsystem.theme.components + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material3.SnackbarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.components.button.ButtonVisuals +import io.element.android.libraries.designsystem.preview.ElementThemedPreview +import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.theme.ElementTheme +import io.element.android.libraries.theme.SnackBarLabelColorDark +import io.element.android.libraries.theme.SnackBarLabelColorLight + +@Composable +fun Snackbar( + message: String, + modifier: Modifier = Modifier, + action: ButtonVisuals? = null, + dismissAction: ButtonVisuals? = null, + actionOnNewLine: Boolean = false, + shape: Shape = RoundedCornerShape(8.dp), + containerColor: Color = SnackbarDefaults.color, + contentColor: Color = ElementTheme.materialColors.inverseOnSurface, + actionContentColor: Color = actionContentColor(), + dismissActionContentColor: Color = SnackbarDefaults.dismissActionContentColor, +) { + Snackbar( + modifier = modifier, + action = action?.let { @Composable { it.Composable() } }, + dismissAction = dismissAction?.let { @Composable { it.Composable() } }, + actionOnNewLine = actionOnNewLine, + shape = shape, + containerColor = containerColor, + contentColor = contentColor, + actionContentColor = actionContentColor, + dismissActionContentColor = dismissActionContentColor, + content = { Text(text = message) }, + ) +} + +@Composable +fun Snackbar( + modifier: Modifier = Modifier, + action: @Composable (() -> Unit)? = null, + dismissAction: @Composable (() -> Unit)? = null, + actionOnNewLine: Boolean = false, + shape: Shape = RoundedCornerShape(8.dp), + containerColor: Color = SnackbarDefaults.color, + contentColor: Color = ElementTheme.materialColors.inverseOnSurface, + actionContentColor: Color = actionContentColor(), + dismissActionContentColor: Color = SnackbarDefaults.dismissActionContentColor, + content: @Composable () -> Unit +) { + androidx.compose.material3.Snackbar( + modifier = modifier, + action = action, + dismissAction = dismissAction, + actionOnNewLine = actionOnNewLine, + shape = shape, + containerColor = containerColor, + contentColor = contentColor, + actionContentColor = actionContentColor, + dismissActionContentColor = dismissActionContentColor, + content = content, + ) +} + +// TODO this color is temporary, an `inverse` version should be added to the semantic colors instead +@Composable +private fun actionContentColor(): Color { + return if (ElementTheme.isLightTheme) { + SnackBarLabelColorLight + } else { + SnackBarLabelColorDark + } +} + +@Preview(name = "Snackbar", group = PreviewGroup.Snackbars) +@Composable +internal fun SnackbarPreview() { + ElementThemedPreview { + Snackbar(message = "Snackbar supporting text") + } +} + +@Preview(name = "Snackbar with action", group = PreviewGroup.Snackbars) +@Composable +internal fun SnackbarWithActionPreview() { + ElementThemedPreview { + Snackbar(message = "Snackbar supporting text", action = ButtonVisuals.Text("Action", {})) + } +} + +@Preview(name = "Snackbar with action and close button", group = PreviewGroup.Snackbars) +@Composable +internal fun SnackbarWithActionAndCloseButtonPreview() { + ElementThemedPreview { + Snackbar( + message = "Snackbar supporting text", + action = ButtonVisuals.Text("Action", {}), + dismissAction = ButtonVisuals.Icon(IconSource.Vector(Icons.Default.Close), {}) + ) + } +} + +@Preview(name = "Snackbar with action on new line", group = PreviewGroup.Snackbars) +@Composable +internal fun SnackbarWithActionOnNewLinePreview() { + ElementThemedPreview { + Snackbar(message = "Snackbar supporting text", action = ButtonVisuals.Text("Action", {}), actionOnNewLine = true) + } +} + +@Preview(name = "Snackbar with action and close button on new line", group = PreviewGroup.Snackbars) +@Composable +internal fun SnackbarWithActionOnNewLineAndCloseButtonPreview() { + ElementThemedPreview { + Snackbar( + message = "Snackbar supporting text", + action = ButtonVisuals.Text("Action", {}), + dismissAction = ButtonVisuals.Icon(IconSource.Vector(Icons.Default.Close), {}), + actionOnNewLine = true + ) + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt index 23848ef76d..93d11e8c9a 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TopAppBar.kt @@ -18,15 +18,21 @@ package io.element.android.libraries.designsystem.theme.components import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Share import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.LocalContentColor import androidx.compose.material3.TopAppBarColors import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.designsystem.preview.PreviewGroup +import io.element.android.libraries.theme.ElementTheme @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -43,7 +49,11 @@ fun TopAppBar( title = title, modifier = modifier, navigationIcon = navigationIcon, - actions = actions, + actions = { + CompositionLocalProvider(LocalContentColor provides ElementTheme.colors.textActionPrimary) { + actions() + } + }, windowInsets = windowInsets, colors = colors, scrollBehavior = scrollBehavior, @@ -58,5 +68,14 @@ internal fun TopAppBarPreview() = @OptIn(ExperimentalMaterial3Api::class) @Composable private fun ContentToPreview() { - TopAppBar(title = { Text(text = "Title") }) + TopAppBar( + title = { Text(text = "Title") }, + navigationIcon = { BackButton(onClick = {}) }, + actions = { + TextButton(text = "Action", onClick = {}) + IconButton(onClick = {}) { + Icon(imageVector = Icons.Default.Share, contentDescription = null) + } + } + ) } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/Snackbar.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/Snackbar.kt index f4e44779d1..4513a90914 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/Snackbar.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/Snackbar.kt @@ -17,6 +17,8 @@ package io.element.android.libraries.designsystem.utils import androidx.annotation.StringRes +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable @@ -25,7 +27,11 @@ import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import io.element.android.libraries.designsystem.components.button.ButtonVisuals +import io.element.android.libraries.designsystem.theme.components.IconSource +import io.element.android.libraries.designsystem.theme.components.Snackbar import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -65,6 +71,19 @@ fun SnackbarDispatcher.collectSnackbarMessageAsState(): State return snackbarMessage.collectAsState(initial = null) } +@Composable +fun SnackbarHost(hostState: SnackbarHostState, modifier: Modifier = Modifier) { + androidx.compose.material3.SnackbarHost(hostState, modifier) { data -> + Snackbar( + message = data.visuals.message, + action = data.visuals.actionLabel?.let { ButtonVisuals.Text(it, data::performAction) }, + dismissAction = if (data.visuals.withDismissAction) { + ButtonVisuals.Icon(IconSource.Vector(Icons.Default.Close), data::dismiss) + } else null, + ) + } +} + @Composable fun rememberSnackbarHostState(snackbarMessage: SnackbarMessage?): SnackbarHostState { val snackbarHostState = remember { SnackbarHostState() } diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/LegacyColors.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/LegacyColors.kt index b797dab86a..2e705c8c79 100644 --- a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/LegacyColors.kt +++ b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/LegacyColors.kt @@ -17,6 +17,8 @@ package io.element.android.libraries.theme import androidx.compose.ui.graphics.Color +import io.element.android.libraries.theme.compound.generated.internal.DarkDesignTokens +import io.element.android.libraries.theme.compound.generated.internal.LightDesignTokens // ================================================================================================= // IMPORTANT! @@ -26,3 +28,6 @@ import androidx.compose.ui.graphics.Color // ================================================================================================= val LinkColor = Color(0xFF0086E6) + +val SnackBarLabelColorLight = LightDesignTokens.colorGray700 +val SnackBarLabelColorDark = DarkDesignTokens.colorGray700 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 5eb7294406..8bcbbee378 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:364f107ffaa4844d0141361642ce3a494a187588f27be50b5fd27d44be21fa64 -size 8887 +oid sha256:016ca2c634b467ba6d98ed221506d5bbb250b58a5ea631a0076a572f745cfa84 +size 8710 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index f317c61b6e..50577b4cbf 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d0a5de3c4e09d76b6453ccc6ace5c690d540d945a62e630474932734209058a -size 11716 +oid sha256:c129a4d13320fb3fec6039e37c802de33d7dd607faa3890f9f1b34f663eecc46 +size 11552 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 34c2f19861..6ace5373a3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9218d1d514342c400051bebb10ef458c99103fda618bba45e61a524c5a58eb63 -size 11903 +oid sha256:abe3498282504f7b0bfa3a2713ea556b7d4d361d5173ea56bcdadb37766d0b93 +size 11742 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 471e59a5b0..209d16749b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f19878925f3b5b377a91885540fb15d29a5b78d8be2282d64e8809af0bbf5ff4 -size 12195 +oid sha256:d52d7c6d6271577c80ad1ca170aeb9686d8bb572459212d6ef1fe97c490b382d +size 12035 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 6c81af0a3c..1e5b0e65de 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d98aabf3bd99793367632e18d5cf678e75fb5ad872a1cb300a7e939ad0c2683 -size 19725 +oid sha256:3e6442ca746462345d0573abfb72028a613b08d53f6a5e3f0ef3c5f09beab423 +size 19491 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index b56214c49f..6c5d1f2553 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c936d2d804bc9e98fcc49430f11ddaa572b05fc8d3a0df93ad6521ee8e78f708 -size 21806 +oid sha256:906b39149036cccadd5232c0373a6a94e039b374988ace3d732a5b63b7d81829 +size 21590 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index ea8bf3f1bd..d25793cf30 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac01bc1992e3fa27950c7071cd3e8a06b94a608238d55972816fa2a1a3175e7c -size 9448 +oid sha256:545fcfe023789a167a5ee2fe11e7f65426890d794fa08ed32ea4e9fe0fe5f59a +size 9387 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 342978d4f7..bf88ad0eee 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e39c5ba30983b034886a6adc330d1510b3a48c40511697edcaf6530716c7ba2e -size 12500 +oid sha256:b7ebe27e257108881e4869213ea754926fceb8a047ccb81120fc1a1df19279f4 +size 12444 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index c1fa61a838..2332b90a7e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4ff75e74c19280308878e3001a8791aaa735439cc667946fefd21070611630e -size 12698 +oid sha256:ecb0ec0f6b9bffc5cd0ab19f85beb962610d5624652b8e6f21d8aedda69a0fb4 +size 12645 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index 8ecfebb614..05ba2a726c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:065d2a09680e35540b870862ec0ba5c54182e016017938ef64e510b6132333c9 -size 13389 +oid sha256:989b630c0925f0a822b2b7460648e874357e1ecf0ab75caea56c78189d936349 +size 13329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index ad38501385..61448871c3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94be795b626868e8afbcc26c3f0161ddcb946a122c02eeed7e823825c9aeba19 -size 22263 +oid sha256:caa94ed925e37306cc9453a5ade0266a897dc010cdde86b92a9e1d4710039edc +size 22184 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index 7701a371c3..c405ee9fea 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.location.impl.show_null_DefaultGroup_ShowLocationViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0bb88c64bc68b10b1fb709135f445de5a5e4d78623448d0fef97504e025d5f6d -size 24551 +oid sha256:6d9458392de2e0f291f752a8cd9558c9eb891d4140e07c5c61a42a16a3c77838 +size 24507 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index d3dc127f68..64c7da001e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d40dd7970069fb798b738a1775b5843522cc28a581d904f44974d7ceefdbf3c9 -size 148087 +oid sha256:cafa0bf978a59ff5903429bcb605afd2cb347bb64c3d58e749b0dd7ddfca6199 +size 148827 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 8227daecf3..dd99c63f05 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:47f20f3dc4e4bdbb6641f26b6e03d0dba0f2c7e0fe22fce927870d14f1472066 -size 148796 +oid sha256:a19c6af3352ad430c6502ccc9dac1ae8b4b8d690c810ab424c61208fa31e52b0 +size 149411 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index bf66c83a75..8a7a211694 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1016b14f6d975828470aa8d6786177d6dde909cc727f39b2c66742a101735e8f -size 66240 +oid sha256:852b7b5515cad1bb129273ba95fc528f9cc67bf4dde0995276a96521c4399cbe +size 66851 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index d3dc127f68..64c7da001e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.screens.waitlistscreen_null_DefaultGroup_WaitListViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d40dd7970069fb798b738a1775b5843522cc28a581d904f44974d7ceefdbf3c9 -size 148087 +oid sha256:cafa0bf978a59ff5903429bcb605afd2cb347bb64c3d58e749b0dd7ddfca6199 +size 148827 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 3d12f9a3f6..e2663fb42c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:77983bf8dfa0683d472683cdb7ad545beb6b05fdfb8b82ecf90db49d387db72e -size 395299 +oid sha256:e4e9e0b3c5be9a07af8cd9a4f4ae725505148805327455350d9504a5974032c2 +size 395377 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 1b77de0157..d16aed691d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a28b7439185193fca0f389d36f287fa38d29d18bfc046a125f936026ffd1918 -size 6318 +oid sha256:9225a19843eb5c9e1a78a7bd307958c2cad544c58ca2637224455326dfcfffb6 +size 6315 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png index 2fff84f3a3..fcfe80f7b0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:459ff294007ea6238f87954e04e8e13614b7c5fa7ade01f44670014013ebfead -size 15382 +oid sha256:628072ca45ae0d215ba9bbe965b68442153d81ad98f330ab1178a0f8fcff3e24 +size 15339 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png index 48767dad09..d3a5dd72ac 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2824676afbff473eff8c8cfbcf8b3e58b2851e37324c6a8d9ba47d626b0f8bc -size 14234 +oid sha256:93ca4c1155010d01315b00bb79d9807cf0e6f80a609be668cb5600c16e82ef4d +size 14190 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 8952186a4e..f916d90928 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8850f5c0e52e9441f92010bebe8828cdf5e3715250aadb68f31d996ec1fb7520 -size 38042 +oid sha256:b7a8ca9eb62e3988fd6adc3d3992bb6a8577b91b44d05a6c45a4af48b3bdbb4f +size 38282 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_0,NEXUS_5,1.0,en].png index 864c9083a3..3c3271d44f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.members_null_DefaultGroup_RoomMemberListLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8338dfb26935d63918749a3dd0c3d06d752172aba69bc67969b4928667fa8da1 -size 39183 +oid sha256:29c635e2bfb59cea20c8ee2c4c008a2edc74127313c437d63f7e4ef4f0851921 +size 39425 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png index 0c4c60a45c..02b9bfb526 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c90d81b34d6876b7d4406adce0a775dc67b48da91eac6b4a814eaae9a7b028c2 -size 54262 +oid sha256:8242b5826755b17c8a1bd919bc87358b701fc8431ba107c7624c69d2d6a61085 +size 54268 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png index 494bf134d4..57d3d81732 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0e1999174751655dd87b042f1ea22f281cbf9b5deecc494f05307f125d6e9dac -size 56358 +oid sha256:05588cb33208fb210e7953509edc739cf9401f713132a7b44a9883615a4423c0 +size 56365 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_MediumTopAppBarPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_MediumTopAppBarPreview_0_null,NEXUS_5,1.0,en].png index 3c6579753b..fee0b4bc86 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_MediumTopAppBarPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_MediumTopAppBarPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c7f0987b11f1ecc5e8359b668ac8cf29f0d5b6ef6f5c74d3660e68696be699ae -size 7219 +oid sha256:c591956dbbc4265381b6bff537efe21bdb70f3ab14862b63976ff2038b193dc6 +size 12176 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_TopAppBarPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_TopAppBarPreview_0_null,NEXUS_5,1.0,en].png index a6ece5f74f..4d3cfde8d2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_TopAppBarPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_AppBars_TopAppBarPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f470f47f972d0c948a308622e49855bb88365675d8b251cd1cce9972f5569c9 -size 6878 +oid sha256:b47ed55d8919fd15da1e3877df6fa32a58bd51ebd676a7ff303e09ddb1d2d7fd +size 11604 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbar_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbar_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a686a3827e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbar_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:313860648fc6bf47ec98182ff018c54df61703fd8c1e302811a1e3a79a35b72a +size 15933 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithaction_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithaction_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c112d357b8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithaction_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92ffb912aeeea5d161bd8c94ce7ac865eee13de50a7bc15217bfd0955e5a9b32 +size 18516 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fd4c9b3d18 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebutton_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:55e08f85f71dc07addee6079425158831ed50209f4a43fad9a8d0252d55d0f4a +size 19486 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..84918c94b2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactionandclosebuttononnewline_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de21230c271bc78f767f130e07403d778ac65d347cbb4ba564d883c679a283ad +size 20084 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactiononnewline_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactiononnewline_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b2689c1029 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_Snackbars_Snackbarwithactiononnewline_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:62b5669a3f1a1138f5b982d27a1d4b2f0a2f79e862a4e50eb238c1d09de3d390 +size 18833 From 218af83b54c8c51dcb55f6cbc0d18feb92734633 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 16 Aug 2023 12:53:50 +0000 Subject: [PATCH 247/251] Update media3 to v1.1.1 (#1063) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e7a352e2e2..ca23a17afd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,7 +18,7 @@ recyclerview = "1.3.1" lifecycle = "2.6.1" activity = "1.7.2" startup = "1.1.1" -media3 = "1.1.0" +media3 = "1.1.1" browser = "1.5.0" # Compose From f2d1658ffab688cdb42055e552e80f80285de314 Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Wed, 16 Aug 2023 16:19:12 +0200 Subject: [PATCH 248/251] "View only" polls in the timeline (#1031) * Handle poll events from the sdk * Render started poll event in the timeline * Create poll module * Check poll kind before revealing the results * Check if user has voted before revealing the results * Add active poll previews * Minor cleanup * Update todos * Fix CI * Remove hardcoded string * Update preview * changelog file * Update screenshots * Use CommonPlurals * Set poll root view as selectableGroup * Improve poll result rendering * Update screenshots * Add missing showkase processor * Update screenshots --------- Co-authored-by: ElementBot --- changelog.d/1031.wip | 1 + features/messages/impl/build.gradle.kts | 1 + .../messages/impl/MessagesPresenter.kt | 2 + .../impl/actionlist/ActionListView.kt | 2 + .../event/TimelineItemContentView.kt | 6 + .../components/event/TimelineItemPollView.kt | 53 ++++++++ .../event/TimelineItemContentFactory.kt | 6 + .../TimelineItemContentPollEndFactory.kt | 29 ++++ .../event/TimelineItemContentPollFactory.kt | 56 ++++++++ .../impl/timeline/groups/Groupability.kt | 6 + .../model/event/TimelineItemEventContent.kt | 1 + .../model/event/TimelineItemPollContent.kt | 31 +++++ .../event/TimelineItemPollContentProvider.kt | 39 ++++++ .../MessageSummaryFormatterImpl.kt | 2 + .../messages/fixtures/timelineItemsFactory.kt | 7 +- features/poll/api/build.gradle.kts | 35 +++++ .../poll/api/ActivePollContentView.kt | 118 +++++++++++++++++ .../features/poll/api/PollAnswerItem.kt | 36 +++++ .../features/poll/api/PollAnswerView.kt | 125 ++++++++++++++++++ .../poll/api/PollAnswerViewProvider.kt | 60 +++++++++ .../features/poll/api/PollEntryPoint.kt | 37 ++++++ features/poll/impl/build.gradle.kts | 52 ++++++++ .../poll/impl/DefaultPollEntryPoint.kt | 46 +++++++ .../features/poll/impl/PollFlowNode.kt | 70 ++++++++++ .../components/LinearProgressIndicator.kt | 90 +++++++++++++ .../impl/DefaultRoomLastMessageFormatter.kt | 3 + .../impl/DefaultTimelineEventFormatter.kt | 4 + .../libraries/matrix/api/poll/PollAnswer.kt | 22 +++ .../libraries/matrix/api/poll/PollKind.kt | 24 ++++ .../api/timeline/item/event/EventContent.kt | 15 +++ .../libraries/matrix/impl/poll/PollAnswer.kt | 25 ++++ .../libraries/matrix/impl/poll/PollKind.kt | 25 ++++ .../item/event/TimelineEventContentMapper.kt | 18 +++ ...Preview-D-13_14_null_0,NEXUS_5,1.0,en].png | 3 + ...Preview-D-13_14_null_1,NEXUS_5,1.0,en].png | 3 + ...Preview-N-13_15_null_0,NEXUS_5,1.0,en].png | 3 + ...Preview-N-13_15_null_1,NEXUS_5,1.0,en].png | 3 + ...review-D-14_15_null_0,NEXUS_5,1.0,en].png} | 0 ...review-N-14_16_null_0,NEXUS_5,1.0,en].png} | 0 ...wPreview-D-15_16_null,NEXUS_5,1.0,en].png} | 0 ...wPreview-N-15_17_null,NEXUS_5,1.0,en].png} | 0 ...ultsPreview-D-0_0_null,NEXUS_5,1.0,en].png | 3 + ...ultsPreview-N-0_1_null,NEXUS_5,1.0,en].png | 3 + ...ultsPreview-D-1_1_null,NEXUS_5,1.0,en].png | 3 + ...ultsPreview-N-1_2_null,NEXUS_5,1.0,en].png | 3 + ...ultsPreview-D-2_2_null,NEXUS_5,1.0,en].png | 3 + ...ultsPreview-N-2_3_null,NEXUS_5,1.0,en].png | 3 + ...sultPreview-D-3_3_null,NEXUS_5,1.0,en].png | 3 + ...sultPreview-N-3_4_null,NEXUS_5,1.0,en].png | 3 + ...ndicatorPreview_0_null,NEXUS_5,1.0,en].png | 3 + 50 files changed, 1085 insertions(+), 1 deletion(-) create mode 100644 changelog.d/1031.wip create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollEndFactory.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollFactory.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContent.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt create mode 100644 features/poll/api/build.gradle.kts create mode 100644 features/poll/api/src/main/kotlin/io/element/android/features/poll/api/ActivePollContentView.kt create mode 100644 features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerItem.kt create mode 100644 features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt create mode 100644 features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt create mode 100644 features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollEntryPoint.kt create mode 100644 features/poll/impl/build.gradle.kts create mode 100644 features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/DefaultPollEntryPoint.kt create mode 100644 features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/PollFlowNode.kt create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/LinearProgressIndicator.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollAnswer.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollKind.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/poll/PollAnswer.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/poll/PollKind.kt create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-D-13_14_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-D-13_14_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-N-13_15_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-N-13_15_null_1,NEXUS_5,1.0,en].png rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-13_14_null_0,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-14_15_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-13_15_null_0,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-14_16_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-14_15_null,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-15_16_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-14_16_null,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-15_17_null,NEXUS_5,1.0,en].png} (100%) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentNoResultsPreview-D-0_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentNoResultsPreview-N-0_1_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentWithResultsPreview-D-1_1_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentWithResultsPreview-N-1_2_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewNoResultsPreview-D-2_2_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewNoResultsPreview-N-2_3_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewWithResultPreview-D-3_3_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewWithResultPreview-N-3_4_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_ProgressIndicators_LinearProgressIndicatorPreview_0_null,NEXUS_5,1.0,en].png diff --git a/changelog.d/1031.wip b/changelog.d/1031.wip new file mode 100644 index 0000000000..9816f1af8a --- /dev/null +++ b/changelog.d/1031.wip @@ -0,0 +1 @@ +[Poll] Render start event in the timeline diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index 948bac4c57..4746cff1de 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -34,6 +34,7 @@ dependencies { anvil(projects.anvilcodegen) api(projects.features.messages.api) implementation(projects.features.location.api) + implementation(projects.features.poll.api) implementation(projects.libraries.androidutils) implementation(projects.libraries.core) implementation(projects.libraries.architecture) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index 811e087b02..8a374471e3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -47,6 +47,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt 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.TimelineItemLocationContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent @@ -277,6 +278,7 @@ class MessagesPresenter @AssistedInject constructor( is TimelineItemLocationContent -> AttachmentThumbnailInfo( type = AttachmentThumbnailType.Location, ) + is TimelineItemPollContent, // TODO Polls: handle reply to is TimelineItemTextBasedContent, is TimelineItemRedactedContent, is TimelineItemStateContent, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt index b642e8e5aa..4838d2fdbf 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt @@ -62,6 +62,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt 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.TimelineItemLocationContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent @@ -236,6 +237,7 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif val textContent = remember(event.content) { formatter.format(event) } when (event.content) { + is TimelineItemPollContent, // TODO Polls: handle summary is TimelineItemTextBasedContent, is TimelineItemStateContent, is TimelineItemEncryptedContent, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt index 3df45eb760..d53e3f1e5b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt @@ -25,6 +25,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt 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.TimelineItemLocationContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent @@ -90,5 +91,10 @@ fun TimelineItemEventContentView( content = content, modifier = modifier ) + is TimelineItemPollContent -> TimelineItemPollView( + content = content, + onAnswerSelected = {}, + modifier = modifier, + ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt new file mode 100644 index 0000000000..db3503be37 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemPollView.kt @@ -0,0 +1,53 @@ +/* + * 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.event + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.PreviewParameter +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContentProvider +import io.element.android.features.poll.api.ActivePollContentView +import io.element.android.libraries.designsystem.preview.DayNightPreviews +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.matrix.api.poll.PollAnswer +import kotlinx.collections.immutable.toImmutableList + +@Composable +fun TimelineItemPollView( + content: TimelineItemPollContent, + onAnswerSelected: (PollAnswer) -> Unit, + modifier: Modifier = Modifier, +) { + ActivePollContentView( + question = content.question, + answerItems = content.answerItems.toImmutableList(), + pollKind = content.pollKind, + onAnswerSelected = onAnswerSelected, + modifier = modifier, + ) +} + +@DayNightPreviews +@Composable +internal fun TimelineItemPollViewPreview(@PreviewParameter(TimelineItemPollContentProvider::class) content: TimelineItemPollContent) = + ElementPreview { + TimelineItemPollView( + content = content, + onAnswerSelected = {}, + ) + } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt index eb6d0e45c0..fad1ccc822 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentFactory.kt @@ -22,6 +22,8 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventTimeline import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import io.element.android.libraries.matrix.api.timeline.item.event.PollEndContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent @@ -35,6 +37,8 @@ class TimelineItemContentFactory @Inject constructor( private val messageFactory: TimelineItemContentMessageFactory, private val redactedMessageFactory: TimelineItemContentRedactedFactory, private val stickerFactory: TimelineItemContentStickerFactory, + private val pollFactory: TimelineItemContentPollFactory, + private val pollEndFactory: TimelineItemContentPollEndFactory, private val utdFactory: TimelineItemContentUTDFactory, private val roomMembershipFactory: TimelineItemContentRoomMembershipFactory, private val profileChangeFactory: TimelineItemContentProfileChangeFactory, @@ -53,6 +57,8 @@ class TimelineItemContentFactory @Inject constructor( is RoomMembershipContent -> roomMembershipFactory.create(eventTimelineItem) is StateContent -> stateFactory.create(eventTimelineItem) is StickerContent -> stickerFactory.create(itemContent) + is PollContent -> pollFactory.create(itemContent) + is PollEndContent -> pollEndFactory.create(itemContent) is UnableToDecryptContent -> utdFactory.create(itemContent) is UnknownContent -> TimelineItemUnknownContent } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollEndFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollEndFactory.kt new file mode 100644 index 0000000000..ff9eb837b6 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollEndFactory.kt @@ -0,0 +1,29 @@ +/* + * 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.factories.event + +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent +import io.element.android.libraries.matrix.api.timeline.item.event.PollEndContent +import javax.inject.Inject + +class TimelineItemContentPollEndFactory @Inject constructor() { + + fun create(@Suppress("UNUSED_PARAMETER") content: PollEndContent): TimelineItemEventContent { + return TimelineItemUnknownContent + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollFactory.kt new file mode 100644 index 0000000000..2ab69f10e4 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentPollFactory.kt @@ -0,0 +1,56 @@ +/* + * 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.factories.event + +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent +import io.element.android.features.poll.api.PollAnswerItem +import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.poll.PollKind +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import javax.inject.Inject + +class TimelineItemContentPollFactory @Inject constructor( + private val matrixClient: MatrixClient, +) { + + fun create(content: PollContent): TimelineItemEventContent { + // Todo Move this computation to the matrix rust sdk + val showResults = content.kind == PollKind.Disclosed && matrixClient.sessionId in content.votes.flatMap { it.value } + val pollVotesCount = content.votes.flatMap { it.value }.size + val userVotes = content.votes.filter { matrixClient.sessionId in it.value }.keys + val answerItems = content.answers.map { answer -> + val votesCount = content.votes[answer.id]?.size ?: 0 + val progress = if (pollVotesCount > 0) votesCount.toFloat() / pollVotesCount.toFloat() else 0f + PollAnswerItem( + answer = answer, + isSelected = answer.id in userVotes, + isDisclosed = showResults, + votesCount = votesCount, + progress = progress, + ) + } + + return TimelineItemPollContent( + question = content.question, + answerItems = answerItems, + votes = content.votes, + pollKind = content.kind, + isDisclosed = showResults + ) + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt index 0b8baf692a..1d2dec09b7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt @@ -22,6 +22,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt 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.TimelineItemLocationContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemProfileChangeContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRoomMembershipContent @@ -33,6 +34,8 @@ import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import io.element.android.libraries.matrix.api.timeline.item.event.PollEndContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent @@ -55,6 +58,7 @@ internal fun TimelineItem.Event.canBeGrouped(): Boolean { is TimelineItemVideoContent, is TimelineItemAudioContent, is TimelineItemLocationContent, + is TimelineItemPollContent, TimelineItemRedactedContent, TimelineItemUnknownContent -> false is TimelineItemProfileChangeContent, @@ -74,6 +78,8 @@ internal fun MatrixTimelineItem.Event.canBeDisplayedInBubbleBlock(): Boolean { is MessageContent, RedactedContent, is StickerContent, + is PollContent, + is PollEndContent, is UnableToDecryptContent -> true is FailedToParseStateContent, is ProfileChangeContent, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt index e0de57c5a5..02837bd6b4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt @@ -45,6 +45,7 @@ fun TimelineItemEventContent.canReact(): Boolean = is TimelineItemFileContent, is TimelineItemImageContent, is TimelineItemLocationContent, + is TimelineItemPollContent, is TimelineItemVideoContent -> true is TimelineItemStateContent, is TimelineItemRedactedContent, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContent.kt new file mode 100644 index 0000000000..b8a2fa8bca --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContent.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 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.model.event + +import io.element.android.features.poll.api.PollAnswerItem +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.poll.PollKind + +data class TimelineItemPollContent( + val question: String, + val answerItems: List, + val votes: Map>, + val pollKind: PollKind, + val isDisclosed: Boolean, +) : TimelineItemEventContent { + override val type: String = "TimelineItemPollContent" +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt new file mode 100644 index 0000000000..665d507ead --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemPollContentProvider.kt @@ -0,0 +1,39 @@ +/* + * 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.model.event + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.poll.api.aPollAnswerItemList +import io.element.android.libraries.matrix.api.poll.PollKind + +open class TimelineItemPollContentProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aTimelineItemPollContent(), + aTimelineItemPollContent().copy(isDisclosed = true), + ) +} + +fun aTimelineItemPollContent(): TimelineItemPollContent { + return TimelineItemPollContent( + pollKind = PollKind.Disclosed, + isDisclosed = false, + question = "What type of food should we have at the party?", + answerItems = aPollAnswerItemList(), + votes = emptyMap(), + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt index 42c50bbd9d..2b35eeda37 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt @@ -24,6 +24,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt 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.TimelineItemLocationContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemProfileChangeContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent @@ -47,6 +48,7 @@ class MessageSummaryFormatterImpl @Inject constructor( is TimelineItemLocationContent -> context.getString(CommonStrings.common_shared_location) is TimelineItemEncryptedContent -> context.getString(CommonStrings.common_unable_to_decrypt) is TimelineItemRedactedContent -> context.getString(CommonStrings.common_message_removed) + is TimelineItemPollContent, // Todo Polls: handle summary is TimelineItemUnknownContent -> context.getString(CommonStrings.common_unsupported_event) is TimelineItemImageContent -> context.getString(CommonStrings.common_image) is TimelineItemVideoContent -> context.getString(CommonStrings.common_video) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt index 638c5e0556..3b1a38e16f 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt @@ -21,6 +21,8 @@ import io.element.android.features.messages.impl.timeline.factories.event.Timeli import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentFailedToParseMessageFactory import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentFailedToParseStateFactory import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentMessageFactory +import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentPollEndFactory +import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentPollFactory import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentProfileChangeFactory import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentRedactedFactory import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemContentRoomMembershipFactory @@ -42,6 +44,7 @@ import kotlinx.coroutines.test.TestScope internal fun TestScope.aTimelineItemsFactory(): TimelineItemsFactory { val timelineEventFormatter = aTimelineEventFormatter() + val matrixClient = FakeMatrixClient() return TimelineItemsFactory( dispatchers = testCoroutineDispatchers(), eventItemFactory = TimelineItemEventFactory( @@ -49,6 +52,8 @@ internal fun TestScope.aTimelineItemsFactory(): TimelineItemsFactory { messageFactory = TimelineItemContentMessageFactory(FakeFileSizeFormatter(), FileExtensionExtractorWithoutValidation()), redactedMessageFactory = TimelineItemContentRedactedFactory(), stickerFactory = TimelineItemContentStickerFactory(), + pollFactory = TimelineItemContentPollFactory(matrixClient), + pollEndFactory = TimelineItemContentPollEndFactory(), utdFactory = TimelineItemContentUTDFactory(), roomMembershipFactory = TimelineItemContentRoomMembershipFactory(timelineEventFormatter), profileChangeFactory = TimelineItemContentProfileChangeFactory(timelineEventFormatter), @@ -56,7 +61,7 @@ internal fun TestScope.aTimelineItemsFactory(): TimelineItemsFactory { failedToParseMessageFactory = TimelineItemContentFailedToParseMessageFactory(), failedToParseStateFactory = TimelineItemContentFailedToParseStateFactory() ), - matrixClient = FakeMatrixClient(), + matrixClient = matrixClient, ), virtualItemFactory = TimelineItemVirtualFactory( daySeparatorFactory = TimelineItemDaySeparatorFactory( diff --git a/features/poll/api/build.gradle.kts b/features/poll/api/build.gradle.kts new file mode 100644 index 0000000000..be198ba740 --- /dev/null +++ b/features/poll/api/build.gradle.kts @@ -0,0 +1,35 @@ +/* + * 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. + */ + +plugins { + id("io.element.android-compose-library") + alias(libs.plugins.ksp) +} + +android { + namespace = "io.element.android.features.poll.api" +} + +dependencies { + implementation(projects.libraries.architecture) + implementation(projects.libraries.designsystem) + implementation(projects.libraries.uiStrings) + implementation(libs.androidx.constraintlayout) + implementation(libs.androidx.constraintlayout.compose) + implementation(projects.libraries.matrix.api) + + ksp(libs.showkase.processor) +} diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/ActivePollContentView.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/ActivePollContentView.kt new file mode 100644 index 0000000000..587c3306b1 --- /dev/null +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/ActivePollContentView.kt @@ -0,0 +1,118 @@ +/* + * 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.poll.api + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.BarChart +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.preview.DayNightPreviews +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.matrix.api.poll.PollAnswer +import io.element.android.libraries.matrix.api.poll.PollKind +import io.element.android.libraries.theme.ElementTheme +import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.ImmutableList + +@Composable +fun ActivePollContentView( + question: String, + answerItems: ImmutableList, + pollKind: PollKind, + onAnswerSelected: (PollAnswer) -> Unit, + modifier: Modifier = Modifier, +) { + val showResults = answerItems.any { it.isSelected } + Column( + modifier = modifier + .selectableGroup() + .fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Icon(imageVector = Icons.Default.BarChart, contentDescription = null) + Text( + text = question, + style = ElementTheme.typography.fontBodyLgMedium + ) + } + + answerItems.forEach { answerItem -> + PollAnswerView( + answerItem = answerItem, + onClick = { onAnswerSelected(answerItem.answer) } + ) + } + + val votesCount = answerItems.sumOf { it.votesCount } + when { + pollKind == PollKind.Undisclosed -> { + Text( + modifier = Modifier + .align(Alignment.Start) + .padding(start = 32.dp), + style = ElementTheme.typography.fontBodyXsRegular, + color = ElementTheme.colors.textSecondary, + text = stringResource(CommonStrings.common_poll_undisclosed_text), + ) + } + showResults -> { + Text( + modifier = Modifier.align(Alignment.End), + style = ElementTheme.typography.fontBodyXsRegular, + color = ElementTheme.colors.textSecondary, + text = stringResource(CommonStrings.common_poll_total_votes, votesCount), + ) + } + } + } +} + +@DayNightPreviews +@Composable +internal fun ActivePollContentNoResultsPreview() = ElementPreview { + ActivePollContentView( + question = "What type of food should we have at the party?", + answerItems = aPollAnswerItemList(isDisclosed = false), + pollKind = PollKind.Undisclosed, + onAnswerSelected = { }, + ) +} + +@DayNightPreviews +@Composable +internal fun ActivePollContentWithResultsPreview() = ElementPreview { + ActivePollContentView( + question = "What type of food should we have at the party?", + answerItems = aPollAnswerItemList(), + pollKind = PollKind.Disclosed, + onAnswerSelected = { }, + ) +} diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerItem.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerItem.kt new file mode 100644 index 0000000000..24db33ad1f --- /dev/null +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerItem.kt @@ -0,0 +1,36 @@ +/* + * 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.poll.api + +import io.element.android.libraries.matrix.api.poll.PollAnswer + +/** + * UI model for a [PollAnswer]. + * + * @property answer the poll answer. + * @property isSelected whether the user has selected this answer. + * @property isDisclosed whether the votes for this answer should be disclosed. + * @property votesCount the number of votes for this answer. + * @property progress the percentage of votes for this answer. + */ +data class PollAnswerItem( + val answer: PollAnswer, + val isSelected: Boolean, + val isDisclosed: Boolean, + val votesCount: Int, + val progress: Float, +) diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt new file mode 100644 index 0000000000..26fa6fbb71 --- /dev/null +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerView.kt @@ -0,0 +1,125 @@ +/* + * 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.poll.api + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.selection.selectable +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.unit.dp +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.Dimension +import androidx.constraintlayout.compose.Visibility +import io.element.android.libraries.designsystem.preview.DayNightPreviews +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.theme.components.LinearProgressIndicator +import io.element.android.libraries.designsystem.theme.components.RadioButton +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.theme.ElementTheme +import io.element.android.libraries.ui.strings.CommonPlurals + +@Suppress("DestructuringDeclarationWithTooManyEntries") // This is necessary to declare the constraints ids +@Composable +fun PollAnswerView( + answerItem: PollAnswerItem, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + ConstraintLayout( + modifier + .wrapContentHeight() + .fillMaxWidth() + .selectable( + selected = answerItem.isSelected, + onClick = onClick, + role = Role.RadioButton, + ) + ) { + val (radioButton, answerText, votesText, progressBar) = createRefs() + RadioButton( + modifier = Modifier.constrainAs(radioButton) { + top.linkTo(answerText.top) + bottom.linkTo(answerText.bottom) + start.linkTo(parent.start) + end.linkTo(answerText.start) + }, + selected = answerItem.isSelected, + onClick = null // null recommended for accessibility with screenreaders + ) + Text( + modifier = Modifier.constrainAs(answerText) { + width = Dimension.fillToConstraints + top.linkTo(parent.top) + start.linkTo(radioButton.end, margin = 8.dp) + end.linkTo(votesText.start) + bottom.linkTo(progressBar.top) + }, + text = answerItem.answer.text, + ) + Text( + modifier = Modifier.constrainAs(votesText) { + start.linkTo(answerText.end) + end.linkTo(parent.end) + bottom.linkTo(answerText.bottom) + visibility = if (answerItem.isDisclosed) Visibility.Visible else Visibility.Gone + }, + text = pluralStringResource( + id = CommonPlurals.common_poll_votes_count, + count = answerItem.votesCount, + answerItem.votesCount + ), + style = ElementTheme.typography.fontBodySmRegular, + color = ElementTheme.colors.textSecondary, + ) + LinearProgressIndicator( + progress = answerItem.progress, + modifier = Modifier + .constrainAs(progressBar) { + start.linkTo(answerText.start) + end.linkTo(votesText.end) + top.linkTo(answerText.bottom, margin = 10.dp) + bottom.linkTo(parent.bottom) + width = Dimension.fillToConstraints + visibility = if (answerItem.isDisclosed) Visibility.Visible else Visibility.Gone + + }, + strokeCap = StrokeCap.Round, + ) + } +} + +@DayNightPreviews +@Composable +internal fun PollAnswerViewNoResultsPreview() = ElementPreview { + PollAnswerView( + answerItem = aPollAnswerItem(), + onClick = { }, + ) +} + +@DayNightPreviews +@Composable +internal fun PollAnswerViewWithResultPreview() = ElementPreview { + PollAnswerView( + answerItem = aPollAnswerItem(isDisclosed = true), + onClick = { } + ) +} diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt new file mode 100644 index 0000000000..062d09fd88 --- /dev/null +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollAnswerViewProvider.kt @@ -0,0 +1,60 @@ +/* + * 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.poll.api + +import io.element.android.libraries.matrix.api.poll.PollAnswer +import kotlinx.collections.immutable.persistentListOf + +fun aPollAnswerItemList(isDisclosed: Boolean = true) = persistentListOf( + aPollAnswerItem( + answer = PollAnswer("option_1", "Italian \uD83C\uDDEE\uD83C\uDDF9"), + isDisclosed = isDisclosed, + votesCount = 5, + progress = 0.5f + ), + aPollAnswerItem( + answer = PollAnswer("option_2", "Chinese \uD83C\uDDE8\uD83C\uDDF3"), + isDisclosed = isDisclosed, + votesCount = 0, + progress = 0f + ), + aPollAnswerItem( + answer = PollAnswer("option_3", "Brazilian \uD83C\uDDE7\uD83C\uDDF7"), + isDisclosed = isDisclosed, + isSelected = true, + votesCount = 1, + progress = 0.1f + ), + aPollAnswerItem(isDisclosed = isDisclosed), +) + +fun aPollAnswerItem( + answer: PollAnswer = PollAnswer( + "option_4", + "French \uD83C\uDDEB\uD83C\uDDF7 But make it a very very very long option then this should just keep expanding" + ), + isSelected: Boolean = false, + isDisclosed: Boolean = true, + votesCount: Int = 4, + progress: Float = 0.4f, +) = PollAnswerItem( + answer = answer, + isSelected = isSelected, + isDisclosed = isDisclosed, + votesCount = votesCount, + progress = progress +) diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollEntryPoint.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollEntryPoint.kt new file mode 100644 index 0000000000..d8f2aed846 --- /dev/null +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/PollEntryPoint.kt @@ -0,0 +1,37 @@ +/* + * 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.poll.api + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import io.element.android.libraries.architecture.FeatureEntryPoint + +interface PollEntryPoint : FeatureEntryPoint { + + fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder + + interface NodeBuilder { + fun callback(callback: Callback): NodeBuilder + fun build(): Node + } + + interface Callback : Plugin { + // Add your callbacks + } +} + diff --git a/features/poll/impl/build.gradle.kts b/features/poll/impl/build.gradle.kts new file mode 100644 index 0000000000..626a7d0f2c --- /dev/null +++ b/features/poll/impl/build.gradle.kts @@ -0,0 +1,52 @@ +/* + * 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. + */ + +// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed +@Suppress("DSL_SCOPE_VIOLATION") +plugins { + id("io.element.android-compose-library") + alias(libs.plugins.anvil) + alias(libs.plugins.ksp) + id("kotlin-parcelize") +} + +android { + namespace = "io.element.android.features.poll.impl" +} + +anvil { + generateDaggerFactories.set(true) +} + +dependencies { + implementation(projects.anvilannotations) + anvil(projects.anvilcodegen) + api(projects.features.poll.api) + implementation(projects.libraries.core) + implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) + implementation(projects.libraries.matrixui) + implementation(projects.libraries.designsystem) + + testImplementation(libs.test.junit) + testImplementation(libs.coroutines.test) + testImplementation(libs.molecule.runtime) + testImplementation(libs.test.truth) + testImplementation(libs.test.turbine) + testImplementation(projects.libraries.matrix.test) + + ksp(libs.showkase.processor) +} diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/DefaultPollEntryPoint.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/DefaultPollEntryPoint.kt new file mode 100644 index 0000000000..052c1bcd5f --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/DefaultPollEntryPoint.kt @@ -0,0 +1,46 @@ +/* + * 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.poll.impl + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.poll.api.PollEntryPoint +import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.di.AppScope +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class DefaultPollEntryPoint @Inject constructor() : PollEntryPoint { + + override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): PollEntryPoint.NodeBuilder { + val plugins = ArrayList() + + return object : PollEntryPoint.NodeBuilder { + + override fun callback(callback: PollEntryPoint.Callback): PollEntryPoint.NodeBuilder { + plugins += callback + return this + } + + override fun build(): Node { + return parentNode.createNode(buildContext, plugins) + } + } + } +} diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/PollFlowNode.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/PollFlowNode.kt new file mode 100644 index 0000000000..9dfeebc692 --- /dev/null +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/PollFlowNode.kt @@ -0,0 +1,70 @@ +/* + * 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.poll.impl + +import android.os.Parcelable +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.composable.Children +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import com.bumble.appyx.navmodel.backstack.BackStack +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import io.element.android.anvilannotations.ContributesNode +import io.element.android.libraries.architecture.BackstackNode +import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler +import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.di.SessionScope +import kotlinx.parcelize.Parcelize + +@ContributesNode(SessionScope::class) +class PollFlowNode @AssistedInject constructor( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, +) : BackstackNode( + backstack = BackStack( + initialElement = NavTarget.Root, + savedStateMap = buildContext.savedStateMap, + ), + buildContext = buildContext, + plugins = plugins, +) { + + sealed interface NavTarget : Parcelable { + @Parcelize + object Root : NavTarget + } + + override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { + return when (navTarget) { + NavTarget.Root -> { + createNode(buildContext) + } + } + } + + @Composable + override fun View(modifier: Modifier) { + Children( + navModel = backstack, + modifier = modifier, + transitionHandler = rememberDefaultTransitionHandler(), + ) + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/LinearProgressIndicator.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/LinearProgressIndicator.kt new file mode 100644 index 0000000000..54985eaa51 --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/LinearProgressIndicator.kt @@ -0,0 +1,90 @@ +/* + * 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.libraries.designsystem.theme.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.ProgressIndicatorDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.preview.ElementThemedPreview +import io.element.android.libraries.designsystem.preview.PreviewGroup + +@Composable +fun LinearProgressIndicator( + progress: Float, + modifier: Modifier = Modifier, + color: Color = ProgressIndicatorDefaults.linearColor, + trackColor: Color = ProgressIndicatorDefaults.linearTrackColor, + strokeCap: StrokeCap = ProgressIndicatorDefaults.LinearStrokeCap, +) { + androidx.compose.material3.LinearProgressIndicator( + modifier = modifier, + progress = progress, + color = color, + trackColor = trackColor, + strokeCap = strokeCap, + ) +} + +@Composable +fun LinearProgressIndicator( + modifier: Modifier = Modifier, + color: Color = ProgressIndicatorDefaults.linearColor, + trackColor: Color = ProgressIndicatorDefaults.linearTrackColor, + strokeCap: StrokeCap = ProgressIndicatorDefaults.LinearStrokeCap, +) { + if (LocalInspectionMode.current) { + // Use a determinate progress indicator to improve the preview rendering + androidx.compose.material3.LinearProgressIndicator( + modifier = modifier, + progress = 0.75F, + color = color, + trackColor = trackColor, + strokeCap = strokeCap, + ) + } else { + androidx.compose.material3.LinearProgressIndicator( + modifier = modifier, + color = color, + trackColor = trackColor, + strokeCap = strokeCap, + ) + } +} + +@Preview(group = PreviewGroup.Progress) +@Composable +internal fun LinearProgressIndicatorPreview() = ElementThemedPreview(vertical = false) { ContentToPreview() } + +@Composable +private fun ContentToPreview() { + Column(verticalArrangement = Arrangement.spacedBy(4.dp)) { + // Indeterminate progress + LinearProgressIndicator( + ) + // Fixed progress + LinearProgressIndicator( + progress = 0.90F + ) + } +} diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt index f1af61c8e7..0736cf61ce 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt @@ -37,6 +37,8 @@ import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessa import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent import io.element.android.libraries.matrix.api.timeline.item.event.MessageType import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import io.element.android.libraries.matrix.api.timeline.item.event.PollEndContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent @@ -94,6 +96,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor( is StateContent -> { stateContentFormatter.format(content, senderDisplayName, isOutgoing, RenderingMode.RoomList) } + is PollContent, is PollEndContent, // TODO Polls: handle last message is FailedToParseMessageLikeContent, is FailedToParseStateContent, is UnknownContent -> { prefixIfNeeded(sp.getString(CommonStrings.common_unsupported_event), senderDisplayName, isDmRoom) } diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt index 8f89233a31..42d8aae083 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultTimelineEventFormatter.kt @@ -26,6 +26,8 @@ import io.element.android.libraries.matrix.api.timeline.item.event.EventTimeline import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import io.element.android.libraries.matrix.api.timeline.item.event.PollEndContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent @@ -63,6 +65,8 @@ class DefaultTimelineEventFormatter @Inject constructor( } RedactedContent, is StickerContent, + is PollContent, + is PollEndContent, is UnableToDecryptContent, is MessageContent, is FailedToParseMessageLikeContent, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollAnswer.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollAnswer.kt new file mode 100644 index 0000000000..2d4abaafb5 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollAnswer.kt @@ -0,0 +1,22 @@ +/* + * 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.libraries.matrix.api.poll + +data class PollAnswer( + val id: String, + val text: String +) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollKind.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollKind.kt new file mode 100644 index 0000000000..85bb7c0256 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/poll/PollKind.kt @@ -0,0 +1,24 @@ +/* + * 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.libraries.matrix.api.poll + +enum class PollKind { + /** Voters should see results as soon as they have voted. */ + Disclosed, + + /** Results should be only revealed when the poll is ended. */ + Undisclosed +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt index 5ddc26a6fc..82c322668c 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt @@ -23,6 +23,8 @@ import io.element.android.libraries.matrix.api.media.FileInfo import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.media.VideoInfo +import io.element.android.libraries.matrix.api.poll.PollAnswer +import io.element.android.libraries.matrix.api.poll.PollKind sealed interface EventContent @@ -69,6 +71,19 @@ data class StickerContent( val url: String ) : EventContent +data class PollContent( + val question: String, + val kind: PollKind, + val maxSelections: ULong, + val answers: List, + val votes: Map>, + val endTime: ULong? +) : EventContent + +data class PollEndContent( + val startEventId: String +) : EventContent + data class UnableToDecryptContent( val data: Data ) : EventContent { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/poll/PollAnswer.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/poll/PollAnswer.kt new file mode 100644 index 0000000000..c3098bdcb0 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/poll/PollAnswer.kt @@ -0,0 +1,25 @@ +/* + * 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.libraries.matrix.impl.poll + +import io.element.android.libraries.matrix.api.poll.PollAnswer +import org.matrix.rustcomponents.sdk.PollAnswer as RustPollAnswer + +fun RustPollAnswer.map(): PollAnswer = PollAnswer( + id = id, + text = text, +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/poll/PollKind.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/poll/PollKind.kt new file mode 100644 index 0000000000..bde49464ad --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/poll/PollKind.kt @@ -0,0 +1,25 @@ +/* + * 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.libraries.matrix.impl.poll + +import io.element.android.libraries.matrix.api.poll.PollKind +import org.matrix.rustcomponents.sdk.PollKind as RustPollKind + +fun RustPollKind.map(): PollKind = when (this) { + RustPollKind.DISCLOSED -> PollKind.Disclosed + RustPollKind.UNDISCLOSED -> PollKind.Undisclosed +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt index 52d646e48e..7ee1d1490d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/TimelineEventContentMapper.kt @@ -22,6 +22,8 @@ import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParse import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange import io.element.android.libraries.matrix.api.timeline.item.event.OtherState +import io.element.android.libraries.matrix.api.timeline.item.event.PollContent +import io.element.android.libraries.matrix.api.timeline.item.event.PollEndContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent @@ -30,6 +32,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.StickerConten import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent import io.element.android.libraries.matrix.impl.media.map +import io.element.android.libraries.matrix.impl.poll.map import org.matrix.rustcomponents.sdk.TimelineItemContent import org.matrix.rustcomponents.sdk.TimelineItemContentKind import org.matrix.rustcomponents.sdk.EncryptedMessage as RustEncryptedMessage @@ -91,6 +94,21 @@ class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMap url = kind.url, ) } + is TimelineItemContentKind.Poll -> { + PollContent( + question = kind.question, + kind = kind.kind.map(), + maxSelections = kind.maxSelections, + answers = kind.answers.map { answer -> answer.map() }, + votes = kind.votes.mapValues { vote -> + vote.value.map { userId -> UserId(userId) } + }, + endTime = kind.endTime, + ) + } + is TimelineItemContentKind.PollEnd -> { + PollEndContent(startEventId = kind.startEventId) + } is TimelineItemContentKind.UnableToDecrypt -> { UnableToDecryptContent( data = kind.msg.map() diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-D-13_14_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-D-13_14_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..05cf88f8f9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-D-13_14_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1978bafb5fd079de3fe7945a0574b2b19b0480d9d7d146e034b749e674f76254 +size 48667 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-D-13_14_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-D-13_14_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..05cf88f8f9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-D-13_14_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1978bafb5fd079de3fe7945a0574b2b19b0480d9d7d146e034b749e674f76254 +size 48667 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-N-13_15_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-N-13_15_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c0d3cd8739 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-N-13_15_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df79a0bd1caa8ecf4b701eaa589cc90efc748b986388f9ae5636b9dceba4492c +size 45928 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-N-13_15_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-N-13_15_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c0d3cd8739 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemPollViewPreview-N-13_15_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df79a0bd1caa8ecf4b701eaa589cc90efc748b986388f9ae5636b9dceba4492c +size 45928 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-13_14_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-14_15_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-13_14_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-D-14_15_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-13_15_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-14_16_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-13_15_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.reactionsummary_null_DefaultGroup_SheetContentPreview-N-14_16_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-14_15_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-15_16_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-14_15_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-D-15_16_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-14_16_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-15_17_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-14_16_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.virtual_null_DefaultGroup_TimelineEncryptedHistoryBannerViewPreview-N-15_17_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentNoResultsPreview-D-0_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentNoResultsPreview-D-0_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3b7a5b286f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentNoResultsPreview-D-0_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1fb1d9bb4ab01a0af8ee20c08084fd01baba0f77a4c4a39c179a8e19d1dadd47 +size 46624 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentNoResultsPreview-N-0_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentNoResultsPreview-N-0_1_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1244226217 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentNoResultsPreview-N-0_1_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18749d3e3a8b4e1176fa6c8303439d07306c3518e6be625d5ec236e2f24433b5 +size 43265 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentWithResultsPreview-D-1_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentWithResultsPreview-D-1_1_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..05cf88f8f9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentWithResultsPreview-D-1_1_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1978bafb5fd079de3fe7945a0574b2b19b0480d9d7d146e034b749e674f76254 +size 48667 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentWithResultsPreview-N-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentWithResultsPreview-N-1_2_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c0d3cd8739 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_ActivePollContentWithResultsPreview-N-1_2_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df79a0bd1caa8ecf4b701eaa589cc90efc748b986388f9ae5636b9dceba4492c +size 45928 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewNoResultsPreview-D-2_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewNoResultsPreview-D-2_2_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d5c1931df5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewNoResultsPreview-D-2_2_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:239eaab2812e0e2968f52a41a741b8782b28ceaf72f029267de6248a5ce67fc6 +size 23063 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewNoResultsPreview-N-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewNoResultsPreview-N-2_3_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..560952376b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewNoResultsPreview-N-2_3_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9cf7435b528b3920e621d6bd688a8692c8bd366db1522d2947d5e204f905644 +size 21563 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewWithResultPreview-D-3_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewWithResultPreview-D-3_3_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d5c1931df5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewWithResultPreview-D-3_3_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:239eaab2812e0e2968f52a41a741b8782b28ceaf72f029267de6248a5ce67fc6 +size 23063 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewWithResultPreview-N-3_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewWithResultPreview-N-3_4_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..560952376b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.poll.api_null_DefaultGroup_PollAnswerViewWithResultPreview-N-3_4_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a9cf7435b528b3920e621d6bd688a8692c8bd366db1522d2947d5e204f905644 +size 21563 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_ProgressIndicators_LinearProgressIndicatorPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_ProgressIndicators_LinearProgressIndicatorPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2f67b451b6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_ProgressIndicators_LinearProgressIndicatorPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b756303958d8234c0e8c0f9ca16335c54d8bd610c73790058d6389eb7c8c3fae +size 4875 From 028ee3a796e4c1eb819906b0e7b71d9b22c33587 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Aug 2023 16:55:49 +0200 Subject: [PATCH 249/251] Release script, do not exit in case of error. --- tools/release/release.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/release/release.sh b/tools/release/release.sh index 9934f43610..20005d2816 100755 --- a/tools/release/release.sh +++ b/tools/release/release.sh @@ -16,8 +16,8 @@ # limitations under the License. # -# exit when any command fails -set -e +# do not exit when any command fails (issue with git flow) +set +e printf "\n================================================================================\n" printf "| Welcome to the release script! |\n" From 45d34e2881a153fe6f9891d39162cbfe41b74f3b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Aug 2023 17:08:40 +0200 Subject: [PATCH 250/251] Changelog for version 0.1.2 --- CHANGES.md | 21 +++++++++++++++++++++ changelog.d/1021.misc | 1 - changelog.d/1031.wip | 1 - changelog.d/1035.bugfix | 1 - changelog.d/1043.misc | 1 - changelog.d/1049.misc | 1 - changelog.d/1050.misc | 1 - changelog.d/1054.misc | 1 - changelog.d/640.bugfix | 1 - 9 files changed, 21 insertions(+), 8 deletions(-) delete mode 100644 changelog.d/1021.misc delete mode 100644 changelog.d/1031.wip delete mode 100644 changelog.d/1035.bugfix delete mode 100644 changelog.d/1043.misc delete mode 100644 changelog.d/1049.misc delete mode 100644 changelog.d/1050.misc delete mode 100644 changelog.d/1054.misc delete mode 100644 changelog.d/640.bugfix diff --git a/CHANGES.md b/CHANGES.md index e04dbb63c8..03766ed66b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,24 @@ +Changes in Element X v0.1.2 (2023-08-16) +======================================== + +Bugfixes 🐛 +---------- + - Filter push notifications using push rules. ([#640](https://github.com/vector-im/element-x-android/issues/640)) + - Use `for` instead of `forEach` in `DefaultDiffCacheInvalidator` to improve performance. ([#1035](https://github.com/vector-im/element-x-android/issues/1035)) + +In development 🚧 +---------------- + - [Poll] Render start event in the timeline ([#1031](https://github.com/vector-im/element-x-android/issues/1031)) + +Other changes +------------- + - Add Button component based on Compound designs ([#1021](https://github.com/vector-im/element-x-android/issues/1021)) + - Compound: implement dialogs. ([#1043](https://github.com/vector-im/element-x-android/issues/1043)) + - Compound: customise `IconButton` component. ([#1049](https://github.com/vector-im/element-x-android/issues/1049)) + - Compound: implement `DropdownMenu` customisations. ([#1050](https://github.com/vector-im/element-x-android/issues/1050)) + - Compound: implement Snackbar component. ([#1054](https://github.com/vector-im/element-x-android/issues/1054)) + + Changes in Element X v0.1.0 (2023-07-19) ======================================== diff --git a/changelog.d/1021.misc b/changelog.d/1021.misc deleted file mode 100644 index ffb278b33d..0000000000 --- a/changelog.d/1021.misc +++ /dev/null @@ -1 +0,0 @@ -Add Button component based on Compound designs diff --git a/changelog.d/1031.wip b/changelog.d/1031.wip deleted file mode 100644 index 9816f1af8a..0000000000 --- a/changelog.d/1031.wip +++ /dev/null @@ -1 +0,0 @@ -[Poll] Render start event in the timeline diff --git a/changelog.d/1035.bugfix b/changelog.d/1035.bugfix deleted file mode 100644 index 4a2a6376ce..0000000000 --- a/changelog.d/1035.bugfix +++ /dev/null @@ -1 +0,0 @@ -Use `for` instead of `forEach` in `DefaultDiffCacheInvalidator` to improve performance. diff --git a/changelog.d/1043.misc b/changelog.d/1043.misc deleted file mode 100644 index 32cc442bc3..0000000000 --- a/changelog.d/1043.misc +++ /dev/null @@ -1 +0,0 @@ -Compound: implement dialogs. diff --git a/changelog.d/1049.misc b/changelog.d/1049.misc deleted file mode 100644 index 2b80eafa4f..0000000000 --- a/changelog.d/1049.misc +++ /dev/null @@ -1 +0,0 @@ -Compound: customise `IconButton` component. diff --git a/changelog.d/1050.misc b/changelog.d/1050.misc deleted file mode 100644 index 5a32d00d15..0000000000 --- a/changelog.d/1050.misc +++ /dev/null @@ -1 +0,0 @@ -Compound: implement `DropdownMenu` customisations. diff --git a/changelog.d/1054.misc b/changelog.d/1054.misc deleted file mode 100644 index 7a0a477d76..0000000000 --- a/changelog.d/1054.misc +++ /dev/null @@ -1 +0,0 @@ -Compound: implement Snackbar component. diff --git a/changelog.d/640.bugfix b/changelog.d/640.bugfix deleted file mode 100644 index 9edb67f4ce..0000000000 --- a/changelog.d/640.bugfix +++ /dev/null @@ -1 +0,0 @@ -Filter push notifications using push rules. From a06c75ad91428844003258ebd8f1dd8b217012b6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 16 Aug 2023 17:09:06 +0200 Subject: [PATCH 251/251] Adding fastlane file for version 0.1.2 --- fastlane/metadata/android/en-US/changelogs/40001020.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/40001020.txt diff --git a/fastlane/metadata/android/en-US/changelogs/40001020.txt b/fastlane/metadata/android/en-US/changelogs/40001020.txt new file mode 100644 index 0000000000..8aacdd89af --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/40001020.txt @@ -0,0 +1,2 @@ +First release of Element X 🚀! +Full changelog: https://github.com/vector-im/element-x-android/releases