diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt index b045d10199..c66ec1ee51 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt @@ -9,6 +9,7 @@ package io.element.android.features.messages.impl import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import kotlinx.collections.immutable.ImmutableList @@ -19,4 +20,5 @@ interface MessagesNavigator { fun onReportContentClick(eventId: EventId, senderId: UserId) fun onEditPollClick(eventId: EventId) fun onPreviewAttachment(attachments: ImmutableList) + fun onNavigateToRoom(roomId: RoomId) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index fc6d01d3a9..84d20fbf94 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -49,18 +49,21 @@ import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.designsystem.utils.OnLifecycleEvent +import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.matrix.api.analytics.toAnalyticsViewRoom import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.permalink.PermalinkData import io.element.android.libraries.matrix.api.permalink.PermalinkParser import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.alias.matches import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.mediaplayer.api.MediaPlayer +import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.api.AnalyticsService import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.CoroutineScope @@ -70,6 +73,7 @@ import kotlinx.coroutines.launch class MessagesNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, + @ApplicationContext private val context: Context, @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, private val room: BaseRoom, @@ -157,7 +161,7 @@ class MessagesNode @AssistedInject constructor( callbacks.forEach { it.onUserDataClick(permalink.userId) } } is PermalinkData.RoomLink -> { - handleRoomLinkClick(activity, permalink, eventSink) + handleRoomLinkClick(permalink, eventSink) } is PermalinkData.FallbackLink -> { if (customTab) { @@ -173,7 +177,6 @@ class MessagesNode @AssistedInject constructor( } private fun handleRoomLinkClick( - context: Context, roomLink: PermalinkData.RoomLink, eventSink: (TimelineEvents) -> Unit, ) { @@ -183,7 +186,7 @@ class MessagesNode @AssistedInject constructor( eventSink(TimelineEvents.FocusOnEvent(eventId)) } else { // Click on the same room, ignore - context.toast("Already viewing this room!") + displaySameRoomToast() } } else { callbacks.forEach { it.onPermalinkClick(roomLink) } @@ -210,6 +213,15 @@ class MessagesNode @AssistedInject constructor( callbacks.forEach { it.onPreviewAttachments(attachments) } } + override fun onNavigateToRoom(roomId: RoomId) { + if (roomId == room.roomId) { + displaySameRoomToast() + } else { + val permalinkData = PermalinkData.RoomLink(roomId.toRoomIdOrAlias()) + callbacks.forEach { it.onPermalinkClick(permalinkData) } + } + } + private fun onViewAllPinnedMessagesClick() { callbacks.forEach { it.onViewAllPinnedEvents() } } @@ -230,6 +242,10 @@ class MessagesNode @AssistedInject constructor( callbacks.forEach { it.onViewKnockRequests() } } + private fun displaySameRoomToast() { + context.toast(CommonStrings.screen_room_permalink_same_room_android) + } + @Composable override fun View(modifier: Modifier) { val activity = requireNotNull(LocalActivity.current) @@ -255,13 +271,13 @@ class MessagesNode @AssistedInject constructor( onCreatePollClick = this::onCreatePollClick, onJoinCallClick = this::onJoinCallClick, onViewAllPinnedMessagesClick = this::onViewAllPinnedMessagesClick, + modifier = modifier, knockRequestsBannerView = { knockRequestsBannerRenderer.View( modifier = Modifier, onViewRequestsClick = this::onViewKnockRequestsClick ) }, - modifier = modifier, ) roomMemberModerationRenderer.Render( state = state.roomMemberModerationState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index 74cb9799f2..aa0d55e29b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -270,6 +270,7 @@ class MessagesPresenter @AssistedInject constructor( pinnedMessagesBannerState = pinnedMessagesBannerState, dmUserVerificationState = dmUserVerificationState, roomMemberModerationState = roomMemberModerationState, + successorRoom = roomInfo.successorRoom, eventSink = { handleEvents(it) } ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt index 8ba2cbd039..5247cb2f5e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt @@ -26,6 +26,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.encryption.identity.IdentityState +import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom import kotlinx.collections.immutable.ImmutableList @Immutable @@ -56,5 +57,6 @@ data class MessagesState( val pinnedMessagesBannerState: PinnedMessagesBannerState, val dmUserVerificationState: IdentityState?, val roomMemberModerationState: RoomMemberModerationState, + val successorRoom: SuccessorRoom?, val eventSink: (MessagesEvents) -> Unit ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index c366a1e4cf..62dd6a15d7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -44,6 +44,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.encryption.identity.IdentityState +import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.libraries.textcomposer.model.aTextEditorStateRich import kotlinx.collections.immutable.persistentListOf @@ -87,6 +88,7 @@ open class MessagesStateProvider : PreviewParameterProvider { ), aMessagesState(roomName = AsyncData.Success("A DM with a very looong name"), dmUserVerificationState = IdentityState.Verified), aMessagesState(roomName = AsyncData.Success("A DM with a very looong name"), dmUserVerificationState = IdentityState.VerificationViolation), + aMessagesState(successorRoom = SuccessorRoom(RoomId("!id:domain"), null)), ) } @@ -119,6 +121,7 @@ fun aMessagesState( pinnedMessagesBannerState: PinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(), dmUserVerificationState: IdentityState? = null, roomMemberModerationState: RoomMemberModerationState = aRoomMemberModerationState(), + successorRoom: SuccessorRoom? = null, eventSink: (MessagesEvents) -> Unit = {}, ) = MessagesState( roomId = RoomId("!id:domain"), @@ -147,6 +150,7 @@ fun aMessagesState( pinnedMessagesBannerState = pinnedMessagesBannerState, dmUserVerificationState = dmUserVerificationState, roomMemberModerationState = roomMemberModerationState, + successorRoom = successorRoom, eventSink = eventSink, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index ee76562b2c..02e9f77768 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -82,6 +82,7 @@ import io.element.android.features.messages.impl.voicemessages.composer.VoiceMes 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.atomic.molecules.IconTitlePlaceholdersRowMolecule import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize @@ -90,6 +91,7 @@ import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog import io.element.android.libraries.designsystem.preview.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.Icon import io.element.android.libraries.designsystem.theme.components.Scaffold @@ -101,8 +103,10 @@ import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.encryption.identity.IdentityState +import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.textcomposer.model.TextEditorState import io.element.android.libraries.ui.strings.CommonStrings @@ -211,8 +215,8 @@ fun MessagesView( onMessageLongClick = ::onMessageLongClick, onUserDataClick = { hidingKeyboard { - state.eventSink(MessagesEvents.OnUserClicked(it)) - } + state.eventSink(MessagesEvents.OnUserClicked(it)) + } }, onLinkClick = { link, customTab -> if (customTab) { @@ -410,6 +414,9 @@ private fun MessagesViewContent( MessagesViewComposerBottomSheetContents( subcomposing = subcomposing, state = state, + onRoomSuccessorClick = { roomId -> + state.timelineState.eventSink(TimelineEvents.NavigateToRoom(roomId = roomId)) + }, onLinkClick = { url, customTab -> onLinkClick(Link(url), customTab) }, ) }, @@ -424,52 +431,59 @@ private fun MessagesViewContent( private fun MessagesViewComposerBottomSheetContents( subcomposing: Boolean, state: MessagesState, + onRoomSuccessorClick: (RoomId) -> Unit, onLinkClick: (String, Boolean) -> Unit, ) { - if (state.userEventPermissions.canSendMessage) { - Column(modifier = Modifier.fillMaxWidth()) { - SuggestionsPickerView( - modifier = Modifier - .heightIn(max = 230.dp) - // Consume all scrolling, preventing the bottom sheet from being dragged when interacting with the list of suggestions - .nestedScroll(object : NestedScrollConnection { - override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset { - return available - } - }), - roomId = state.roomId, - roomName = state.roomName.dataOrNull(), - roomAvatarData = state.roomAvatar.dataOrNull(), - suggestions = state.composerState.suggestions, - onSelectSuggestion = { - state.composerState.eventSink(MessageComposerEvents.InsertSuggestion(it)) + when { + state.successorRoom != null -> { + SuccessorRoomBanner(roomSuccessor = state.successorRoom, onRoomSuccessorClick = onRoomSuccessorClick) + } + state.userEventPermissions.canSendMessage -> { + Column(modifier = Modifier.fillMaxWidth()) { + SuggestionsPickerView( + modifier = Modifier + .heightIn(max = 230.dp) + // Consume all scrolling, preventing the bottom sheet from being dragged when interacting with the list of suggestions + .nestedScroll(object : NestedScrollConnection { + override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset { + return available + } + }), + roomId = state.roomId, + roomName = state.roomName.dataOrNull(), + roomAvatarData = state.roomAvatar.dataOrNull(), + suggestions = state.composerState.suggestions, + onSelectSuggestion = { + state.composerState.eventSink(MessageComposerEvents.InsertSuggestion(it)) + } + ) + // Do not show the identity change if user is composing a Rich message or is seeing suggestion(s). + if (state.composerState.suggestions.isEmpty() && + state.composerState.textEditorState is TextEditorState.Markdown) { + IdentityChangeStateView( + state = state.identityChangeState, + onLinkClick = onLinkClick, + ) + } + val verificationViolation = state.identityChangeState.roomMemberIdentityStateChanges.firstOrNull { + it.identityState == IdentityState.VerificationViolation + } + if (verificationViolation != null) { + DisabledComposerView(modifier = Modifier.fillMaxWidth()) + } else { + MessageComposerView( + state = state.composerState, + voiceMessageState = state.voiceMessageComposerState, + subcomposing = subcomposing, + enableVoiceMessages = state.enableVoiceMessages, + modifier = Modifier.fillMaxWidth(), + ) } - ) - // Do not show the identity change if user is composing a Rich message or is seeing suggestion(s). - if (state.composerState.suggestions.isEmpty() && - state.composerState.textEditorState is TextEditorState.Markdown) { - IdentityChangeStateView( - state = state.identityChangeState, - onLinkClick = onLinkClick, - ) - } - val verificationViolation = state.identityChangeState.roomMemberIdentityStateChanges.firstOrNull { - it.identityState == IdentityState.VerificationViolation - } - if (verificationViolation != null) { - DisabledComposerView(modifier = Modifier.fillMaxWidth()) - } else { - MessageComposerView( - state = state.composerState, - voiceMessageState = state.voiceMessageComposerState, - subcomposing = subcomposing, - enableVoiceMessages = state.enableVoiceMessages, - modifier = Modifier.fillMaxWidth(), - ) } } - } else { - CantSendMessageBanner() + else -> { + CantSendMessageBanner() + } } } @@ -572,9 +586,9 @@ private fun RoomAvatarAndNameRow( private fun CantSendMessageBanner() { Row( modifier = Modifier - .fillMaxWidth() - .background(ElementTheme.colors.bgSubtleSecondary) - .padding(16.dp), + .fillMaxWidth() + .background(ElementTheme.colors.bgSubtleSecondary) + .padding(16.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center ) { @@ -588,6 +602,22 @@ private fun CantSendMessageBanner() { } } +@Composable +private fun SuccessorRoomBanner( + roomSuccessor: SuccessorRoom, + onRoomSuccessorClick: (RoomId) -> Unit, + modifier: Modifier = Modifier, +) { + ComposerAlertMolecule( + avatar = null, + content = stringResource(R.string.screen_room_timeline_tombstoned_room_message).toAnnotatedString(), + onSubmitClick = { onRoomSuccessorClick(roomSuccessor.roomId) }, + modifier = modifier, + isCritical = false, + submitText = stringResource(R.string.screen_room_timeline_tombstoned_room_action) + ) +} + @PreviewsDayNight @Composable internal fun MessagesViewPreview(@PreviewParameter(MessagesStateProvider::class) state: MessagesState) = ElementPreview { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt index a7901ff52d..1c9e18c9e6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenter.kt @@ -106,7 +106,8 @@ class PinnedMessagesListPresenter @AssistedInject constructor( renderTypingNotifications = false, typingMembers = persistentListOf(), reserveSpace = false, - ) + ), + predecessorRoom = room.predecessorRoom(), ) } val timelineProtectionState = timelineProtectionPresenter.present() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt index 464a5caa22..da6837df21 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt @@ -9,6 +9,7 @@ package io.element.android.features.messages.impl.timeline import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield import kotlin.time.Duration @@ -30,6 +31,7 @@ sealed interface TimelineEvents { data class ComputeVerifiedUserSendFailure(val event: TimelineItem.Event) : EventFromTimelineItem data class ShowShieldDialog(val messageShield: MessageShield) : EventFromTimelineItem data class LoadMore(val direction: Timeline.PaginationDirection) : EventFromTimelineItem + data class NavigateToRoom(val roomId: RoomId) : EventFromTimelineItem /** * Events coming from a poll item. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 4684b8cfdd..d8c43e7de7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -178,6 +178,9 @@ class TimelinePresenter @AssistedInject constructor( is TimelineEvents.ComputeVerifiedUserSendFailure -> { resolveVerifiedUserSendFailureState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(event.event)) } + is TimelineEvents.NavigateToRoom -> { + navigator.onNavigateToRoom(event.roomId) + } } } @@ -257,8 +260,9 @@ class TimelinePresenter @AssistedInject constructor( userHasPermissionToSendMessage = userHasPermissionToSendMessage, userHasPermissionToSendReaction = userHasPermissionToSendReaction, roomCallState = roomCallState, - pinnedEventIds = roomInfo.pinnedEventIds.orEmpty(), + pinnedEventIds = roomInfo.pinnedEventIds, typingNotificationState = typingNotificationState, + predecessorRoom = room.predecessorRoom(), ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt index af4b7622fe..a3cdb34c94 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt @@ -15,6 +15,7 @@ import io.element.android.features.messages.impl.typing.TypingNotificationState import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UniqueId +import io.element.android.libraries.matrix.api.room.tombstone.PredecessorRoom import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield import kotlinx.collections.immutable.ImmutableList import kotlin.time.Duration @@ -77,4 +78,5 @@ data class TimelineRoomInfo( val roomCallState: RoomCallState, val pinnedEventIds: List, val typingNotificationState: TypingNotificationState, + val predecessorRoom: PredecessorRoom?, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index d47379da3d..8bb66b4f60 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt @@ -30,6 +30,7 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.room.tombstone.PredecessorRoom import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield @@ -246,6 +247,7 @@ internal fun aTimelineRoomInfo( userHasPermissionToSendMessage: Boolean = true, pinnedEventIds: List = emptyList(), typingNotificationState: TypingNotificationState = aTypingNotificationState(), + predecessorRoom: PredecessorRoom? = null, ) = TimelineRoomInfo( isDm = isDm, name = name, @@ -254,4 +256,5 @@ internal fun aTimelineRoomInfo( roomCallState = aStandByCallState(), pinnedEventIds = pinnedEventIds, typingNotificationState = typingNotificationState, + predecessorRoom = predecessorRoom, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemVirtualRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemVirtualRow.kt index 2507fb9f01..6f49bffab1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemVirtualRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemVirtualRow.kt @@ -41,7 +41,15 @@ fun TimelineItemVirtualRow( when (virtual.model) { is TimelineItemDaySeparatorModel -> TimelineItemDaySeparatorView(virtual.model) TimelineItemReadMarkerModel -> TimelineItemReadMarkerView() - TimelineItemRoomBeginningModel -> TimelineItemRoomBeginningView(roomName = timelineRoomInfo.name) + TimelineItemRoomBeginningModel -> { + TimelineItemRoomBeginningView( + predecessorRoom = timelineRoomInfo.predecessorRoom, + roomName = timelineRoomInfo.name, + onPredecessorRoomClick = { roomId -> + eventSink(TimelineEvents.NavigateToRoom(roomId)) + }, + ) + } is TimelineItemLoadingIndicatorModel -> { TimelineLoadingMoreIndicator(virtual.model.direction) val latestEventSink by rememberUpdatedState(eventSink) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt index 645f36602c..dccb1433fc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt @@ -7,6 +7,7 @@ package io.element.android.features.messages.impl.timeline.components.virtual +import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth @@ -19,44 +20,74 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.R +import io.element.android.libraries.designsystem.atomic.molecules.ComposerAlertMolecule 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.Text +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.tombstone.PredecessorRoom @Composable fun TimelineItemRoomBeginningView( roomName: String?, + predecessorRoom: PredecessorRoom?, + onPredecessorRoomClick: (RoomId) -> Unit, modifier: Modifier = Modifier ) { - Box( - modifier = modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 8.dp), - contentAlignment = Alignment.Center, + Column( + modifier = modifier.fillMaxWidth() ) { - val text = if (roomName == null) { - stringResource(id = R.string.screen_room_timeline_beginning_of_room_no_name) - } else { - stringResource(id = R.string.screen_room_timeline_beginning_of_room, roomName) + if (predecessorRoom != null) { + ComposerAlertMolecule( + avatar = null, + content = stringResource(R.string.screen_room_timeline_upgraded_room_message).toAnnotatedString(), + onSubmitClick = { onPredecessorRoomClick(predecessorRoom.roomId) }, + isCritical = false, + submitText = stringResource(R.string.screen_room_timeline_upgraded_room_action) + ) + } + + Box( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + contentAlignment = Alignment.Center, + ) { + val text = if (roomName == null) { + stringResource(id = R.string.screen_room_timeline_beginning_of_room_no_name) + } else { + stringResource(id = R.string.screen_room_timeline_beginning_of_room, roomName) + } + Text( + color = ElementTheme.colors.textSecondary, + style = ElementTheme.typography.fontBodyMdRegular, + text = text, + textAlign = TextAlign.Center, + ) } - Text( - color = ElementTheme.colors.textSecondary, - style = ElementTheme.typography.fontBodyMdRegular, - text = text, - textAlign = TextAlign.Center, - ) } } @PreviewsDayNight @Composable internal fun TimelineItemRoomBeginningViewPreview() = ElementPreview { - Column { + Column(verticalArrangement = spacedBy(16.dp)) { TimelineItemRoomBeginningView( + predecessorRoom = null, roomName = null, + onPredecessorRoomClick = {}, ) TimelineItemRoomBeginningView( + predecessorRoom = null, roomName = "Room Name", + onPredecessorRoomClick = {}, + ) + TimelineItemRoomBeginningView( + predecessorRoom = PredecessorRoom(RoomId("!roomId:matrix.org"), EventId("\$eventId:matrix.org")), + roomName = "Room Name", + onPredecessorRoomClick = {}, ) } } diff --git a/features/messages/impl/src/main/res/values-cs/translations.xml b/features/messages/impl/src/main/res/values-cs/translations.xml index af33e7376d..3b43f897e2 100644 --- a/features/messages/impl/src/main/res/values-cs/translations.xml +++ b/features/messages/impl/src/main/res/values-cs/translations.xml @@ -43,6 +43,10 @@ "%1$d změny místnosti" "%1$d změn místnosti" + "Přejít do nové místnosti" + "Tato místnost byla nahrazena a již není aktivní" + "Zobrazit staré zprávy" + "Tato místnost je pokračováním jiné místnosti" "%1$s, %2$s a %3$d další" "%1$s, %2$s a %3$d další" diff --git a/features/messages/impl/src/main/res/values/localazy.xml b/features/messages/impl/src/main/res/values/localazy.xml index 704ba6ed05..984eb1231f 100644 --- a/features/messages/impl/src/main/res/values/localazy.xml +++ b/features/messages/impl/src/main/res/values/localazy.xml @@ -51,6 +51,10 @@ "%1$d room change" "%1$d room changes" + "Jump to new room" + "This room has been replaced and is no longer active" + "See old messages" + "This room is a continuation of another room" "%1$s, %2$s and %3$d other" "%1$s, %2$s and %3$d others" diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt index 92fc0d8171..32638d7b8d 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt @@ -9,6 +9,7 @@ package io.element.android.features.messages.impl import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.tests.testutils.lambda.lambdaError @@ -20,6 +21,7 @@ class FakeMessagesNavigator( private val onReportContentClickLambda: (eventId: EventId, senderId: UserId) -> Unit = { _, _ -> lambdaError() }, private val onEditPollClickLambda: (eventId: EventId) -> Unit = { _ -> lambdaError() }, private val onPreviewAttachmentLambda: (attachments: ImmutableList) -> Unit = { _ -> lambdaError() }, + private val onNavigateToRoomLambda: (roomId: RoomId) -> Unit = { _ -> lambdaError() } ) : MessagesNavigator { override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { onShowEventDebugInfoClickLambda(eventId, debugInfo) @@ -40,4 +42,8 @@ class FakeMessagesNavigator( override fun onPreviewAttachment(attachments: ImmutableList) { onPreviewAttachmentLambda(attachments) } + + override fun onNavigateToRoom(roomId: RoomId) { + onNavigateToRoomLambda(roomId) + } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index e42941da0d..2271f6a7d1 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -50,6 +50,7 @@ import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.media.MediaSource @@ -57,6 +58,7 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkParser import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.RoomMembersState import io.element.android.libraries.matrix.api.room.RoomMembershipState +import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId @@ -1130,6 +1132,57 @@ class MessagesPresenterTest { } } + @Test + fun `present - room with successor room includes successor info in state`() = runTest { + val successorRoomId = RoomId("!successor:server.org") + val successorReason = "This room has been moved to a new location" + val room = FakeJoinedRoom( + baseRoom = FakeBaseRoom( + canUserSendMessageResult = { _, _ -> Result.success(true) }, + canRedactOwnResult = { Result.success(true) }, + canRedactOtherResult = { Result.success(true) }, + canUserJoinCallResult = { Result.success(true) }, + canUserPinUnpinResult = { Result.success(true) }, + initialRoomInfo = aRoomInfo( + successorRoom = SuccessorRoom( + roomId = successorRoomId, + reason = successorReason + ) + ) + ), + typingNoticeResult = { Result.success(Unit) }, + ) + val presenter = createMessagesPresenter(joinedRoom = room) + presenter.testWithLifecycleOwner { + skipItems(1) + val initialState = awaitItem() + assertThat(initialState.successorRoom).isNotNull() + assertThat(initialState.successorRoom?.roomId).isEqualTo(successorRoomId) + assertThat(initialState.successorRoom?.reason).isEqualTo(successorReason) + } + } + + @Test + fun `present - room without successor room has null successor info in state`() = runTest { + val room = FakeJoinedRoom( + baseRoom = FakeBaseRoom( + canUserSendMessageResult = { _, _ -> Result.success(true) }, + canRedactOwnResult = { Result.success(true) }, + canRedactOtherResult = { Result.success(true) }, + canUserJoinCallResult = { Result.success(true) }, + canUserPinUnpinResult = { Result.success(true) }, + initialRoomInfo = aRoomInfo(successorRoom = null) + ), + typingNoticeResult = { Result.success(Unit) }, + ) + val presenter = createMessagesPresenter(joinedRoom = room) + presenter.testWithLifecycleOwner { + skipItems(1) + val initialState = awaitItem() + assertThat(initialState.successorRoom).isNull() + } + } + @Test fun `present - when room is encrypted and a DM, the DM user's identity state is fetched onResume`() = runTest { val room = FakeJoinedRoom( diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt index d433614537..5ff6b7e874 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt @@ -52,7 +52,9 @@ import io.element.android.features.messages.impl.timeline.components.receipt.aRe 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.event.aTimelineItemTextContent +import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom import io.element.android.libraries.matrix.api.timeline.item.event.getAvatarUrl import io.element.android.libraries.matrix.api.timeline.item.event.getDisplayName import io.element.android.libraries.matrix.api.user.MatrixUser @@ -65,6 +67,7 @@ import io.element.android.tests.testutils.EnsureNeverCalledWithParam import io.element.android.tests.testutils.EnsureNeverCalledWithTwoParams import io.element.android.tests.testutils.EnsureNeverCalledWithTwoParamsAndResult import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.assertNoNodeWithText import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.ensureCalledOnce import io.element.android.tests.testutils.pressBack @@ -560,6 +563,36 @@ class MessagesViewTest { rule.onNodeWithText("This is a pinned message").performClick() eventsRecorder.assertSingle(TimelineEvents.FocusOnEvent(AN_EVENT_ID, debounce = FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS.milliseconds)) } + + @Test + fun `clicking on successor room button emits expected event`() { + val eventsRecorder = EventsRecorder() + val successorRoomId = RoomId("!successor:server.org") + val state = aMessagesState( + successorRoom = SuccessorRoom( + roomId = successorRoomId, + reason = "This room has been upgraded" + ), + timelineState = aTimelineState(eventSink = eventsRecorder) + ) + rule.setMessagesView(state = state) + val text = rule.activity.getString(R.string.screen_room_timeline_tombstoned_room_action) + // The bottomsheet subcompose seems to make the node to appear twice + rule.onAllNodesWithText(text).onFirst().performClick() + eventsRecorder.assertSingle(TimelineEvents.NavigateToRoom(successorRoomId)) + } + + @Test + fun `no banner shown when there is no successor room`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + val state = aMessagesState( + successorRoom = null, + eventSink = eventsRecorder + ) + rule.setMessagesView(state = state) + rule.assertNoNodeWithText(R.string.screen_room_timeline_tombstoned_room_message) + rule.assertNoNodeWithText(R.string.screen_room_timeline_tombstoned_room_action) + } } private fun AndroidComposeTestRule.setMessagesView( diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt index a95cf4f31f..2cc7d9764a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt @@ -32,6 +32,7 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.room.RoomMembersState +import io.element.android.libraries.matrix.api.room.tombstone.PredecessorRoom import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.Timeline @@ -64,6 +65,7 @@ import io.element.android.tests.testutils.test import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf @@ -80,7 +82,7 @@ import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds @Suppress("LargeClass") -@OptIn(ExperimentalCoroutinesApi::class) +@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class) class TimelinePresenterTest { @get:Rule val warmUpRule = WarmUpRule() @@ -705,6 +707,73 @@ class TimelinePresenterTest { } } + @Test + fun `present - timeline room info includes predecessor room when room has predecessor`() = runTest { + val predecessorRoomId = RoomId("!predecessor:server.org") + val predecessorEventId = EventId("\$predecessorEvent:server.org") + val predecessorRoom = PredecessorRoom( + roomId = predecessorRoomId, + lastEventId = predecessorEventId + ) + + val room = FakeJoinedRoom( + baseRoom = FakeBaseRoom( + canUserSendMessageResult = { _, _ -> Result.success(true) }, + predecessorRoomResult = { predecessorRoom } + ), + ) + + val presenter = createTimelinePresenter(room = room) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitFirstItem() + assertThat(initialState.timelineRoomInfo.predecessorRoom).isNotNull() + assertThat(initialState.timelineRoomInfo.predecessorRoom?.roomId).isEqualTo(predecessorRoomId) + assertThat(initialState.timelineRoomInfo.predecessorRoom?.lastEventId).isEqualTo(predecessorEventId) + } + } + + @Test + fun `present - timeline room info no predecessor`() = runTest { + val room = FakeJoinedRoom( + baseRoom = FakeBaseRoom( + canUserSendMessageResult = { _, _ -> Result.success(true) }, + predecessorRoomResult = { null } + ), + ) + val presenter = createTimelinePresenter(room = room) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitFirstItem() + assertThat(initialState.timelineRoomInfo.predecessorRoom).isNull() + } + } + + @Test + fun `present - timeline event navigate to room`() = runTest { + val room = FakeJoinedRoom( + baseRoom = FakeBaseRoom( + canUserSendMessageResult = { _, _ -> Result.success(true) }, + ), + ) + val onNavigateToRoomLambda = lambdaRecorder {} + val navigator = FakeMessagesNavigator( + onNavigateToRoomLambda = onNavigateToRoomLambda + ) + val presenter = createTimelinePresenter(room = room, messagesNavigator = navigator) + presenter.test { + val initialState = awaitFirstItem() + initialState.eventSink(TimelineEvents.NavigateToRoom(A_ROOM_ID)) + assert(onNavigateToRoomLambda) + .isCalledOnce() + .with( + value(A_ROOM_ID) + ) + } + } + private suspend fun ReceiveTurbine.awaitFirstItem(): T { return awaitItem() } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt index 28f773e52c..b1c959d86d 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt @@ -148,7 +148,7 @@ internal fun RoomSummaryRow( Spacer(modifier = Modifier.height(4.dp)) } Text( - text = stringResource(id = R.string.screen_join_room_knock_sent_title), + text = stringResource(id = R.string.screen_roomlist_knock_event_sent_description), maxLines = 1, overflow = TextOverflow.Ellipsis, style = ElementTheme.typography.fontBodyMdRegular, diff --git a/features/roomlist/impl/src/main/res/values-cs/translations.xml b/features/roomlist/impl/src/main/res/values-cs/translations.xml index 927f9a40b9..7c15f1c82a 100644 --- a/features/roomlist/impl/src/main/res/values-cs/translations.xml +++ b/features/roomlist/impl/src/main/res/values-cs/translations.xml @@ -15,7 +15,6 @@ "Odmítnout chat" "Žádné pozvánky" "%1$s (%2$s) vás pozval(a)" - "Žádost o vstup odeslána" "Jedná se o jednorázový proces, prosíme o strpení." "Nastavení vašeho účtu" "Vytvořte novou konverzaci nebo místnost" @@ -37,9 +36,11 @@ Prozatím můžete zrušit výběr filtrů, abyste viděli své další chaty""Nepřečtené" "Gratulujeme! Nemáte žádné nepřečtené zprávy!" + "Žádost o vstup odeslána" "Všechny chaty" "Označit jako přečtené" "Označit jako nepřečtené" + "Tato místnost byla aktualizována" "Zdá se, že používáte nové zařízení. Ověřte přihlášení, abyste měli přístup k zašifrovaným zprávám." "Ověřte, že jste to vy" diff --git a/features/roomlist/impl/src/main/res/values-cy/translations.xml b/features/roomlist/impl/src/main/res/values-cy/translations.xml index de6fe0cf0f..7d779e0904 100644 --- a/features/roomlist/impl/src/main/res/values-cy/translations.xml +++ b/features/roomlist/impl/src/main/res/values-cy/translations.xml @@ -15,7 +15,6 @@ "Gwrthod sgwrs" "Dim Gwahoddiadau" "Mae %1$s (%2$s) wedi eich gwahodd" - "Anfonwyd y cais i ymuno" "Mae hon yn broses un tro, diolch am aros." "Creu eich cyfrif." "Crëwch sgwrs neu ystafell newydd" @@ -37,6 +36,7 @@ Am y tro, gallwch ddad-ddewis hidlwyr er mwyn gweld eich sgyrsiau eraill""Heb ei ddarllen" "Llongyfarchiadau! Does gennych chi ddim negeseuon heb eu darllen!" + "Anfonwyd y cais i ymuno" "Sgyrsiau" "Marcio fel wedi\'i ddarllen" "Marcio fel heb ei ddarllen" diff --git a/features/roomlist/impl/src/main/res/values-de/translations.xml b/features/roomlist/impl/src/main/res/values-de/translations.xml index 59cd4dc0c6..bd09aad06d 100644 --- a/features/roomlist/impl/src/main/res/values-de/translations.xml +++ b/features/roomlist/impl/src/main/res/values-de/translations.xml @@ -15,7 +15,6 @@ "Einladung ablehnen" "Keine Einladungen" "%1$s (%2$s) hat dich eingeladen" - "Beitrittsanfrage geschickt" "Dies ist ein einmaliger Vorgang, danke fürs Warten." "Dein Konto wird eingerichtet." "Eine Unterthaltung oder Raum erstellen" @@ -37,6 +36,7 @@ Deaktivieren Sie den entsprechenden Filter, um Ihre anderen Chatrooms zu sehen"< "Ungelesen" "Glückwunsch! Sie haben keine ungelesenen Nachrichten!" + "Beitrittsanfrage geschickt" "Chats" "Als gelesen markieren" "Als ungelesen markieren" diff --git a/features/roomlist/impl/src/main/res/values-el/translations.xml b/features/roomlist/impl/src/main/res/values-el/translations.xml index cd91ad7efa..596592f594 100644 --- a/features/roomlist/impl/src/main/res/values-el/translations.xml +++ b/features/roomlist/impl/src/main/res/values-el/translations.xml @@ -15,7 +15,6 @@ "Απόρριψη συνομιλίας" "Χωρίς προσκλήσεις" "%1$s (%2$s) σέ προσκάλεσε" - "Το αίτημα συμμετοχής στάλθηκε" "Αυτή είναι μια εφάπαξ διαδικασία, ευχαριστώ που περίμενες." "Ρύθμιση του λογαριασμού σου." "Δημιουργία νέας συνομιλίας ή δωματίου" @@ -37,6 +36,7 @@ "Μη αναγνωσμένα" "Συγχαρητήρια! Δεν έχεις μη αναγνωσμένα μηνύματα!" + "Το αίτημα συμμετοχής στάλθηκε" "Συνομιλίες" "Επισήμανση ως αναγνωσμένου" "Επισήμανση ως μη αναγνωσμένου" diff --git a/features/roomlist/impl/src/main/res/values-es/translations.xml b/features/roomlist/impl/src/main/res/values-es/translations.xml index fced9f1141..a58f9e743a 100644 --- a/features/roomlist/impl/src/main/res/values-es/translations.xml +++ b/features/roomlist/impl/src/main/res/values-es/translations.xml @@ -15,7 +15,6 @@ "Rechazar el chat" "Sin invitaciones" "%1$s (%2$s) te invitó" - "Solicitud de unión enviada" "Este proceso solo se hace una vez, gracias por esperar." "Configura tu cuenta" "Crear una nueva conversación o sala" @@ -37,6 +36,7 @@ Por ahora, puedes deseleccionar los filtros para ver tus otros chats" "No leídos" "¡Felicidades! ¡No tienes ningún mensaje sin leer!" + "Solicitud de unión enviada" "Chats" "Marcar como leído" "Marcar como no leído" diff --git a/features/roomlist/impl/src/main/res/values-et/translations.xml b/features/roomlist/impl/src/main/res/values-et/translations.xml index a7e3f822bf..ecba029e6e 100644 --- a/features/roomlist/impl/src/main/res/values-et/translations.xml +++ b/features/roomlist/impl/src/main/res/values-et/translations.xml @@ -15,7 +15,6 @@ "Keeldu vestlusest" "Kutseid pole" "%1$s (%2$s) saatis sulle kutse" - "Liitumispalve on saadetud" "Tänud, et ootad - seda toimingut on vaja teha vaid üks kord." "Seadistame sinu kasutajakontot." "Loo uus vestlus või jututuba" @@ -37,6 +36,7 @@ Aga seni… oma teiste vestluste nägemiseks pead eemaldama filtrid" "Lugemata" "Õnnitleme! Sul pole ühtegi lugemata sõnumit!" + "Liitumispalve on saadetud" "Vestlused" "Märgi loetuks" "Märgi mitteloetuks" diff --git a/features/roomlist/impl/src/main/res/values-eu/translations.xml b/features/roomlist/impl/src/main/res/values-eu/translations.xml index 977ae65fe3..8289dc97d4 100644 --- a/features/roomlist/impl/src/main/res/values-eu/translations.xml +++ b/features/roomlist/impl/src/main/res/values-eu/translations.xml @@ -11,7 +11,6 @@ "Baztertu txata" "Ez dago gonbidapenik" "%1$s(e)k (%2$s) gonbidatu zaitu" - "Sartzeko eskaera bidali da" "Behin egin beharreko prozesua da; eskerrik asko itxaroteagatik." "Zure kontua konfiguratzen." "Sortu elkarrizketa edo gela berria" @@ -33,6 +32,7 @@ Oraingoz, iragazkiak desautatu ditzakezu zure gainerako txatak ikusteko""Irakurri gabeak" "Bejondeizula! Ez duzu irakurri gabeko mezurik!" + "Sartzeko eskaera bidali da" "Txatak" "Markatu irakurritzat" "Markatu irakurri gabetzat" diff --git a/features/roomlist/impl/src/main/res/values-fa/translations.xml b/features/roomlist/impl/src/main/res/values-fa/translations.xml index b5a277bb3a..f2b8757107 100644 --- a/features/roomlist/impl/src/main/res/values-fa/translations.xml +++ b/features/roomlist/impl/src/main/res/values-fa/translations.xml @@ -11,7 +11,6 @@ "رد گپ" "بدون دعوت" "%1$s (%2$s) دعوتتان کرد" - "درخواست پیوستن فرستاده شد" "فرایندی یک باره است. ممنون از شکیباییتان." "برپایی حسابتان." "ایجاد اتاق یا گفت‌وگویی جدید" @@ -31,6 +30,7 @@ "نخوانده‌ها" "تبریک! هیچ پیام نخوانده‌ای ندارید!" + "درخواست پیوستن فرستاده شد" "گپ‌ها" "علامت‌گذاری به عنوان خوانده شده" "نشان به ناخوانده" diff --git a/features/roomlist/impl/src/main/res/values-fi/translations.xml b/features/roomlist/impl/src/main/res/values-fi/translations.xml index 27c4972452..910564ba32 100644 --- a/features/roomlist/impl/src/main/res/values-fi/translations.xml +++ b/features/roomlist/impl/src/main/res/values-fi/translations.xml @@ -15,7 +15,6 @@ "Hylkää keskustelu" "Ei kutsuja" "%1$s (%2$s) kutsui sinut" - "Liittymispyyntö lähetetty" "Tämä on kertaluonteinen prosessi, kiitos odottamisesta." "Tiliä määritetään." "Luo uusi keskustelu tai huone" @@ -37,6 +36,7 @@ Toistaiseksi voit poistaa suodattimien valinnan, jotta näet muut keskustelut."< "Lukemattomat" "Onnittelut! Sinulla ei ole lukemattomia viestejä!" + "Liittymispyyntö lähetetty" "Keskustelut" "Merkitse luetuksi" "Merkitse lukemattomaksi" diff --git a/features/roomlist/impl/src/main/res/values-fr/translations.xml b/features/roomlist/impl/src/main/res/values-fr/translations.xml index 0c6805086a..4ffd377d4f 100644 --- a/features/roomlist/impl/src/main/res/values-fr/translations.xml +++ b/features/roomlist/impl/src/main/res/values-fr/translations.xml @@ -15,7 +15,6 @@ "Refuser l’invitation" "Aucune invitation" "%1$s (%2$s) vous a invité(e)" - "Demande de rejoindre le salon envoyée" "Il s’agit d’une opération ponctuelle, merci d’attendre quelques instants." "Configuration de votre compte." "Créer une nouvelle discussion ou un nouveau salon" @@ -37,6 +36,7 @@ En attendant, vous pouvez désélectionner des filtres pour voir vos autres salo "Non-lus" "Félicitations ! Vous n’avez plus de messages non-lus !" + "Demande de rejoindre le salon envoyée" "Conversations" "Marquer comme lu" "Marquer comme non lu" diff --git a/features/roomlist/impl/src/main/res/values-hu/translations.xml b/features/roomlist/impl/src/main/res/values-hu/translations.xml index 145919ffb7..4c1b882ef6 100644 --- a/features/roomlist/impl/src/main/res/values-hu/translations.xml +++ b/features/roomlist/impl/src/main/res/values-hu/translations.xml @@ -15,7 +15,6 @@ "Csevegés elutasítása" "Nincsenek meghívások" "%1$s (%2$s) meghívta" - "Csatlakozási kérés elküldve" "Ez egy egyszeri folyamat, köszönjük a türelmét." "A fiók beállítása." "Új beszélgetés vagy szoba létrehozása" @@ -37,6 +36,7 @@ Egyelőre törölheti a szűrőket a többi csevegés megtekintéséhez.""Olvasatlan" "Gratulálunk! Nincs olvasatlan üzenete!" + "Csatlakozási kérés elküldve" "Összes csevegés" "Megjelölés olvasottként" "Megjelölés olvasatlanként" diff --git a/features/roomlist/impl/src/main/res/values-in/translations.xml b/features/roomlist/impl/src/main/res/values-in/translations.xml index 310e2a256c..2b390790e1 100644 --- a/features/roomlist/impl/src/main/res/values-in/translations.xml +++ b/features/roomlist/impl/src/main/res/values-in/translations.xml @@ -15,7 +15,6 @@ "Tolak obrolan" "Tidak ada undangan" "%1$s (%2$s) mengundang Anda" - "Permintaan untuk bergabung dikirim" "Ini adalah proses satu kali, terima kasih telah menunggu." "Menyiapkan akun Anda." "Buat percakapan atau ruangan baru" @@ -37,6 +36,7 @@ Untuk sementara, Anda dapat membatalkan pilihan saringan untuk melihat percakapa "Belum dibaca" "Selamat! Anda tidak memiliki pesan yang belum dibaca!" + "Permintaan untuk bergabung dikirim" "Semua Obrolan" "Tandai sebagai dibaca" "Tandai sebagai belum dibaca" diff --git a/features/roomlist/impl/src/main/res/values-it/translations.xml b/features/roomlist/impl/src/main/res/values-it/translations.xml index 464267ab27..981fb27d96 100644 --- a/features/roomlist/impl/src/main/res/values-it/translations.xml +++ b/features/roomlist/impl/src/main/res/values-it/translations.xml @@ -15,7 +15,6 @@ "Rifiuta l\'invito alla conversazione" "Nessun invito" "%1$s (%2$s) ti ha invitato" - "Richiesta di accesso inviata" "Si tratta di una procedura che si effettua una sola volta, grazie per l\'attesa." "Configurazione del tuo account." "Crea una nuova conversazione o stanza" @@ -37,6 +36,7 @@ Per il momento, puoi deselezionare i filtri per vedere le altre conversazioni."< "Non letti" "Congratulazioni! Non hai messaggi non letti!" + "Richiesta di accesso inviata" "Tutte le conversazioni" "Segna come letto" "Segna come non letto" diff --git a/features/roomlist/impl/src/main/res/values-nb/translations.xml b/features/roomlist/impl/src/main/res/values-nb/translations.xml index 7e4de08fae..b4fc0b1950 100644 --- a/features/roomlist/impl/src/main/res/values-nb/translations.xml +++ b/features/roomlist/impl/src/main/res/values-nb/translations.xml @@ -15,7 +15,6 @@ "Avslå chat" "Ingen invitasjoner" "%1$s(%2$s) inviterte deg" - "Forespørsel om å bli med sendt" "Dette er en engangsprosess, takk for at du venter." "Setter opp kontoen din." "Opprett en ny samtale eller et nytt rom" @@ -37,6 +36,7 @@ Inntil videre kan du velge bort filtre for å se de andre chattene dine""Uleste" "Gratulerer! Du har ingen uleste meldinger!" + "Forespørsel om å bli med sendt" "Chatter" "Marker som lest" "Merk som ulest" diff --git a/features/roomlist/impl/src/main/res/values-nl/translations.xml b/features/roomlist/impl/src/main/res/values-nl/translations.xml index 425e21d5ed..fc207da016 100644 --- a/features/roomlist/impl/src/main/res/values-nl/translations.xml +++ b/features/roomlist/impl/src/main/res/values-nl/translations.xml @@ -13,7 +13,6 @@ "Chat weigeren" "Geen uitnodigingen" "%1$s (%2$s) heeft je uitgenodigd" - "Verzoek om toe te treden verzonden" "Dit is een eenmalig proces, bedankt voor het wachten." "Je account instellen." "Begin een nieuw gesprek of maak een nieuwe kamer" @@ -35,6 +34,7 @@ Voor nu kun je filters deselecteren om je andere chats te zien" "Ongelezen" "Gefeliciteerd! Je hebt geen ongelezen berichten!" + "Verzoek om toe te treden verzonden" "Chats" "Markeren als gelezen" "Markeren als ongelezen" diff --git a/features/roomlist/impl/src/main/res/values-pl/translations.xml b/features/roomlist/impl/src/main/res/values-pl/translations.xml index bc7f2cb189..8bb236e71a 100644 --- a/features/roomlist/impl/src/main/res/values-pl/translations.xml +++ b/features/roomlist/impl/src/main/res/values-pl/translations.xml @@ -15,7 +15,6 @@ "Odrzuć czat" "Brak zaproszeń" "%1$s (%2$s) zaprosił Cię" - "Wysłano prośbę o dołączenie" "Jest to jednorazowy proces, dziękujemy za czekanie." "Konfigurowanie Twojego konta." "Utwórz nową rozmowę lub pokój" @@ -37,6 +36,7 @@ Na razie możesz wyczyścić filtry, aby zobaczyć pozostałe czaty" "Nieprzeczytane" "Gratulacje! Nie masz żadnych nieprzeczytanych wiadomości!" + "Wysłano prośbę o dołączenie" "Wszystkie czaty" "Oznacz jako przeczytane" "Oznacz jako nieprzeczytane" diff --git a/features/roomlist/impl/src/main/res/values-pt-rBR/translations.xml b/features/roomlist/impl/src/main/res/values-pt-rBR/translations.xml index 00a47154c2..01b75db869 100644 --- a/features/roomlist/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/roomlist/impl/src/main/res/values-pt-rBR/translations.xml @@ -15,7 +15,6 @@ "Recusar chat" "Sem convites" "%1$s(%2$s) convidou você" - "Pedido de adesão enviado" "Este é um processo único, obrigado por esperar." "Configurando sua conta." "Criar uma nova conversa ou sala" @@ -37,6 +36,7 @@ Por enquanto, você pode desmarcar os filtros para ver seus outros bate-papos""Não lidos" "Parabéns! Você não tem nenhuma mensagem não lida!" + "Pedido de adesão enviado" "Conversas" "Marcar como lido" "Marcar como não lido" diff --git a/features/roomlist/impl/src/main/res/values-pt/translations.xml b/features/roomlist/impl/src/main/res/values-pt/translations.xml index 4d87f54b8b..b332c4f1f8 100644 --- a/features/roomlist/impl/src/main/res/values-pt/translations.xml +++ b/features/roomlist/impl/src/main/res/values-pt/translations.xml @@ -15,7 +15,6 @@ "Rejeitar conversa" "Sem convites" "%1$s (%2$s) convidou-te" - "Pedido de adesão enviado" "Este processo só acontece uma única vez, obrigado por esperares." "A configurar a tua conta…" "Criar uma nova conversa ou sala" @@ -37,6 +36,7 @@ Por enquanto, podes anular a seleção dos filtros para veres as tuas outras con "Por ler" "Parabéns! Não tens nenhuma mensagem por ler!" + "Pedido de adesão enviado" "Conversas" "Marcar como lida" "Marcar como não lida" diff --git a/features/roomlist/impl/src/main/res/values-ro/translations.xml b/features/roomlist/impl/src/main/res/values-ro/translations.xml index b332101f2c..2d7bdd8a95 100644 --- a/features/roomlist/impl/src/main/res/values-ro/translations.xml +++ b/features/roomlist/impl/src/main/res/values-ro/translations.xml @@ -13,7 +13,6 @@ "Refuzați conversația" "Nicio invitație" "%1$s (%2$s) v-a invitat." - "Cererea de alăturare a fost trimisă" "Acesta este un proces care se desfășoară o singură dată, vă mulțumim pentru așteptare." "Contul dumneavoastră se configurează" "Creați o conversație sau o cameră nouă" @@ -35,6 +34,7 @@ Deocamdată, puteți deselecta filtrele pentru a vedea celelalte chat-uri""Necitite" "Felicitari! Nu aveți mesaje necitite!" + "Cererea de alăturare a fost trimisă" "Toate conversatiile" "Marcați ca citită" "Marcați ca necitită" diff --git a/features/roomlist/impl/src/main/res/values-ru/translations.xml b/features/roomlist/impl/src/main/res/values-ru/translations.xml index c51fbc21d4..1ae52ac1a5 100644 --- a/features/roomlist/impl/src/main/res/values-ru/translations.xml +++ b/features/roomlist/impl/src/main/res/values-ru/translations.xml @@ -15,7 +15,6 @@ "Отклонить чат" "Нет приглашений" "%1$s (%2$s) пригласил вас" - "Запрос на присоединение отправлен" "Это одноразовый процесс, спасибо, что подождали." "Настройка учетной записи." "Создайте новую беседу или комнату" @@ -37,6 +36,7 @@ "Непрочитанные" "Поздравляем! У вас нет непрочитанных сообщений!" + "Запрос на присоединение отправлен" "Все чаты" "Пометить как прочитанное" "Отметить как непрочитанное" diff --git a/features/roomlist/impl/src/main/res/values-sk/translations.xml b/features/roomlist/impl/src/main/res/values-sk/translations.xml index fb9da3707d..9898a0cbad 100644 --- a/features/roomlist/impl/src/main/res/values-sk/translations.xml +++ b/features/roomlist/impl/src/main/res/values-sk/translations.xml @@ -15,7 +15,6 @@ "Odmietnuť konverzáciu" "Žiadne pozvánky" "%1$s (%2$s) vás pozval/a" - "Žiadosť o pripojenie bola odoslaná" "Ide o jednorazový proces, ďakujeme za trpezlivosť." "Nastavenie vášho účtu." "Vytvorte novú konverzáciu alebo miestnosť" @@ -37,6 +36,7 @@ Zatiaľ môžete zrušiť výber filtrov, aby ste videli ostatné konverzácie"< "Neprečítané" "Gratulujeme! Nemáte žiadne neprečítané správy!" + "Žiadosť o pripojenie bola odoslaná" "Všetky konverzácie" "Označiť ako prečítané" "Označiť ako neprečítané" diff --git a/features/roomlist/impl/src/main/res/values-sv/translations.xml b/features/roomlist/impl/src/main/res/values-sv/translations.xml index 9c69cb1937..9cc2fe2f99 100644 --- a/features/roomlist/impl/src/main/res/values-sv/translations.xml +++ b/features/roomlist/impl/src/main/res/values-sv/translations.xml @@ -15,7 +15,6 @@ "Avböj chatt" "Inga inbjudningar" "%1$s (%2$s) bjöd in dig" - "Begäran om att gå med skickad" "Detta är en engångsprocess, tack för att du väntar." "Konfigurerar ditt konto" "Skapa en ny konversation eller ett nytt rum" @@ -37,6 +36,7 @@ För tillfället kan du avmarkera filter för att se dina andra chattar""Olästa" "Grattis! Du har inga olästa meddelanden!" + "Begäran om att gå med skickad" "Alla chattar" "Markera som läst" "Markera som oläst" diff --git a/features/roomlist/impl/src/main/res/values-tr/translations.xml b/features/roomlist/impl/src/main/res/values-tr/translations.xml index 1502b4fcaf..6f42809ef3 100644 --- a/features/roomlist/impl/src/main/res/values-tr/translations.xml +++ b/features/roomlist/impl/src/main/res/values-tr/translations.xml @@ -15,7 +15,6 @@ "Sohbeti reddet" "Davet Yok" "%1$s (%2$s) sizi davet etti" - "Katılma isteği gönderildi" "Bu tek seferlik bir işlemdir, beklediğiniz için teşekkürler." "Hesabınızı ayarlanıyor." "Yeni bir sohbet veya oda oluşturun" @@ -37,6 +36,7 @@ "Okunmamış" "Tebrikler! Okunmamış mesajınız yok!" + "Katılma isteği gönderildi" "Sohbetler" "Okundu olarak işaretle" "Okunmamış olarak işaretle" diff --git a/features/roomlist/impl/src/main/res/values-uk/translations.xml b/features/roomlist/impl/src/main/res/values-uk/translations.xml index 26875754d3..16836724df 100644 --- a/features/roomlist/impl/src/main/res/values-uk/translations.xml +++ b/features/roomlist/impl/src/main/res/values-uk/translations.xml @@ -15,7 +15,6 @@ "Відхилити бесіду" "Немає запрошень" "%1$s (%2$s) запрошує вас" - "Запит на приєднання надіслано" "Це одноразовий процес, дякую за очікування." "Налаштування облікового запису." "Створити нову розмову або кімнату" @@ -37,6 +36,7 @@ "Непрочитані" "Вітаємо! У вас немає непрочитаних повідомлень!" + "Запит на приєднання надіслано" "Бесіди" "Позначити прочитаним" "Позначити непрочитаним" diff --git a/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml b/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml index 4a13d33c6c..1bc2ae181c 100644 --- a/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml @@ -15,7 +15,6 @@ "拒絕聊天" "沒有邀請" "%1$s(%2$s)邀請您" - "已傳送加入請求" "這是一次性的程序,感謝您耐心等候。" "正在設定您的帳號。" "建立新的對話或聊天室" @@ -37,6 +36,7 @@ "未讀" "恭喜! 您沒有任何未讀的訊息!" + "已傳送加入請求" "所有聊天室" "標為已讀" "標為未讀" diff --git a/features/roomlist/impl/src/main/res/values-zh/translations.xml b/features/roomlist/impl/src/main/res/values-zh/translations.xml index 7c5d778ddf..e7e97040dd 100644 --- a/features/roomlist/impl/src/main/res/values-zh/translations.xml +++ b/features/roomlist/impl/src/main/res/values-zh/translations.xml @@ -15,7 +15,6 @@ "拒绝聊天" "没有邀请" "%1$s (%2$s)邀请了你" - "加入请求已发送" "这是一个一次性的过程,感谢您的等待。" "设置您的账户。" "创建新的对话或聊天室" @@ -37,6 +36,7 @@ "未读" "恭喜! 没有任何未读消息!" + "加入请求已发送" "全部聊天" "标记为已读" "标记为未读" diff --git a/features/roomlist/impl/src/main/res/values/localazy.xml b/features/roomlist/impl/src/main/res/values/localazy.xml index a7b7ce8047..90432433c2 100644 --- a/features/roomlist/impl/src/main/res/values/localazy.xml +++ b/features/roomlist/impl/src/main/res/values/localazy.xml @@ -18,7 +18,6 @@ "Decline chat" "No Invites" "%1$s (%2$s) invited you" - "Request to join sent" "This is a one time process, thanks for waiting." "Setting up your account." "Create a new conversation or room" @@ -40,9 +39,11 @@ For now, you can deselect filters in order to see your other chats" "Unreads" "Congrats! You don’t have any unread messages!" + "Request to join sent" "Chats" "Mark as read" "Mark as unread" + "This room has been upgraded" "Looks like you’re using a new device. Verify with another device to access your encrypted messages." "Verify it’s you" diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMolecule.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMolecule.kt index 33706239dc..0108779913 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMolecule.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/molecules/ComposerAlertMolecule.kt @@ -39,7 +39,7 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun ComposerAlertMolecule( - avatar: AvatarData, + avatar: AvatarData?, content: AnnotatedString, onSubmitClick: () -> Unit, modifier: Modifier = Modifier, @@ -71,9 +71,9 @@ fun ComposerAlertMolecule( Row( horizontalArrangement = Arrangement.spacedBy(16.dp) ) { - Avatar( - avatarData = avatar, - ) + if (avatar != null) { + Avatar(avatarData = avatar) + } Text( text = content, modifier = Modifier.weight(1f), diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/BaseRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/BaseRoom.kt index 120321bddf..ecbf2af20b 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/BaseRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/BaseRoom.kt @@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.draft.ComposerDraft import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels +import io.element.android.libraries.matrix.api.room.tombstone.PredecessorRoom import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility import io.element.android.libraries.matrix.api.timeline.ReceiptType import kotlinx.coroutines.CoroutineScope @@ -55,6 +56,8 @@ interface BaseRoom : Closeable { */ fun info(): RoomInfo = roomInfoFlow.value + fun predecessorRoom(): PredecessorRoom? + /** * A one-to-one is a room with exactly 2 members. * See [the Matrix spec](https://spec.matrix.org/latest/client-server-api/#default-underride-rules). diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomInfo.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomInfo.kt index 8702218150..13e2ed44da 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomInfo.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomInfo.kt @@ -14,6 +14,7 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility import io.element.android.libraries.matrix.api.room.join.JoinRule +import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableMap diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/tombstone/PredecessorRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/tombstone/PredecessorRoom.kt new file mode 100644 index 0000000000..cd54b5a07b --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/tombstone/PredecessorRoom.kt @@ -0,0 +1,31 @@ +/* + * 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.libraries.matrix.api.room.tombstone + +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId + +/** + * + * When a room A is tombstoned, it is replaced by a room B. The room A is the + * predecessor of B, and B is the successor of A. This type holds information + * about the predecessor room. + * + * A room is tombstoned if it has received a m.room.tombstone state event. + * + */ +data class PredecessorRoom( + /** + * The ID of the replaced room. + */ + val roomId: RoomId, + /** + * The event ID of the last known event in the predecessor room. + */ + val lastEventId: EventId, +) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/SuccessorRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/tombstone/SuccessorRoom.kt similarity index 92% rename from libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/SuccessorRoom.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/tombstone/SuccessorRoom.kt index 4aaf3dd2d2..957585a464 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/SuccessorRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/tombstone/SuccessorRoom.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.api.room +package io.element.android.libraries.matrix.api.room.tombstone import io.element.android.libraries.matrix.api.core.RoomId diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt index 2717f6275e..e0dec91899 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt @@ -14,11 +14,11 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.RoomInfo import io.element.android.libraries.matrix.api.room.RoomNotificationMode -import io.element.android.libraries.matrix.api.room.SuccessorRoom import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.impl.room.history.map import io.element.android.libraries.matrix.impl.room.join.map import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper +import io.element.android.libraries.matrix.impl.room.tombstone.map import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toPersistentMap @@ -28,7 +28,6 @@ import uniffi.matrix_sdk_base.EncryptionState import org.matrix.rustcomponents.sdk.Membership as RustMembership import org.matrix.rustcomponents.sdk.RoomInfo as RustRoomInfo import org.matrix.rustcomponents.sdk.RoomNotificationMode as RustRoomNotificationMode -import org.matrix.rustcomponents.sdk.SuccessorRoom as RustSuccessorRoom class RoomInfoMapper { fun map(rustRoomInfo: RustRoomInfo): RoomInfo = rustRoomInfo.let { @@ -88,11 +87,6 @@ fun RustRoomNotificationMode.map(): RoomNotificationMode = when (this) { RustRoomNotificationMode.MUTE -> RoomNotificationMode.MUTE } -fun RustSuccessorRoom.map(): SuccessorRoom = SuccessorRoom( - roomId = RoomId(roomId), - reason = reason, -) - /** * Map a RoomHero to a MatrixUser. There is not need to create a RoomHero type on the application side. */ diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt index f86a179af2..09b7ec1285 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt @@ -24,12 +24,14 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.draft.ComposerDraft import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels +import io.element.android.libraries.matrix.api.room.tombstone.PredecessorRoom import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.impl.room.draft.into import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper import io.element.android.libraries.matrix.impl.room.powerlevels.RoomPowerLevelsMapper +import io.element.android.libraries.matrix.impl.room.tombstone.map import io.element.android.libraries.matrix.impl.roomdirectory.map import io.element.android.libraries.matrix.impl.timeline.toRustReceiptType import io.element.android.libraries.matrix.impl.util.mxCallbackFlow @@ -78,6 +80,10 @@ class RustBaseRoom( }) }.stateIn(roomCoroutineScope, started = SharingStarted.Lazily, initialValue = initialRoomInfo) + override fun predecessorRoom(): PredecessorRoom? { + return innerRoom.predecessorRoom()?.map() + } + override suspend fun subscribeToSync() = roomSyncSubscriber.subscribe(roomId) override suspend fun updateMembers() { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/tombstone/PredecessorRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/tombstone/PredecessorRoom.kt new file mode 100644 index 0000000000..08daee4f19 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/tombstone/PredecessorRoom.kt @@ -0,0 +1,20 @@ +/* + * 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.libraries.matrix.impl.room.tombstone + +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.tombstone.PredecessorRoom +import org.matrix.rustcomponents.sdk.PredecessorRoom as RustPredecessorRoom + +fun RustPredecessorRoom.map(): PredecessorRoom { + return PredecessorRoom( + roomId = RoomId(roomId), + lastEventId = EventId(lastEventId), + ) +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/tombstone/SuccessorRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/tombstone/SuccessorRoom.kt new file mode 100644 index 0000000000..afc0ee96af --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/tombstone/SuccessorRoom.kt @@ -0,0 +1,19 @@ +/* + * 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.libraries.matrix.impl.room.tombstone + +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom +import org.matrix.rustcomponents.sdk.SuccessorRoom as RustSuccessorRoom + +fun RustSuccessorRoom.map(): SuccessorRoom { + return SuccessorRoom( + roomId = RoomId(roomId), + reason = reason + ) +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt index 275c55ccc0..53b8127ab8 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt @@ -27,6 +27,11 @@ import org.matrix.rustcomponents.sdk.RoomListService import kotlin.coroutines.CoroutineContext import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList +private val ROOM_LIST_RUST_FILTERS = listOf( + RoomListEntriesDynamicFilterKind.NonLeft, + RoomListEntriesDynamicFilterKind.DeduplicateVersions +) + internal class RoomListFactory( private val innerRoomListService: RoomListService, private val sessionCoroutineScope: CoroutineScope, @@ -55,11 +60,11 @@ internal class RoomListFactory( coroutineScope.launch(coroutineContext) { innerRoomList = innerProvider() - innerRoomList?.let { innerRoomList -> + innerRoomList.let { innerRoomList -> innerRoomList.entriesFlow( pageSize = pageSize, roomListDynamicEvents = dynamicEvents, - initialFilterKind = RoomListEntriesDynamicFilterKind.NonLeft + initialFilterKind = RoomListEntriesDynamicFilterKind.All(ROOM_LIST_RUST_FILTERS), ).onEach { update -> processor.postUpdate(update) }.launchIn(this) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeBaseRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeBaseRoom.kt index ae9aee8736..c7e1d3e143 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeBaseRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeBaseRoom.kt @@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.room.RoomMembersState import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.draft.ComposerDraft import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevels +import io.element.android.libraries.matrix.api.room.tombstone.PredecessorRoom import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.test.A_ROOM_ID @@ -66,6 +67,7 @@ class FakeBaseRoom( private val getRoomVisibilityResult: () -> Result = { lambdaError() }, private val forgetResult: () -> Result = { lambdaError() }, private val reportRoomResult: (String?) -> Result = { lambdaError() }, + private val predecessorRoomResult: () -> PredecessorRoom? = { null }, ) : BaseRoom { private val _roomInfoFlow: MutableStateFlow = MutableStateFlow(initialRoomInfo) override val roomInfoFlow: StateFlow = _roomInfoFlow @@ -215,6 +217,8 @@ class FakeBaseRoom( } override suspend fun reportRoom(reason: String?) = reportRoomResult(reason) + + override fun predecessorRoom(): PredecessorRoom? = predecessorRoomResult() } fun defaultRoomPowerLevels() = RoomPowerLevels( diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt index 5b6040dee5..0fc88d1d81 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt @@ -15,9 +15,9 @@ import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.RoomInfo import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomNotificationMode -import io.element.android.libraries.matrix.api.room.SuccessorRoom import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility import io.element.android.libraries.matrix.api.room.join.JoinRule +import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.A_ROOM_ID diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt index 11e462f977..27302a2d87 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt @@ -15,10 +15,10 @@ import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.RoomInfo import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomNotificationMode -import io.element.android.libraries.matrix.api.room.SuccessorRoom import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.room.message.RoomMessage +import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom import io.element.android.libraries.matrix.api.roomlist.RoomSummary import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem import io.element.android.libraries.matrix.api.user.MatrixUser diff --git a/libraries/ui-strings/src/main/res/values-cy/translations.xml b/libraries/ui-strings/src/main/res/values-cy/translations.xml index c6747aca86..52c1046030 100644 --- a/libraries/ui-strings/src/main/res/values-cy/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cy/translations.xml @@ -396,7 +396,6 @@ Ydych chi\'n siŵr eich bod am barhau?" "Wrthi\'n llwytho neges…" "Dangos y Cyfan" "Sgwrs" - "Anfonwyd y cais i ymuno" "Rhannu lleoliad" "Rhannu fy lleoliad" "Agor yn Apple Maps" diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index afcc1f784f..94f6dc40fc 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -372,7 +372,6 @@ Möchten Sie wirklich fortfahren?" "Nachricht wird geladen…" "Alle anzeigen" "Chat" - "Beitrittsanfrage geschickt" "Standort teilen" "Meinen Standort teilen" "In Apple Maps öffnen" diff --git a/libraries/ui-strings/src/main/res/values-el/translations.xml b/libraries/ui-strings/src/main/res/values-el/translations.xml index efadb6753d..795acf8c29 100644 --- a/libraries/ui-strings/src/main/res/values-el/translations.xml +++ b/libraries/ui-strings/src/main/res/values-el/translations.xml @@ -372,7 +372,6 @@ "Φόρτωση μηνύματος…" "Προβολή Όλων" "Συνομιλία" - "Το αίτημα συμμετοχής στάλθηκε" "Κοινή χρήση τοποθεσίας" "Κοινή χρήση της τοποθεσίας μου" "Άνοιγμα στο Apple Maps" diff --git a/libraries/ui-strings/src/main/res/values-es/translations.xml b/libraries/ui-strings/src/main/res/values-es/translations.xml index dff3581256..2b0c7b84d4 100644 --- a/libraries/ui-strings/src/main/res/values-es/translations.xml +++ b/libraries/ui-strings/src/main/res/values-es/translations.xml @@ -372,7 +372,6 @@ Motivo: %1$s." "Cargando mensaje…" "Ver todos" "Chat" - "Solicitud de unión enviada" "Compartir ubicación" "Compartir mi ubicación" "Abrir en Apple Maps" diff --git a/libraries/ui-strings/src/main/res/values-eu/translations.xml b/libraries/ui-strings/src/main/res/values-eu/translations.xml index c3c2c12a1c..f3bbd83167 100644 --- a/libraries/ui-strings/src/main/res/values-eu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-eu/translations.xml @@ -330,7 +330,6 @@ Arrazoia: %1$s." "Mezua kargatzen…" "Ikusi guztia" "Txata" - "Sartzeko eskaera bidali da" "Partekatu kokapena" "Partekatu nire kokapena" "Ireki Apple Maps-en" diff --git a/libraries/ui-strings/src/main/res/values-fa/translations.xml b/libraries/ui-strings/src/main/res/values-fa/translations.xml index 4fb5ceea2d..a6cc73c1f6 100644 --- a/libraries/ui-strings/src/main/res/values-fa/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fa/translations.xml @@ -323,7 +323,6 @@ "بار کردن پشام‌ها…" "نمایش همه" "گپ" - "درخواست پیوستن فرستاده شد" "هم‌رسانی موقعیت" "هم‌رسانی مکانم" "گشودن در نقشه‌های اپل" diff --git a/libraries/ui-strings/src/main/res/values-in/translations.xml b/libraries/ui-strings/src/main/res/values-in/translations.xml index 722bf149ac..c27f8ef33b 100644 --- a/libraries/ui-strings/src/main/res/values-in/translations.xml +++ b/libraries/ui-strings/src/main/res/values-in/translations.xml @@ -334,7 +334,6 @@ Alasan: %1$s." "Memuat pesan…" "Lihat Semua" "Obrolan" - "Permintaan untuk bergabung dikirim" "Bagikan lokasi" "Bagikan lokasi saya" "Buka di Apple Maps" diff --git a/libraries/ui-strings/src/main/res/values-it/translations.xml b/libraries/ui-strings/src/main/res/values-it/translations.xml index c78465247f..4f4446050b 100644 --- a/libraries/ui-strings/src/main/res/values-it/translations.xml +++ b/libraries/ui-strings/src/main/res/values-it/translations.xml @@ -359,7 +359,6 @@ Sei sicuro di voler continuare?" "Caricamento messaggio…" "Mostra tutti" "Conversazione" - "Richiesta di accesso inviata" "Condividi posizione" "Condividi la mia posizione" "Apri in Apple Maps" diff --git a/libraries/ui-strings/src/main/res/values-nl/translations.xml b/libraries/ui-strings/src/main/res/values-nl/translations.xml index 1066ee7e79..fcb11918db 100644 --- a/libraries/ui-strings/src/main/res/values-nl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-nl/translations.xml @@ -323,7 +323,6 @@ Reden: %1$s." "Bericht laden…" "Bekijk alles" "Chat" - "Verzoek om toe te treden verzonden" "Locatie delen" "Deel mijn locatie" "Openen in Apple Maps" diff --git a/libraries/ui-strings/src/main/res/values-pl/translations.xml b/libraries/ui-strings/src/main/res/values-pl/translations.xml index 76b3177b6a..bb8e162f30 100644 --- a/libraries/ui-strings/src/main/res/values-pl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pl/translations.xml @@ -378,7 +378,6 @@ Czy na pewno chcesz kontynuować?" "Wczytywanie wiadomości…" "Wyświetl wszystkie" "Czat" - "Wysłano prośbę o dołączenie" "Udostępnij lokalizację" "Udostępnij moją lokalizację" "Otwórz w Apple Maps" diff --git a/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml b/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml index d8fddecca2..f5952be07e 100644 --- a/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml @@ -372,7 +372,6 @@ Você tem certeza de que deseja continuar?" "Carregando mensagem…" "Ver tudo" "Bate-papo" - "Pedido de adesão enviado" "Compartilhar localização" "Compartilhar minha localização" "Abrir no Apple Maps" diff --git a/libraries/ui-strings/src/main/res/values-pt/translations.xml b/libraries/ui-strings/src/main/res/values-pt/translations.xml index d8931a4a99..721755f9f1 100644 --- a/libraries/ui-strings/src/main/res/values-pt/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pt/translations.xml @@ -372,7 +372,6 @@ Tens a certeza de que queres continuar?" "A carregar mensagem…" "Ver todas" "Conversa" - "Pedido de adesão enviado" "Partilhar localização" "Partilhar a minha localização" "Abrir no Apple Maps" diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index 76771a4c9f..aad4fc5981 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -327,7 +327,6 @@ Motiv:%1$s." "Se încarcă mesajul…" "Vedeți toate" "Chat" - "Cererea de alăturare a fost trimisă" "Partajați locația" "Distribuiți locația mea" "Deschideți în Apple Maps" diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index fa2ccf4b58..14dea09167 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -378,7 +378,6 @@ Naozaj chcete pokračovať?" "Načítava sa správa…" "Zobraziť všetko" "Konverzácia" - "Žiadosť o pripojenie bola odoslaná" "Zdieľať polohu" "Zdieľať moju polohu" "Otvoriť v Apple Maps" diff --git a/libraries/ui-strings/src/main/res/values-tr/translations.xml b/libraries/ui-strings/src/main/res/values-tr/translations.xml index 40bb3fe7e4..fee4cbe5dd 100644 --- a/libraries/ui-strings/src/main/res/values-tr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-tr/translations.xml @@ -348,7 +348,6 @@ Neden: %1$s." "Mesaj yükleniyor…" "Tümünü görüntüle" "Sohbet" - "Katılma isteği gönderildi" "Konum paylaş" "Konumumu paylaş" "Apple Maps\'de aç" diff --git a/libraries/ui-strings/src/main/res/values-uk/translations.xml b/libraries/ui-strings/src/main/res/values-uk/translations.xml index b317dd24d4..985a951d93 100644 --- a/libraries/ui-strings/src/main/res/values-uk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-uk/translations.xml @@ -378,7 +378,6 @@ "Завантаження повідомлення…" "Переглянути всі" "Бесіда" - "Запит на приєднання надіслано" "Поділитися розташуванням" "Поділитися моїм розташуванням" "Відкрити в Apple Maps" diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml index 835b89bc19..f83e689b17 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml @@ -366,7 +366,6 @@ "正在載入訊息……" "檢視全部" "聊天" - "已傳送加入請求" "分享位置" "分享我的位置" "在 Apple Maps 中開啟" diff --git a/libraries/ui-strings/src/main/res/values-zh/translations.xml b/libraries/ui-strings/src/main/res/values-zh/translations.xml index c43a8bf1da..c5a29e7f61 100644 --- a/libraries/ui-strings/src/main/res/values-zh/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh/translations.xml @@ -357,7 +357,6 @@ "正在加载消息…" "查看全部" "聊天" - "加入请求已发送" "分享位置" "分享我的位置" "在 Apple Maps 中打开" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 9623c459b0..323b762a43 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -381,6 +381,7 @@ Are you sure you want to continue?" "Failed processing media to upload, please try again." "Could not retrieve user details" "Message in %1$s" + "Already viewing this room!" "%1$s of %2$s" "%1$s Pinned messages" "Loading message…" diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/SemanticsNodeInteractionsProviderExtensions.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/SemanticsNodeInteractionsProviderExtensions.kt index 440af51676..55371a960d 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/SemanticsNodeInteractionsProviderExtensions.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/SemanticsNodeInteractionsProviderExtensions.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasText import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.onFirst +import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import io.element.android.libraries.ui.strings.CommonStrings import org.junit.rules.TestRule @@ -54,3 +55,8 @@ fun AndroidComposeTestRule.pressBackKey() { fun SemanticsNodeInteractionsProvider.pressTag(tag: String) { onNode(hasTestTag(tag)).performClick() } + +fun AndroidComposeTestRule.assertNoNodeWithText(@StringRes res: Int) { + val text = activity.getString(res) + onNodeWithText(text).assertDoesNotExist() +} diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en.png index 16e9f5cc43..b46fcdcc5c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:138be910fa88b2e59882b909f5d83824503d88676d23d327c1f4948121cbebe0 -size 14297 +oid sha256:abf66dd56be501e27477d83f97eed4c948fdf049130d46dc5a23e46e4cf18dfc +size 33561 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en.png index 5808ed69cf..806c68d57e 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b9ce5853af7f03ac91da6bb318bd4f033936279d211e87efff4ef6ec8794f51 -size 13988 +oid sha256:603ce16c8ce5e92c6cf1b2639be69e0dc64b99c1c7f1879762661258d222fd84 +size 36317 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_14_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_14_en.png new file mode 100644 index 0000000000..c766a093d8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_14_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79e6ab4ac13eeb03cb02843a122b519a6410aeaad40e5f272d0b09f2547af6e7 +size 62973 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_14_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_14_en.png new file mode 100644 index 0000000000..b21fe7cf0b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_14_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3241f342c62665d1a343d085ec9174e0e6555219ce51e74f4199bea74f641183 +size 65429 diff --git a/tools/localazy/config.json b/tools/localazy/config.json index d6c5b351ac..dbe8c4221a 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -165,14 +165,14 @@ "name" : ":features:roomlist:impl", "includeRegex" : [ "screen_roomlist_.*", + "screen\\.roomlist\\..*", "session_verification_banner_.*", "confirm_recovery_key_banner_.*", "banner\\.set_up_recovery\\..*", "banner\\.battery_optimization\\..*", "full_screen_intent_banner_.*", "screen_migration_.*", - "screen_invites_.*", - "screen\\.join_room\\.knock_sent_title" + "screen_invites_.*" ] }, { @@ -211,6 +211,7 @@ "screen_room_message.*", "screen_room_retry.*", "screen_room_timeline.*", + "screen\\.room_timeline.*", "screen_room_typing.*" ] },