Merge branch 'develop' into feature/fga/space_list_sdk_api

This commit is contained in:
ganfra 2025-09-24 15:21:40 +02:00 committed by GitHub
commit 6a5098fc2a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 477 additions and 315 deletions

View file

@ -1,3 +1,73 @@
Changes in Element X v25.09.2
=============================
## What's Changed
### ✨ Features
* Show progress dialog while we are sending invites in a room by @richvdh in https://github.com/element-hq/element-x-android/pull/5342
* Call: RTC decline event support by @BillCarsonFr in https://github.com/element-hq/element-x-android/pull/5305
* Add room info to the thread's top app bar by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5374
### 🙌 Improvements
* Use the new RtcNotification event instead of the now deprecated CallNotify by @BillCarsonFr in https://github.com/element-hq/element-x-android/pull/5357
### 🐛 Bugfixes
* Increase Element Call audio init delay ensuring the right audio device is used by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5315
* Do not center the dialog title text for dialogs with no icon by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5332
* Media viewer: release the `ExoPlayers` when the hosting composables are disposed by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5351
* Make PushData.clientSecret mandatory. by @bmarty in https://github.com/element-hq/element-x-android/pull/5369
* Cleanup ftue code and ensure verification confirmation is displayed by @bmarty in https://github.com/element-hq/element-x-android/pull/5379
* Change in clear cache behavior by @bmarty in https://github.com/element-hq/element-x-android/pull/5388
* fix (room navigation) : fix navigation when leaving room/space by @ganfra in https://github.com/element-hq/element-x-android/pull/5376
* fix (timeline) : forward pagination regression by @ganfra in https://github.com/element-hq/element-x-android/pull/5389
* When joining a call, wait for the `content_loaded` action by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5399
* Ensure the thread summary sender's display name won't wrap to the next line by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5403
### 🗣 Translations
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/5349
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/5385
### 🧱 Build
* Improve release script and the file Versions.kt by @bmarty in https://github.com/element-hq/element-x-android/pull/5318
* Dependency: extract the Matrix SDK and add instructions for upgrading the library by @bmarty in https://github.com/element-hq/element-x-android/pull/5363
* Add test on DefaultSpaceEntryPoint by @bmarty in https://github.com/element-hq/element-x-android/pull/5343
### 🚧 In development 🚧
* Space list by @bmarty in https://github.com/element-hq/element-x-android/pull/5320
* Feature : Join Space (WIP) by @ganfra in https://github.com/element-hq/element-x-android/pull/5378
### Dependency upgrades
* Update activity to v1.11.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5324
* Update dependency com.google.truth:truth to v1.4.5 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5322
* Update dependency io.sentry:sentry-android to v8.21.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5310
* Update dependency org.matrix.rustcomponents:sdk-android to v25.9.10 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5323
* Update dependency androidx.sqlite:sqlite-ktx to v2.6.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5337
* Update camera to v1.5.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5336
* Update dependency com.posthog:posthog-android to v3.21.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5333
* Update dependency com.google.testparameterinjector:test-parameter-injector to v1.19 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5341
* Upgrade Rust SDK bindings to v25.09.15 by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5353
* Update dependency org.matrix.rustcomponents:sdk-android to v25.9.16 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5359
* Update dependency org.matrix.rustcomponents:sdk-android to v25.9.18 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5365
* Update telephoto to v0.17.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5350
* Update dependency org.matrix.rustcomponents:sdk-android to v25.9.19 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5377
* Update dependency com.google.firebase:firebase-bom to v34.3.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5367
* Upgrade Element Call embedded dependency to `v0.16.0-rc.4` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5391
* Update dependencyAnalysis to v3 (major) by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5194
* Update dependency org.maplibre.gl:android-sdk to v11.13.5 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5381
* Update dependency org.matrix.rustcomponents:sdk-android to v25.9.23 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5396
* Update plugin dependencycheck to v12.1.5 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5382
* Update dependency io.sentry:sentry-android to v8.22.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5397
### Others
* Cleanup nodes by @bmarty in https://github.com/element-hq/element-x-android/pull/5358
* Complete test on MediaGalleryPresenter by @bmarty in https://github.com/element-hq/element-x-android/pull/5361
* Remove dead code by @bmarty in https://github.com/element-hq/element-x-android/pull/5306
* Introduce BugReportFlowNode, and remove NavTarget.ViewLogs from RootFlowNode by @bmarty in https://github.com/element-hq/element-x-android/pull/5370
* When logging out from Pin code screen, logout from all the sessions. by @bmarty in https://github.com/element-hq/element-x-android/pull/5372
* Clean MatrixAuthenticationService and SessionStore API by @bmarty in https://github.com/element-hq/element-x-android/pull/5371
* Add logs to detect duplicates in the room list by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5364
* Add troubleshoot notification test about blocked users by @bmarty in https://github.com/element-hq/element-x-android/pull/5394
* Add thread decoration with latest event details by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5355
* Rework on messages view top bars by @bmarty in https://github.com/element-hq/element-x-android/pull/5401
* Put developer settings at the end of the view by @p1gp1g in https://github.com/element-hq/element-x-android/pull/5387
## New Contributors
* @p1gp1g made their first contribution in https://github.com/element-hq/element-x-android/pull/5387
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v25.09.1...v25.09.2
Changes in Element X v25.09.1
=============================

View file

@ -0,0 +1,2 @@
Main changes in this version: bug fixes and improvements.
Full changelog: https://github.com/element-hq/element-x-android/releases

View file

@ -36,7 +36,6 @@ import io.element.android.features.messages.impl.timeline.protection.TimelinePro
import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState
import io.element.android.features.roomcall.api.RoomCallState
import io.element.android.features.roomcall.api.aStandByCallState
import io.element.android.features.roomcall.api.anOngoingCallState
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
import io.element.android.libraries.architecture.AsyncData
@ -60,36 +59,29 @@ open class MessagesStateProvider : PreviewParameterProvider<MessagesState> {
aMessagesState(composerState = aMessageComposerState(showAttachmentSourcePicker = true)),
aMessagesState(userEventPermissions = aUserEventPermissions(canSendMessage = false)),
aMessagesState(showReinvitePrompt = true),
aMessagesState(roomName = null),
aMessagesState(composerState = aMessageComposerState(showTextFormatting = true)),
aMessagesState(
voiceMessageComposerState = aVoiceMessageComposerState(showPermissionRationaleDialog = true),
),
aMessagesState(
roomCallState = anOngoingCallState(),
),
aMessagesState(
voiceMessageComposerState = aVoiceMessageComposerState(
voiceMessageState = aVoiceMessagePreviewState(),
showSendFailureDialog = true
),
),
aMessagesState(
roomCallState = aStandByCallState(canStartCall = false),
),
aMessagesState(
pinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(
knownPinnedMessagesCount = 4,
currentPinnedMessageIndex = 0,
),
),
aMessagesState(roomName = "A DM with a very looong name", dmUserVerificationState = IdentityState.Verified),
aMessagesState(roomName = "A DM with a very looong name", dmUserVerificationState = IdentityState.VerificationViolation),
aMessagesState(successorRoom = SuccessorRoom(RoomId("!id:domain"), null)),
aMessagesState(timelineState = aTimelineState(
timelineMode = Timeline.Mode.Thread(threadRootId = ThreadId("\$a-thread-id")),
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
)),
aMessagesState(
timelineState = aTimelineState(
timelineMode = Timeline.Mode.Thread(threadRootId = ThreadId("\$a-thread-id")),
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
)
),
)
}

View file

@ -11,12 +11,10 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
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.WindowInsets
import androidx.compose.foundation.layout.consumeWindowInsets
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.statusBars
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.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontStyle
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.unit.dp
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.impl.actionlist.ActionListEvents
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.TimelineEvents
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.CustomReactionEvents
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.ReadReceiptBottomSheetEvents
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.VoiceMessageSendingFailedDialog
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.designsystem.atomic.molecules.ComposerAlertMolecule
import io.element.android.libraries.designsystem.components.ExpandableBottomSheetLayout
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.rememberExpandableBottomSheetLayoutState
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
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.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.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.KeepScreenOn
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.timeline.Timeline
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.ui.strings.CommonStrings
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 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
private fun CantSendMessageBanner() {
Row(
@ -719,58 +549,3 @@ internal fun MessagesViewPreview(@PreviewParameter(MessagesStateProvider::class)
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 = {},
)
}
}
}

View file

@ -0,0 +1,211 @@
/*
* 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.Column
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.features.roomcall.api.aStandByCallState
import io.element.android.features.roomcall.api.anOngoingCallState
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.avatar.anAvatarData
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.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.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 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
)
}
}
@PreviewsDayNight
@Composable
internal fun MessagesViewTopBarPreview() = ElementPreview {
@Composable
fun AMessagesViewTopBar(
roomName: String? = "Room name",
roomAvatar: AvatarData = anAvatarData(
name = "Room name",
size = AvatarSize.TimelineRoom,
),
isTombstoned: Boolean = false,
heroes: ImmutableList<AvatarData> = persistentListOf(),
roomCallState: RoomCallState = RoomCallState.Unavailable,
dmUserIdentityState: IdentityState? = null,
) = MessagesViewTopBar(
roomName = roomName,
roomAvatar = roomAvatar,
isTombstoned = isTombstoned,
heroes = heroes,
roomCallState = roomCallState,
dmUserIdentityState = dmUserIdentityState,
onRoomDetailsClick = {},
onJoinCallClick = {},
onBackClick = {},
)
Column {
AMessagesViewTopBar()
HorizontalDivider()
AMessagesViewTopBar(
heroes = aMatrixUserList().map { it.getAvatarData(AvatarSize.TimelineRoom) }.toImmutableList(),
roomCallState = anOngoingCallState(),
)
HorizontalDivider()
AMessagesViewTopBar(
roomName = null,
roomCallState = anOngoingCallState(canJoinCall = false),
)
HorizontalDivider()
AMessagesViewTopBar(
roomName = "A DM with a very very very long name",
roomAvatar = anAvatarData(
size = AvatarSize.TimelineRoom,
url = "https://some-avatar.jpg"
),
roomCallState = aStandByCallState(canStartCall = false),
dmUserIdentityState = IdentityState.Verified
)
HorizontalDivider()
AMessagesViewTopBar(
roomName = "A DM with a very very very long name",
isTombstoned = true,
dmUserIdentityState = IdentityState.VerificationViolation
)
}
}

View file

@ -0,0 +1,135 @@
/*
* 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.avatar.anAvatarData
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 {
@Composable
fun AThreadTopBar(
roomName: String? = "Room name",
roomAvatarData: AvatarData = anAvatarData(
name = "Room name",
size = AvatarSize.TimelineRoom,
),
isTombstoned: Boolean = false,
heroes: ImmutableList<AvatarData> = persistentListOf(),
) = ThreadTopBar(
roomName = roomName,
roomAvatarData = roomAvatarData,
isTombstoned = isTombstoned,
heroes = heroes,
onBackClick = {},
)
Column {
AThreadTopBar()
HorizontalDivider()
AThreadTopBar(
heroes = aMatrixUserList().map { it.getAvatarData(AvatarSize.TimelineRoom) }.toImmutableList(),
)
HorizontalDivider()
AThreadTopBar(
roomName = null,
)
HorizontalDivider()
AThreadTopBar(
roomAvatarData = anAvatarData(
name = "Room name",
url = "https://some-avatar.jpg",
size = AvatarSize.TimelineRoom,
),
)
HorizontalDivider()
AThreadTopBar(
isTombstoned = true,
)
}
}

View file

@ -214,9 +214,6 @@ private fun ColumnScope.GeneralSection(
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Settings())),
onClick = onOpenAdvancedSettings,
)
if (state.showDeveloperSettings) {
DeveloperPreferencesView(onOpenDeveloperSettings)
}
ListItem(
headlineContent = { Text(stringResource(id = CommonStrings.action_signout)) },
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.SignOut())),
@ -231,6 +228,10 @@ private fun ColumnScope.GeneralSection(
onClick = onDeactivateClick,
)
}
// Put developer settings at the end, so nothing bad happens if the user clicks 8 times to enable the entry
if (state.showDeveloperSettings) {
DeveloperPreferencesView(onOpenDeveloperSettings)
}
}
@Composable

View file

@ -206,7 +206,7 @@ haze_materials = { module = "dev.chrisbanes.haze:haze-materials", version.ref =
# Analytics
posthog = "com.posthog:posthog-android:3.21.2"
sentry = "io.sentry:sentry-android:8.21.1"
sentry = "io.sentry:sentry-android:8.22.0"
# main branch can be tested replacing the version with main-SNAPSHOT
matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.28.0"
@ -241,7 +241,7 @@ metro = { id = "dev.zacsweers.metro", version.ref = "metro" }
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
ktlint = "org.jlleitschuh.gradle.ktlint:13.1.0"
dependencygraph = "com.savvasdalkitsis.module-dependency-graph:0.12"
dependencycheck = "org.owasp.dependencycheck:12.1.3"
dependencycheck = "org.owasp.dependencycheck:12.1.5"
dependencyanalysis = { id = "com.autonomousapps.dependency-analysis", version.ref = "dependencyAnalysis" }
paparazzi = "app.cash.paparazzi:2.0.0-alpha02"
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }

View file

@ -44,7 +44,7 @@ private const val versionMonth = 9
* Release number in the month. Value must be in [0,99].
* Do not update this value. it is updated by the release script.
*/
private const val versionReleaseNumber = 1
private const val versionReleaseNumber = 2
object Versions {
/**

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:065466dc414d77a58f2d22c26fc9d5e9b7afe5d5206cdbcc2bbdc6e8ea192d15
size 40479

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3c34b8c47d89231b8e5660726d63b94e3e8b97a2fc7e0b615f8f9e2d61066470
size 39172

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:617a819de3cd223323b0fa1853185ceb26ffb97a22d79524acec0f4d5b3a634b
size 33550

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6e146eee5e94e29a2b87b3d4a9bf248445e08dc6eb68c2268b6f263533675c78
size 33050

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e1ffac0c95378fa7778a4d5b3e5af5a47eef44757f13dbe0032d30b30f96561b
size 58549
oid sha256:9571176fb73b9871b6db3c30679a46c776badb56d4e1919ea882b391d893b8c4
size 53144

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:983db5846ee050f682b895ef0ce54362b1214c5bee48ef94139ef825e0ecbab6
size 61678

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:501b8bc5c6163c85d1aaee4c9b81c56610931b6d693421d87b5107b3389170ea
size 60829

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b116183b02d89b112d35ae86d68fdfcec0cb63e5ded20c611a152836090b19b2
size 60755

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0fd783aa6c219e5bdc3c03579bd823a286e6048581728a6786ee724ef458050c
size 63807

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9571176fb73b9871b6db3c30679a46c776badb56d4e1919ea882b391d893b8c4
size 53144

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:373b60146f1fe215eea0c559fbe9810bfe9b714f021e3dddbb649dd6105c5f43
size 59547
oid sha256:f6a08f9b69ec278f8706c8de459fd6d2747cef7ed51da32eb7239667d0805e58
size 55311

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f6a08f9b69ec278f8706c8de459fd6d2747cef7ed51da32eb7239667d0805e58
size 55311
oid sha256:1b298a6432190f54fdb824c7edc2f8cda30a746d2a13322793c84a5a3d42f5ea
size 59985

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1b298a6432190f54fdb824c7edc2f8cda30a746d2a13322793c84a5a3d42f5ea
size 59985
oid sha256:61add48c03d1f4df4bc9010826c4a6f5adaa312ef5f910bc0b3a77211a377f66
size 50651

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:34a70e62b687f5726073a2339a4246f0e6bf31c187483829082612be69938087
size 60107
oid sha256:983db5846ee050f682b895ef0ce54362b1214c5bee48ef94139ef825e0ecbab6
size 61678

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:61add48c03d1f4df4bc9010826c4a6f5adaa312ef5f910bc0b3a77211a377f66
size 50651
oid sha256:0fd783aa6c219e5bdc3c03579bd823a286e6048581728a6786ee724ef458050c
size 63807

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:edc830108c2dd54d594a383020c2e73dba6b6fdff44e279b87889f3dc84ea546
size 56308
oid sha256:d6ea6d7eb98c546dbee109d160e2adbbbd4d22d4844835667bfbab1de2060115
size 52351

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bf12305ee99f199905e94e325743b1589f11d36815e16e86558691400e2991e3
size 59022

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e1b50937116f9d14103a1753199686f959371b8fe6ad006cadf1c3fb121fb72a
size 58491

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8f5c6283d080341dd600f723329355447fa190c8b36aa4241536c4993ce55b76
size 58466

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:01ea3893df495012f2e417e81d2223696ee11efe2499c0c2a112f015c1f72f7a
size 64610

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d6ea6d7eb98c546dbee109d160e2adbbbd4d22d4844835667bfbab1de2060115
size 52351

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:21ef350495865cd55510b9dd59ffcb7fa3600369df3077322c352e47cc8d93b1
size 57291
oid sha256:c1534b9c3b6952c290ca20a2853e56be023e5c8129a10b9f3fd63c290c7ba9fb
size 52977

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c1534b9c3b6952c290ca20a2853e56be023e5c8129a10b9f3fd63c290c7ba9fb
size 52977
oid sha256:7b50e874595cfcb65cf2a4f7146ed7c99ee06899d6512024b900a64439748a6f
size 54078

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7b50e874595cfcb65cf2a4f7146ed7c99ee06899d6512024b900a64439748a6f
size 54078
oid sha256:aafdc477f090674d0473076071049bba77bc57025c602cb6bc1cdfada03a1937
size 44740

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b5052598f1d095bb854522a2b0e880a6a88c070e372e1a2fd73b487611f8f733
size 57716
oid sha256:bf12305ee99f199905e94e325743b1589f11d36815e16e86558691400e2991e3
size 59022

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:aafdc477f090674d0473076071049bba77bc57025c602cb6bc1cdfada03a1937
size 44740
oid sha256:01ea3893df495012f2e417e81d2223696ee11efe2499c0c2a112f015c1f72f7a
size 64610

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:10ef1d5b008da94cdf48525d98bdb1e10f6013124c499f67c9fcd39bee85b7aa
size 33773

View file

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d98bfa7c506d089ae16b2f35ffa4e189ae1a31ca114333d755fd837b13128759
size 32921

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a3329563aa6c438335aaadb18cc2767d78eb16c34b616c5e9b14f0d8b66fb032
size 38106
oid sha256:b5c95596f8a3e78692c7fa13b95a3491d320e4a89273c61cc22595817bf4e846
size 38104

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a7b3d3f464858c9a298461aad3d3a329ac894fbd6206cbc73ec0de16d20e69e3
size 37946
oid sha256:7cfb090fcb56f52c935f72ec52659f574aa088a663a991c7cd49688d42001388
size 37944

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f0ba38204b90b53b0b03efabc5dd69200089327c22250e9416d8ee2fd2b94537
size 38915
oid sha256:b092653f3c1192c3c2e0f0eb8803d20a0879af6bdcde2d51f6f6d4894ccc7c52
size 38914

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cb9b09711d4538aa350d9c072b31becb223cd2b842d606f405b208e3cc13b77c
oid sha256:78411c84878a5f97b2208b7d688094148fbf88660b1b8fbc0e5fa7b2aebbbf5b
size 38968

View file

@ -146,7 +146,7 @@ printf "Creating fastlane file...\n"
printf -v versionReleaseNumber2Digits "%02d" "${versionReleaseNumber}"
fastlaneFile="20${versionYear}${versionMonth}${versionReleaseNumber2Digits}0.txt"
fastlanePathFile="./fastlane/metadata/android/en-US/changelogs/${fastlaneFile}"
printf "Main changes in this version: TODO.\nFull changelog: https://github.com/element-hq/element-x-android/releases" > "${fastlanePathFile}"
printf "Main changes in this version: bug fixes and improvements.\nFull changelog: https://github.com/element-hq/element-x-android/releases" > "${fastlanePathFile}"
read -r -p "I have created the file ${fastlanePathFile}, please edit it and press enter to continue. "
git add "${fastlanePathFile}"