Merge branch 'develop' into feature/fga/timeline-multi-forward

This commit is contained in:
ganfra
2026-10-01 15:23:42 +02:00
491 changed files with 1483 additions and 1141 deletions
@@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -33,7 +32,6 @@ import io.element.android.libraries.matrix.ui.components.MatrixUserRow
import io.element.android.libraries.ui.strings.CommonStrings
@Suppress("MultipleEmitters") // False positive
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AccountSelectView(
state: AccountSelectState,
@@ -32,6 +32,8 @@ import io.element.android.libraries.designsystem.theme.components.Text
/**
* IconTitleSubtitleMolecule is a molecule which displays an icon, a title and a subtitle.
*
* Ref: https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=1964-3385
*
* @param title the title to display
* @param subTitle the subtitle to display
* @param iconStyle the style of the [BigIcon] to display
@@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -40,7 +39,6 @@ import io.element.android.libraries.designsystem.theme.components.TopAppBar
* - a content.
* - a footer, as ButtonColumnMolecule
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FlowStepPage(
iconStyle: BigIcon.Style,
@@ -53,13 +53,11 @@ object BigIcon {
* @param vectorIcon the [ImageVector] to display
* @param contentDescription the content description of the icon, if any. It defaults to `null`
* @param useCriticalTint whether the icon and background should be rendered using critical tint
* @param usePrimaryTint whether the icon should be rendered using primary tint
*/
data class Default(
val vectorIcon: ImageVector,
val contentDescription: String? = null,
val useCriticalTint: Boolean = false,
val usePrimaryTint: Boolean = false,
) : Style
/**
@@ -129,10 +127,8 @@ object BigIcon {
val iconTint = when (style) {
is Style.Default -> if (style.useCriticalTint) {
ElementTheme.colors.iconCriticalPrimary
} else if (style.usePrimaryTint) {
ElementTheme.colors.iconPrimary
} else {
ElementTheme.colors.iconSecondary
ElementTheme.colors.iconPrimary
}
Style.Alert,
Style.AlertSolid -> ElementTheme.colors.iconCriticalPrimary
@@ -8,7 +8,6 @@
package io.element.android.libraries.designsystem.components
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Surface
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.contentColorFor
@@ -24,7 +23,6 @@ import io.element.android.compound.theme.ElementTheme
* It places the content according to the current height offset of the scroll behavior.
*
*/
@ExperimentalMaterial3Api
@Composable
fun TopAppBarScrollBehaviorLayout(
scrollBehavior: TopAppBarScrollBehavior,
@@ -31,6 +31,7 @@ enum class AvatarSize(val dp: Dp) {
UserHeader(96.dp),
UserListItem(36.dp),
AccountInfoUser(36.dp),
SelectedUser(52.dp),
SelectedRoom(56.dp),
@@ -9,7 +9,6 @@
package io.element.android.libraries.designsystem.components.dialogs
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -22,7 +21,6 @@ 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)
@Composable
fun AlertDialog(
content: String,
@@ -9,7 +9,6 @@
package io.element.android.libraries.designsystem.components.dialogs
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -22,7 +21,6 @@ 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)
@Composable
fun ConfirmationDialog(
content: String,
@@ -9,7 +9,6 @@
package io.element.android.libraries.designsystem.components.dialogs
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -23,7 +22,6 @@ 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)
@Composable
fun ErrorDialog(
content: String,
@@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -31,7 +30,6 @@ import io.element.android.libraries.designsystem.theme.components.SimpleAlertDia
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ErrorDialogWithDoNotShowAgain(
content: String,
@@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -29,7 +28,6 @@ import io.element.android.libraries.designsystem.theme.components.ListSupporting
import io.element.android.libraries.designsystem.theme.components.SimpleAlertDialogContent
import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ListDialog(
onSubmit: () -> Unit,
@@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.toMutableStateList
@@ -32,7 +31,6 @@ import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MultipleSelectionDialog(
options: ImmutableList<ListOption>,
@@ -9,7 +9,6 @@
package io.element.android.libraries.designsystem.components.dialogs
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -22,7 +21,6 @@ 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)
@Composable
fun RetryDialog(
content: String,
@@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -29,7 +28,6 @@ import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SingleSelectionDialog(
options: ImmutableList<ListOption>,
@@ -6,8 +6,6 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.libraries.designsystem.components.preferences
import androidx.annotation.DrawableRes
@@ -16,7 +14,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -17,7 +17,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.plus
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.heading
@@ -68,7 +67,6 @@ fun PreferencePage(
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun PreferenceTopAppBar(
title: String,
@@ -17,7 +17,6 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledIconButton
import androidx.compose.material3.FloatingToolbarColors
import androidx.compose.material3.FloatingToolbarDefaults
@@ -46,7 +45,6 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
/**
* Ref: https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=4457-1136
*/
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
@Composable
fun HorizontalFloatingToolbar(
modifier: Modifier = Modifier,
@@ -194,7 +192,6 @@ internal fun HorizontalFloatingToolbarNoFabPreview() = ElementPreview {
)
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
@Composable
private fun ContentToPreview(
floatingActionButton: (@Composable () -> Unit)?,
@@ -10,7 +10,6 @@ package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.TopAppBarColors
import androidx.compose.material3.TopAppBarDefaults
@@ -25,7 +24,6 @@ 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
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MediumTopAppBar(
title: @Composable () -> Unit,
@@ -51,7 +49,6 @@ fun MediumTopAppBar(
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Preview(group = PreviewGroup.AppBars)
@Composable
internal fun MediumTopAppBarPreview() = ElementThemedPreview {
@@ -79,6 +79,9 @@ fun <T> SearchBar(
}
}
// TODO Migrate to the SearchBarState API. The replacement (ExpandedFullScreenSearchBar) displays the results in a dialog,
// which would change the UX, in particular for the emoji picker bottom sheet.
@Suppress("DEPRECATION")
SearchBar(
inputField = {
SearchBarDefaults.InputField(
@@ -156,7 +159,6 @@ fun <T> SearchBar(
}
object ElementSearchBarDefaults {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun inactiveColors() = SearchBarDefaults.colors(
containerColor = Color.Transparent,
@@ -176,7 +178,6 @@ object ElementSearchBarDefaults {
errorContainerColor = ElementTheme.colors.bgSubtleSecondary,
)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun activeColors() = SearchBarDefaults.colors(
containerColor = Color.Transparent,
@@ -286,7 +287,6 @@ internal fun SearchBarActiveWithContentPreview() = ElementThemedPreview {
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@ExcludeFromCoverage
private fun ContentToPreview(
@@ -9,7 +9,6 @@
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SegmentedButton
import androidx.compose.material3.SegmentedButtonDefaults
import androidx.compose.material3.SingleChoiceSegmentedButtonRowScope
@@ -19,7 +18,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import io.element.android.compound.theme.ElementTheme
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SingleChoiceSegmentedButtonRowScope.SegmentedButton(
index: Int,
@@ -6,8 +6,6 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.interaction.DragInteraction
@@ -15,9 +13,9 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.height
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SliderColors
import androidx.compose.material3.SliderDefaults
import androidx.compose.material3.SliderState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -60,16 +58,16 @@ fun Slider(
}
}
}
val sliderState = remember(steps, valueRange) { SliderState(value = value, steps = steps, trackRange = valueRange) }
sliderState.value = value
androidx.compose.material3.Slider(
value = value,
state = sliderState,
onValueChange = onValueChange,
modifier = modifier,
enabled = enabled,
valueRange = valueRange,
steps = steps,
onValueChangeFinished = onValueChangeFinish,
colors = colors,
thumb = {
thumb = { _ ->
if (useCustomLayout) {
SliderDefaults.Thumb(
modifier = Modifier.drawWithContent {
@@ -10,7 +10,6 @@ package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.TopAppBarColors
import androidx.compose.material3.TopAppBarDefaults
@@ -38,7 +37,6 @@ import io.element.android.libraries.designsystem.theme.aliasScreenTitle
* @param colors The colors used for this top app bar.
* @param scrollBehavior Optional scroll behavior for this top app bar.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TopAppBar(
titleStr: String,
@@ -78,7 +76,6 @@ fun TopAppBar(
* @param colors The colors used for this top app bar.
* @param scrollBehavior Optional scroll behavior for this top app bar.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TopAppBar(
title: @Composable () -> Unit,
@@ -104,7 +101,6 @@ fun TopAppBar(
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Preview(group = PreviewGroup.AppBars)
@Composable
internal fun TopAppBarPreview() = ElementThemedPreview {
@@ -123,7 +119,6 @@ internal fun TopAppBarPreview() = ElementThemedPreview {
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Preview(group = PreviewGroup.AppBars)
@Composable
internal fun TopAppBarStrPreview() = ElementThemedPreview {
@@ -10,7 +10,6 @@ package io.element.android.libraries.designsystem.theme.components.previews
import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.DatePicker
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.rememberDatePickerState
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
@@ -33,7 +32,6 @@ internal fun DatePickerDarkPreview() {
ElementPreviewDark { ContentToPreview() }
}
@OptIn(ExperimentalMaterial3Api::class)
@ExcludeFromCoverage
@Composable
private fun ContentToPreview() {
@@ -9,7 +9,6 @@
package io.element.android.libraries.designsystem.theme.components.previews
import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TimePicker
import androidx.compose.material3.TimePickerLayoutType
import androidx.compose.material3.rememberTimePickerState
@@ -22,7 +21,6 @@ import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.AlertDialogContent
@OptIn(ExperimentalMaterial3Api::class)
@Preview(widthDp = 600, group = PreviewGroup.DateTimePickers)
@Composable
internal fun TimePickerHorizontalPreview() {
@@ -44,7 +42,6 @@ internal fun TimePickerHorizontalPreview() {
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Preview(group = PreviewGroup.DateTimePickers)
@Composable
internal fun TimePickerVerticalLightPreview() {
@@ -66,7 +63,6 @@ internal fun TimePickerVerticalLightPreview() {
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Preview(group = PreviewGroup.DateTimePickers)
@Composable
internal fun TimePickerVerticalDarkPreview() {
@@ -21,7 +21,6 @@ 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.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SecondaryTabRow
import androidx.compose.material3.Tab
import androidx.compose.runtime.Composable
@@ -47,7 +46,6 @@ import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.persistentSetOf
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun EmojiPickerView(
state: DefaultEmojiPickerState,
@@ -121,14 +121,14 @@ enum class FeatureFlags(
title = "Automatic back pagination of rooms",
description = "Allow the app to automatically back paginate in rooms to pre-fetch older messages in background." +
"\nRequires an app restart to take effect.",
defaultValue = { false },
defaultValue = { true },
isFinished = false,
),
UnreadIndicatorCount(
key = "feature.unread_indicator_count",
title = "Unread indicator count",
description = "Show the number of unread messages on the unread indicator in the room list.",
defaultValue = { false },
defaultValue = { true },
isFinished = false,
),
SendGalleryMessages(
@@ -31,6 +31,7 @@ import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.material3.LocalTextStyle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.SideEffect
@@ -218,7 +219,7 @@ private fun ParagraphView(
TextBlock(
text = node.text,
inlineContent = node.inlineContent,
textStyle = ElementTheme.typography.fontBodyMdRegular,
textStyle = LocalTextStyle.current,
context = context,
modifier = modifier,
onTextLayout = { measureLastLine?.invoke(it) },
@@ -6,8 +6,6 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.libraries.matrix.ui.components
import androidx.activity.compose.BackHandler
@@ -0,0 +1,192 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.ui.components.accountinfo
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtom
import io.element.android.libraries.designsystem.atomic.atoms.RoundedIconAtomSize
import io.element.android.libraries.designsystem.atomic.molecules.TextWithLabelMolecule
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
import io.element.android.libraries.designsystem.icons.CompoundDrawables
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
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.matrix.ui.model.getBestName
import io.element.android.libraries.ui.strings.CommonStrings
/**
* Ref: https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=5873-996
*/
@Composable
fun AccountInfoCard(
state: AccountInfoCardState,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.fillMaxWidth()
.border(1.dp, ElementTheme.colors.borderDisabled, RoundedCornerShape(8.dp))
.padding(20.dp),
) {
when (state.mode) {
AccountInfoCardMode.Simple -> Unit
is AccountInfoCardMode.DeviceVerification -> Unit
is AccountInfoCardMode.UserVerification -> {
Text(
// TODO
text = "User requesting verification:",
style = ElementTheme.typography.fontBodySmRegular,
color = ElementTheme.colors.textSecondary,
)
Spacer(modifier = Modifier.height(8.dp))
}
}
AccountInfoCardUser(
matrixUser = state.matrixUser,
)
when (state.mode) {
AccountInfoCardMode.Simple -> Unit
is AccountInfoCardMode.DeviceVerification -> {
AccountInfoSeparator()
DeviceInfoRow(state.mode)
}
is AccountInfoCardMode.UserVerification -> {
AccountInfoSeparator()
AccountInfoCardUser(
matrixUser = state.mode.otherUser,
)
}
}
}
}
@Composable
private fun AccountInfoSeparator() {
HorizontalDivider(
modifier = Modifier.padding(vertical = 20.dp),
color = ElementTheme.colors.separatorPrimary,
)
}
@Composable
private fun DeviceInfoRow(
mode: AccountInfoCardMode.DeviceVerification,
) {
Column(
modifier = Modifier
.fillMaxWidth()
.semantics(mergeDescendants = true) {},
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
RoundedIconAtom(
modifier = Modifier,
size = RoundedIconAtomSize.Big,
resourceId = CompoundDrawables.ic_compound_devices
)
Text(
text = mode.deviceName ?: mode.deviceId.value,
style = ElementTheme.typography.fontBodyMdMedium,
color = ElementTheme.colors.textPrimary,
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
TextWithLabelMolecule(
// TODO i18n
label = "Signed in",
text = mode.signInFormattedTimestamp,
modifier = Modifier.weight(1f),
)
TextWithLabelMolecule(
label = stringResource(CommonStrings.common_device_id),
text = mode.deviceId.value,
modifier = Modifier.weight(1f),
spellText = true,
)
}
}
}
@Composable
private fun AccountInfoCardUser(matrixUser: MatrixUser) {
val subtext = if (matrixUser.displayName.isNullOrEmpty()) null else matrixUser.userId.value
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
Avatar(
avatarData = matrixUser.getAvatarData(AvatarSize.AccountInfoUser),
avatarType = AvatarType.User,
)
Spacer(modifier = Modifier.width(12.dp))
Column(
modifier = Modifier.weight(1f),
) {
// Name
Text(
modifier = Modifier.clipToBounds(),
text = matrixUser.getBestName(),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = ElementTheme.colors.textPrimary,
style = ElementTheme.typography.fontBodyLgMedium,
)
// Id
subtext?.let {
Text(
text = subtext,
color = ElementTheme.colors.textSecondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = ElementTheme.typography.fontBodyMdRegular,
)
}
}
}
}
@PreviewsDayNight
@Composable
internal fun AccountInfoCardPreview(
@PreviewParameter(AccountInfoCardStatePreviewParam::class) state: AccountInfoCardState
) = ElementPreview {
// Add some padding to view the border
Box(modifier = Modifier.padding(16.dp)) {
AccountInfoCard(state)
}
}
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.ui.components.accountinfo
import androidx.compose.runtime.Immutable
import io.element.android.libraries.matrix.api.core.DeviceId
import io.element.android.libraries.matrix.api.user.MatrixUser
data class AccountInfoCardState(
val matrixUser: MatrixUser,
val mode: AccountInfoCardMode,
)
@Immutable
sealed interface AccountInfoCardMode {
data object Simple : AccountInfoCardMode
data class UserVerification(
val otherUser: MatrixUser,
) : AccountInfoCardMode
// TODO Replace SessionDetailsView with this component
data class DeviceVerification(
val deviceName: String?,
val deviceId: DeviceId,
val signInFormattedTimestamp: String,
) : AccountInfoCardMode
}
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.ui.components.accountinfo
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.matrix.api.core.DeviceId
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.components.aMatrixUser
open class AccountInfoCardStatePreviewParam : PreviewParameterProvider<AccountInfoCardState> {
override val values: Sequence<AccountInfoCardState>
get() = sequenceOf(
anAccountInfoCardState(),
anAccountInfoCardState(aMatrixUser(displayName = null)),
anAccountInfoCardState(
mode = AccountInfoCardMode.UserVerification(
otherUser = aMatrixUser(displayName = "Other User"),
)
),
anAccountInfoCardState(
mode = AccountInfoCardMode.DeviceVerification(
deviceName = "My Device",
deviceId = DeviceId("DEVICEID"),
signInFormattedTimestamp = "2024-06-01 12:00:00",
)
),
)
}
private fun anAccountInfoCardState(
matrixUser: MatrixUser = aMatrixUser(),
mode: AccountInfoCardMode = AccountInfoCardMode.Simple,
) = AccountInfoCardState(
matrixUser = matrixUser,
mode = mode,
)
@@ -49,6 +49,9 @@ import io.element.android.libraries.mediaviewer.impl.R
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.libraries.ui.strings.Strings
/**
* Ref: https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=4168-7384
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MediaDeleteConfirmationBottomSheet(
@@ -5,13 +5,10 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.libraries.mediaviewer.impl.fileviewer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -34,7 +34,6 @@ import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
@@ -99,7 +98,6 @@ import io.element.android.libraries.mediaviewer.impl.model.id
import io.element.android.libraries.voiceplayer.api.VoiceMessageState
import kotlinx.collections.immutable.ImmutableList
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MediaGalleryView(
state: MediaGalleryState,
@@ -71,7 +71,6 @@ fun MediaFileView(
modifier = Modifier.align(Alignment.CenterHorizontally),
style = BigIcon.Style.Default(
vectorIcon = icon,
usePrimaryTint = true,
),
)
if (info != null) {
@@ -6,8 +6,6 @@
* Please see LICENSE files in the repository root for full details.
*/
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.libraries.mediaviewer.impl.viewer
import androidx.activity.compose.BackHandler
@@ -36,7 +34,6 @@ import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
@@ -548,7 +545,6 @@ private fun rememberShowProgress(downloadedMedia: AsyncData<LocalMedia>): Boolea
return showProgress
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun MediaViewerTopBar(
data: MediaViewerPageData.MediaViewerData,
@@ -29,7 +29,6 @@ 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.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -70,7 +69,6 @@ import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
@Suppress("MultipleEmitters") // False positive
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RoomSelectView(
state: RoomSelectState,
@@ -162,7 +162,9 @@ class MarkdownTextInputTest : RobolectricTest() {
val state = aMarkdownTextEditorState(initialText = "Test", initialFocus = false)
setMarkdownTextInput(state = state)
val editor = activity!!.findEditor()
editor.requestFocus()
// The test window is in touch mode, where the editor is not focusable in Robolectric.
// requestFocusFromTouch() leaves the touch mode first, like a hardware keyboard would do.
editor.requestFocusFromTouch()
// Focus state is updated
assertThat(state.hasFocus).isTrue()
}
@@ -20,7 +20,6 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -53,7 +52,6 @@ import io.element.android.libraries.push.api.history.PushHistoryItem
import io.element.android.libraries.troubleshoot.impl.R
import io.element.android.libraries.ui.strings.CommonStrings
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PushHistoryView(
state: PushHistoryState,