Extract topbars from MessagesView to reduce this file size.
No code change.
This commit is contained in:
parent
b75b79bf40
commit
f05b1355fb
3 changed files with 288 additions and 227 deletions
|
|
@ -11,12 +11,10 @@ import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.expandVertically
|
import androidx.compose.animation.expandVertically
|
||||||
import androidx.compose.animation.shrinkVertically
|
import androidx.compose.animation.shrinkVertically
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
|
@ -27,30 +25,23 @@ import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.statusBars
|
import androidx.compose.foundation.layout.statusBars
|
||||||
import androidx.compose.foundation.layout.systemBarsPadding
|
import androidx.compose.foundation.layout.systemBarsPadding
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.draw.shadow
|
import androidx.compose.ui.draw.shadow
|
||||||
import androidx.compose.ui.graphics.RectangleShape
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.semantics.Role
|
import androidx.compose.ui.semantics.Role
|
||||||
import androidx.compose.ui.semantics.heading
|
|
||||||
import androidx.compose.ui.semantics.onClick
|
import androidx.compose.ui.semantics.onClick
|
||||||
import androidx.compose.ui.semantics.role
|
import androidx.compose.ui.semantics.role
|
||||||
import androidx.compose.ui.semantics.semantics
|
import androidx.compose.ui.semantics.semantics
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
|
||||||
import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerEvents
|
import io.element.android.features.messages.api.timeline.voicemessages.composer.VoiceMessageComposerEvents
|
||||||
import io.element.android.features.messages.impl.actionlist.ActionListEvents
|
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.ActionListView
|
||||||
|
|
@ -69,7 +60,6 @@ import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBan
|
||||||
import io.element.android.features.messages.impl.timeline.FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS
|
import io.element.android.features.messages.impl.timeline.FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS
|
||||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||||
import io.element.android.features.messages.impl.timeline.TimelineView
|
import io.element.android.features.messages.impl.timeline.TimelineView
|
||||||
import io.element.android.features.messages.impl.timeline.components.CallMenuItem
|
|
||||||
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionBottomSheet
|
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.customreaction.CustomReactionEvents
|
||||||
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents
|
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents
|
||||||
|
|
@ -77,30 +67,23 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum
|
||||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheet
|
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheet
|
||||||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents
|
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
|
import io.element.android.features.messages.impl.topbars.MessagesViewTopBar
|
||||||
|
import io.element.android.features.messages.impl.topbars.ThreadTopBar
|
||||||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessagePermissionRationaleDialog
|
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessagePermissionRationaleDialog
|
||||||
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageSendingFailedDialog
|
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageSendingFailedDialog
|
||||||
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView
|
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView
|
||||||
import io.element.android.features.roomcall.api.RoomCallState
|
|
||||||
import io.element.android.libraries.androidutils.ui.hideKeyboard
|
import io.element.android.libraries.androidutils.ui.hideKeyboard
|
||||||
import io.element.android.libraries.designsystem.atomic.molecules.ComposerAlertMolecule
|
import io.element.android.libraries.designsystem.atomic.molecules.ComposerAlertMolecule
|
||||||
import io.element.android.libraries.designsystem.components.ExpandableBottomSheetLayout
|
import io.element.android.libraries.designsystem.components.ExpandableBottomSheetLayout
|
||||||
import io.element.android.libraries.designsystem.components.ExpandableBottomSheetLayoutState
|
import io.element.android.libraries.designsystem.components.ExpandableBottomSheetLayoutState
|
||||||
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.avatar.AvatarType
|
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
|
||||||
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
|
||||||
import io.element.android.libraries.designsystem.components.rememberExpandableBottomSheetLayoutState
|
import io.element.android.libraries.designsystem.components.rememberExpandableBottomSheetLayoutState
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.text.toAnnotatedString
|
import io.element.android.libraries.designsystem.text.toAnnotatedString
|
||||||
import io.element.android.libraries.designsystem.theme.components.BottomSheetDragHandle
|
import io.element.android.libraries.designsystem.theme.components.BottomSheetDragHandle
|
||||||
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
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.Text
|
||||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
|
||||||
import io.element.android.libraries.designsystem.utils.HideKeyboardWhenDisposed
|
import io.element.android.libraries.designsystem.utils.HideKeyboardWhenDisposed
|
||||||
import io.element.android.libraries.designsystem.utils.KeepScreenOn
|
import io.element.android.libraries.designsystem.utils.KeepScreenOn
|
||||||
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
|
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
|
||||||
|
|
@ -113,14 +96,9 @@ import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
|
||||||
import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom
|
import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom
|
||||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
|
|
||||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
|
||||||
import io.element.android.libraries.textcomposer.model.TextEditorState
|
import io.element.android.libraries.textcomposer.model.TextEditorState
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
import io.element.android.wysiwyg.link.Link
|
import io.element.android.wysiwyg.link.Link
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
|
|
@ -517,154 +495,6 @@ private fun MessagesViewComposerBottomSheetContents(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
private fun MessagesViewTopBar(
|
|
||||||
roomName: String?,
|
|
||||||
roomAvatar: AvatarData,
|
|
||||||
isTombstoned: Boolean,
|
|
||||||
heroes: ImmutableList<AvatarData>,
|
|
||||||
roomCallState: RoomCallState,
|
|
||||||
dmUserIdentityState: IdentityState?,
|
|
||||||
onRoomDetailsClick: () -> Unit,
|
|
||||||
onJoinCallClick: () -> Unit,
|
|
||||||
onBackClick: () -> Unit,
|
|
||||||
) {
|
|
||||||
TopAppBar(
|
|
||||||
navigationIcon = {
|
|
||||||
BackButton(onClick = onBackClick)
|
|
||||||
},
|
|
||||||
title = {
|
|
||||||
val roundedCornerShape = RoundedCornerShape(8.dp)
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.clip(roundedCornerShape)
|
|
||||||
.clickable { onRoomDetailsClick() },
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
) {
|
|
||||||
val titleModifier = Modifier.weight(1f, fill = false)
|
|
||||||
RoomAvatarAndNameRow(
|
|
||||||
roomName = roomName,
|
|
||||||
roomAvatar = roomAvatar,
|
|
||||||
isTombstoned = isTombstoned,
|
|
||||||
heroes = heroes,
|
|
||||||
modifier = titleModifier
|
|
||||||
)
|
|
||||||
|
|
||||||
when (dmUserIdentityState) {
|
|
||||||
IdentityState.Verified -> {
|
|
||||||
Icon(
|
|
||||||
imageVector = CompoundIcons.Verified(),
|
|
||||||
tint = ElementTheme.colors.iconSuccessPrimary,
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
IdentityState.VerificationViolation -> {
|
|
||||||
Icon(
|
|
||||||
imageVector = CompoundIcons.ErrorSolid(),
|
|
||||||
tint = ElementTheme.colors.iconCriticalPrimary,
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions = {
|
|
||||||
CallMenuItem(
|
|
||||||
roomCallState = roomCallState,
|
|
||||||
onJoinCallClick = onJoinCallClick,
|
|
||||||
)
|
|
||||||
Spacer(Modifier.width(8.dp))
|
|
||||||
},
|
|
||||||
windowInsets = WindowInsets(0.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
private fun ThreadTopBar(
|
|
||||||
roomName: String?,
|
|
||||||
roomAvatarData: AvatarData,
|
|
||||||
heroes: ImmutableList<AvatarData>,
|
|
||||||
isTombstoned: Boolean,
|
|
||||||
onBackClick: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
TopAppBar(
|
|
||||||
modifier = modifier,
|
|
||||||
navigationIcon = {
|
|
||||||
BackButton(onClick = onBackClick)
|
|
||||||
},
|
|
||||||
title = {
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
||||||
Avatar(
|
|
||||||
avatarData = roomAvatarData,
|
|
||||||
avatarType = AvatarType.Room(
|
|
||||||
heroes = heroes,
|
|
||||||
isTombstoned = isTombstoned,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
.padding(horizontal = 8.dp)
|
|
||||||
.semantics {
|
|
||||||
heading()
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = stringResource(CommonStrings.common_thread),
|
|
||||||
style = ElementTheme.typography.fontBodyLgMedium,
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = roomName ?: stringResource(CommonStrings.common_no_room_name),
|
|
||||||
style = ElementTheme.typography.fontBodySmRegular,
|
|
||||||
fontStyle = FontStyle.Italic.takeIf { roomName == null },
|
|
||||||
color = ElementTheme.colors.textSecondary,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun RoomAvatarAndNameRow(
|
|
||||||
roomName: String?,
|
|
||||||
roomAvatar: AvatarData,
|
|
||||||
heroes: ImmutableList<AvatarData>,
|
|
||||||
isTombstoned: Boolean,
|
|
||||||
modifier: Modifier = Modifier
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = modifier,
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Avatar(
|
|
||||||
avatarData = roomAvatar,
|
|
||||||
avatarType = AvatarType.Room(
|
|
||||||
heroes = heroes,
|
|
||||||
isTombstoned = isTombstoned,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(horizontal = 8.dp)
|
|
||||||
.semantics {
|
|
||||||
heading()
|
|
||||||
},
|
|
||||||
text = roomName ?: stringResource(CommonStrings.common_no_room_name),
|
|
||||||
style = ElementTheme.typography.fontBodyLgMedium,
|
|
||||||
fontStyle = FontStyle.Italic.takeIf { roomName == null },
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun CantSendMessageBanner() {
|
private fun CantSendMessageBanner() {
|
||||||
Row(
|
Row(
|
||||||
|
|
@ -719,58 +549,3 @@ internal fun MessagesViewPreview(@PreviewParameter(MessagesStateProvider::class)
|
||||||
knockRequestsBannerView = {},
|
knockRequestsBannerView = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreviewsDayNight
|
|
||||||
@Composable
|
|
||||||
internal fun ThreadTopBarPreview() {
|
|
||||||
ElementPreview {
|
|
||||||
val name = "Room name"
|
|
||||||
val initialsAvatarData = AvatarData(
|
|
||||||
id = "id",
|
|
||||||
name = name,
|
|
||||||
url = null,
|
|
||||||
size = AvatarSize.TimelineRoom,
|
|
||||||
)
|
|
||||||
Column {
|
|
||||||
ThreadTopBar(
|
|
||||||
roomName = name,
|
|
||||||
roomAvatarData = initialsAvatarData,
|
|
||||||
heroes = persistentListOf(),
|
|
||||||
isTombstoned = false,
|
|
||||||
onBackClick = {},
|
|
||||||
)
|
|
||||||
HorizontalDivider()
|
|
||||||
ThreadTopBar(
|
|
||||||
roomName = name,
|
|
||||||
roomAvatarData = initialsAvatarData,
|
|
||||||
heroes = aMatrixUserList().map { it.getAvatarData(AvatarSize.TimelineRoom) }.toImmutableList(),
|
|
||||||
isTombstoned = false,
|
|
||||||
onBackClick = {},
|
|
||||||
)
|
|
||||||
HorizontalDivider()
|
|
||||||
ThreadTopBar(
|
|
||||||
roomName = null,
|
|
||||||
roomAvatarData = initialsAvatarData,
|
|
||||||
heroes = persistentListOf(),
|
|
||||||
isTombstoned = false,
|
|
||||||
onBackClick = {},
|
|
||||||
)
|
|
||||||
HorizontalDivider()
|
|
||||||
ThreadTopBar(
|
|
||||||
roomName = name,
|
|
||||||
roomAvatarData = initialsAvatarData.copy(url = "https://some-avatar.jpg"),
|
|
||||||
heroes = persistentListOf(),
|
|
||||||
isTombstoned = false,
|
|
||||||
onBackClick = {},
|
|
||||||
)
|
|
||||||
HorizontalDivider()
|
|
||||||
ThreadTopBar(
|
|
||||||
roomName = name,
|
|
||||||
roomAvatarData = initialsAvatarData,
|
|
||||||
heroes = persistentListOf(),
|
|
||||||
isTombstoned = true,
|
|
||||||
onBackClick = {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector 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.features.messages.impl.topbars
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.semantics.heading
|
||||||
|
import androidx.compose.ui.semantics.semantics
|
||||||
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.element.android.compound.theme.ElementTheme
|
||||||
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
|
import io.element.android.features.messages.impl.timeline.components.CallMenuItem
|
||||||
|
import io.element.android.features.roomcall.api.RoomCallState
|
||||||
|
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.AvatarType
|
||||||
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
|
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.TopAppBar
|
||||||
|
import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
|
||||||
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
internal fun MessagesViewTopBar(
|
||||||
|
roomName: String?,
|
||||||
|
roomAvatar: AvatarData,
|
||||||
|
isTombstoned: Boolean,
|
||||||
|
heroes: ImmutableList<AvatarData>,
|
||||||
|
roomCallState: RoomCallState,
|
||||||
|
dmUserIdentityState: IdentityState?,
|
||||||
|
onRoomDetailsClick: () -> Unit,
|
||||||
|
onJoinCallClick: () -> Unit,
|
||||||
|
onBackClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
TopAppBar(
|
||||||
|
modifier = modifier,
|
||||||
|
navigationIcon = {
|
||||||
|
BackButton(onClick = onBackClick)
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
val roundedCornerShape = RoundedCornerShape(8.dp)
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(roundedCornerShape)
|
||||||
|
.clickable { onRoomDetailsClick() },
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
val titleModifier = Modifier.weight(1f, fill = false)
|
||||||
|
RoomAvatarAndNameRow(
|
||||||
|
roomName = roomName,
|
||||||
|
roomAvatar = roomAvatar,
|
||||||
|
isTombstoned = isTombstoned,
|
||||||
|
heroes = heroes,
|
||||||
|
modifier = titleModifier
|
||||||
|
)
|
||||||
|
|
||||||
|
when (dmUserIdentityState) {
|
||||||
|
IdentityState.Verified -> {
|
||||||
|
Icon(
|
||||||
|
imageVector = CompoundIcons.Verified(),
|
||||||
|
tint = ElementTheme.colors.iconSuccessPrimary,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
IdentityState.VerificationViolation -> {
|
||||||
|
Icon(
|
||||||
|
imageVector = CompoundIcons.ErrorSolid(),
|
||||||
|
tint = ElementTheme.colors.iconCriticalPrimary,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
CallMenuItem(
|
||||||
|
roomCallState = roomCallState,
|
||||||
|
onJoinCallClick = onJoinCallClick,
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(8.dp))
|
||||||
|
},
|
||||||
|
windowInsets = WindowInsets(0.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun RoomAvatarAndNameRow(
|
||||||
|
roomName: String?,
|
||||||
|
roomAvatar: AvatarData,
|
||||||
|
heroes: ImmutableList<AvatarData>,
|
||||||
|
isTombstoned: Boolean,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Avatar(
|
||||||
|
avatarData = roomAvatar,
|
||||||
|
avatarType = AvatarType.Room(
|
||||||
|
heroes = heroes,
|
||||||
|
isTombstoned = isTombstoned,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 8.dp)
|
||||||
|
.semantics {
|
||||||
|
heading()
|
||||||
|
},
|
||||||
|
text = roomName ?: stringResource(CommonStrings.common_no_room_name),
|
||||||
|
style = ElementTheme.typography.fontBodyLgMedium,
|
||||||
|
fontStyle = FontStyle.Italic.takeIf { roomName == null },
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector 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.features.messages.impl.topbars
|
||||||
|
|
||||||
|
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.material3.ExperimentalMaterial3Api
|
||||||
|
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.semantics.heading
|
||||||
|
import androidx.compose.ui.semantics.semantics
|
||||||
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import io.element.android.compound.theme.ElementTheme
|
||||||
|
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.avatar.AvatarType
|
||||||
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
|
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.designsystem.theme.components.TopAppBar
|
||||||
|
import io.element.android.libraries.matrix.ui.components.aMatrixUserList
|
||||||
|
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||||
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
internal fun ThreadTopBar(
|
||||||
|
roomName: String?,
|
||||||
|
roomAvatarData: AvatarData,
|
||||||
|
heroes: ImmutableList<AvatarData>,
|
||||||
|
isTombstoned: Boolean,
|
||||||
|
onBackClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
TopAppBar(
|
||||||
|
modifier = modifier,
|
||||||
|
navigationIcon = {
|
||||||
|
BackButton(onClick = onBackClick)
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Avatar(
|
||||||
|
avatarData = roomAvatarData,
|
||||||
|
avatarType = AvatarType.Room(
|
||||||
|
heroes = heroes,
|
||||||
|
isTombstoned = isTombstoned,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 8.dp)
|
||||||
|
.semantics {
|
||||||
|
heading()
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(CommonStrings.common_thread),
|
||||||
|
style = ElementTheme.typography.fontBodyLgMedium,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = roomName ?: stringResource(CommonStrings.common_no_room_name),
|
||||||
|
style = ElementTheme.typography.fontBodySmRegular,
|
||||||
|
fontStyle = FontStyle.Italic.takeIf { roomName == null },
|
||||||
|
color = ElementTheme.colors.textSecondary,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreviewsDayNight
|
||||||
|
@Composable
|
||||||
|
internal fun ThreadTopBarPreview() = ElementPreview {
|
||||||
|
val name = "Room name"
|
||||||
|
val initialsAvatarData = AvatarData(
|
||||||
|
id = "id",
|
||||||
|
name = name,
|
||||||
|
url = null,
|
||||||
|
size = AvatarSize.TimelineRoom,
|
||||||
|
)
|
||||||
|
Column {
|
||||||
|
ThreadTopBar(
|
||||||
|
roomName = name,
|
||||||
|
roomAvatarData = initialsAvatarData,
|
||||||
|
heroes = persistentListOf(),
|
||||||
|
isTombstoned = false,
|
||||||
|
onBackClick = {},
|
||||||
|
)
|
||||||
|
HorizontalDivider()
|
||||||
|
ThreadTopBar(
|
||||||
|
roomName = name,
|
||||||
|
roomAvatarData = initialsAvatarData,
|
||||||
|
heroes = aMatrixUserList().map { it.getAvatarData(AvatarSize.TimelineRoom) }.toImmutableList(),
|
||||||
|
isTombstoned = false,
|
||||||
|
onBackClick = {},
|
||||||
|
)
|
||||||
|
HorizontalDivider()
|
||||||
|
ThreadTopBar(
|
||||||
|
roomName = null,
|
||||||
|
roomAvatarData = initialsAvatarData,
|
||||||
|
heroes = persistentListOf(),
|
||||||
|
isTombstoned = false,
|
||||||
|
onBackClick = {},
|
||||||
|
)
|
||||||
|
HorizontalDivider()
|
||||||
|
ThreadTopBar(
|
||||||
|
roomName = name,
|
||||||
|
roomAvatarData = initialsAvatarData.copy(url = "https://some-avatar.jpg"),
|
||||||
|
heroes = persistentListOf(),
|
||||||
|
isTombstoned = false,
|
||||||
|
onBackClick = {},
|
||||||
|
)
|
||||||
|
HorizontalDivider()
|
||||||
|
ThreadTopBar(
|
||||||
|
roomName = name,
|
||||||
|
roomAvatarData = initialsAvatarData,
|
||||||
|
heroes = persistentListOf(),
|
||||||
|
isTombstoned = true,
|
||||||
|
onBackClick = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue