feature (room upgrade) : start rendering SuccessorRoom and PredecessorRoom banners in timeline
This commit is contained in:
parent
ce5d656320
commit
aff8dd8522
24 changed files with 255 additions and 89 deletions
|
|
@ -55,6 +55,7 @@ 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.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
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.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.PermalinkData
|
||||||
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
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.BaseRoom
|
||||||
|
|
@ -143,6 +144,15 @@ class MessagesNode @AssistedInject constructor(
|
||||||
callbacks.forEach { it.onUserDataClick(userId) }
|
callbacks.forEach { it.onUserDataClick(userId) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onRoomDataClick(
|
||||||
|
activity: Activity,
|
||||||
|
eventSink: (TimelineEvents) -> Unit,
|
||||||
|
roomId: RoomId
|
||||||
|
) {
|
||||||
|
val roomLink = PermalinkData.RoomLink(roomId.toRoomIdOrAlias())
|
||||||
|
handleRoomLinkClick(activity, roomLink, eventSink)
|
||||||
|
}
|
||||||
|
|
||||||
private fun onLinkClick(
|
private fun onLinkClick(
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
darkTheme: Boolean,
|
darkTheme: Boolean,
|
||||||
|
|
@ -250,6 +260,7 @@ class MessagesNode @AssistedInject constructor(
|
||||||
onRoomDetailsClick = this::onRoomDetailsClick,
|
onRoomDetailsClick = this::onRoomDetailsClick,
|
||||||
onEventContentClick = this::onEventClick,
|
onEventContentClick = this::onEventClick,
|
||||||
onUserDataClick = this::onUserDataClick,
|
onUserDataClick = this::onUserDataClick,
|
||||||
|
onRoomDataClick = {roomId -> onRoomDataClick(activity, state.timelineState.eventSink, roomId) },
|
||||||
onLinkClick = { url, customTab -> onLinkClick(activity, isDark, url, state.timelineState.eventSink, customTab) },
|
onLinkClick = { url, customTab -> onLinkClick(activity, isDark, url, state.timelineState.eventSink, customTab) },
|
||||||
onSendLocationClick = this::onSendLocationClick,
|
onSendLocationClick = this::onSendLocationClick,
|
||||||
onCreatePollClick = this::onCreatePollClick,
|
onCreatePollClick = this::onCreatePollClick,
|
||||||
|
|
|
||||||
|
|
@ -270,6 +270,7 @@ class MessagesPresenter @AssistedInject constructor(
|
||||||
pinnedMessagesBannerState = pinnedMessagesBannerState,
|
pinnedMessagesBannerState = pinnedMessagesBannerState,
|
||||||
dmUserVerificationState = dmUserVerificationState,
|
dmUserVerificationState = dmUserVerificationState,
|
||||||
roomMemberModerationState = roomMemberModerationState,
|
roomMemberModerationState = roomMemberModerationState,
|
||||||
|
successorRoom = roomInfo.successorRoom,
|
||||||
eventSink = { handleEvents(it) }
|
eventSink = { handleEvents(it) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.designsystem.utils.snackbar.SnackbarMessage
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
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.encryption.identity.IdentityState
|
||||||
|
import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
|
|
@ -56,5 +57,6 @@ data class MessagesState(
|
||||||
val pinnedMessagesBannerState: PinnedMessagesBannerState,
|
val pinnedMessagesBannerState: PinnedMessagesBannerState,
|
||||||
val dmUserVerificationState: IdentityState?,
|
val dmUserVerificationState: IdentityState?,
|
||||||
val roomMemberModerationState: RoomMemberModerationState,
|
val roomMemberModerationState: RoomMemberModerationState,
|
||||||
|
val successorRoom: SuccessorRoom?,
|
||||||
val eventSink: (MessagesEvents) -> Unit
|
val eventSink: (MessagesEvents) -> Unit
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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.designsystem.components.avatar.AvatarSize
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
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.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.MessageComposerMode
|
||||||
import io.element.android.libraries.textcomposer.model.aTextEditorStateRich
|
import io.element.android.libraries.textcomposer.model.aTextEditorStateRich
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
@ -119,6 +120,7 @@ fun aMessagesState(
|
||||||
pinnedMessagesBannerState: PinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(),
|
pinnedMessagesBannerState: PinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(),
|
||||||
dmUserVerificationState: IdentityState? = null,
|
dmUserVerificationState: IdentityState? = null,
|
||||||
roomMemberModerationState: RoomMemberModerationState = aRoomMemberModerationState(),
|
roomMemberModerationState: RoomMemberModerationState = aRoomMemberModerationState(),
|
||||||
|
successorRoom: SuccessorRoom? = null,
|
||||||
eventSink: (MessagesEvents) -> Unit = {},
|
eventSink: (MessagesEvents) -> Unit = {},
|
||||||
) = MessagesState(
|
) = MessagesState(
|
||||||
roomId = RoomId("!id:domain"),
|
roomId = RoomId("!id:domain"),
|
||||||
|
|
@ -147,6 +149,7 @@ fun aMessagesState(
|
||||||
pinnedMessagesBannerState = pinnedMessagesBannerState,
|
pinnedMessagesBannerState = pinnedMessagesBannerState,
|
||||||
dmUserVerificationState = dmUserVerificationState,
|
dmUserVerificationState = dmUserVerificationState,
|
||||||
roomMemberModerationState = roomMemberModerationState,
|
roomMemberModerationState = roomMemberModerationState,
|
||||||
|
successorRoom = successorRoom,
|
||||||
eventSink = eventSink,
|
eventSink = eventSink,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.networkmonitor.api.ui.ConnectivityIndicatorView
|
||||||
import io.element.android.features.roomcall.api.RoomCallState
|
import io.element.android.features.roomcall.api.RoomCallState
|
||||||
import io.element.android.libraries.androidutils.ui.hideKeyboard
|
import io.element.android.libraries.androidutils.ui.hideKeyboard
|
||||||
|
import io.element.android.libraries.designsystem.atomic.molecules.ComposerAlertMolecule
|
||||||
import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule
|
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.AvatarData
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
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.components.dialogs.ConfirmationDialog
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
|
import io.element.android.libraries.designsystem.text.toAnnotatedString
|
||||||
import io.element.android.libraries.designsystem.theme.components.BottomSheetDragHandle
|
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.Icon
|
||||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
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.SnackbarHost
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
|
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.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.UserId
|
||||||
import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
|
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.matrix.api.user.MatrixUser
|
||||||
import io.element.android.libraries.textcomposer.model.TextEditorState
|
import io.element.android.libraries.textcomposer.model.TextEditorState
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
@ -119,6 +123,7 @@ fun MessagesView(
|
||||||
onRoomDetailsClick: () -> Unit,
|
onRoomDetailsClick: () -> Unit,
|
||||||
onEventContentClick: (isLive: Boolean, event: TimelineItem.Event) -> Boolean,
|
onEventContentClick: (isLive: Boolean, event: TimelineItem.Event) -> Boolean,
|
||||||
onUserDataClick: (UserId) -> Unit,
|
onUserDataClick: (UserId) -> Unit,
|
||||||
|
onRoomDataClick: (RoomId) -> Unit,
|
||||||
onLinkClick: (String, Boolean) -> Unit,
|
onLinkClick: (String, Boolean) -> Unit,
|
||||||
onSendLocationClick: () -> Unit,
|
onSendLocationClick: () -> Unit,
|
||||||
onCreatePollClick: () -> Unit,
|
onCreatePollClick: () -> Unit,
|
||||||
|
|
@ -205,14 +210,15 @@ fun MessagesView(
|
||||||
MessagesViewContent(
|
MessagesViewContent(
|
||||||
state = state,
|
state = state,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(padding)
|
.padding(padding)
|
||||||
.consumeWindowInsets(padding),
|
.consumeWindowInsets(padding),
|
||||||
onContentClick = ::onContentClick,
|
onContentClick = ::onContentClick,
|
||||||
onMessageLongClick = ::onMessageLongClick,
|
onMessageLongClick = ::onMessageLongClick,
|
||||||
|
onRoomSuccessorClicked = onRoomDataClick,
|
||||||
onUserDataClick = {
|
onUserDataClick = {
|
||||||
hidingKeyboard {
|
hidingKeyboard {
|
||||||
state.eventSink(MessagesEvents.OnUserClicked(it))
|
state.eventSink(MessagesEvents.OnUserClicked(it))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLinkClick = { link, customTab ->
|
onLinkClick = { link, customTab ->
|
||||||
if (customTab) {
|
if (customTab) {
|
||||||
|
|
@ -299,6 +305,7 @@ private fun MessagesViewContent(
|
||||||
state: MessagesState,
|
state: MessagesState,
|
||||||
onContentClick: (TimelineItem.Event) -> Unit,
|
onContentClick: (TimelineItem.Event) -> Unit,
|
||||||
onUserDataClick: (MatrixUser) -> Unit,
|
onUserDataClick: (MatrixUser) -> Unit,
|
||||||
|
onRoomSuccessorClicked: (RoomId) -> Unit,
|
||||||
onLinkClick: (Link, Boolean) -> Unit,
|
onLinkClick: (Link, Boolean) -> Unit,
|
||||||
onReactionClick: (key: String, TimelineItem.Event) -> Unit,
|
onReactionClick: (key: String, TimelineItem.Event) -> Unit,
|
||||||
onReactionLongClick: (key: String, TimelineItem.Event) -> Unit,
|
onReactionLongClick: (key: String, TimelineItem.Event) -> Unit,
|
||||||
|
|
@ -316,9 +323,9 @@ private fun MessagesViewContent(
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.navigationBarsPadding()
|
.navigationBarsPadding()
|
||||||
.imePadding(),
|
.imePadding(),
|
||||||
) {
|
) {
|
||||||
AttachmentsBottomSheet(
|
AttachmentsBottomSheet(
|
||||||
state = state.composerState,
|
state = state.composerState,
|
||||||
|
|
@ -410,6 +417,7 @@ private fun MessagesViewContent(
|
||||||
MessagesViewComposerBottomSheetContents(
|
MessagesViewComposerBottomSheetContents(
|
||||||
subcomposing = subcomposing,
|
subcomposing = subcomposing,
|
||||||
state = state,
|
state = state,
|
||||||
|
onRoomSuccessorClicked = onRoomSuccessorClicked,
|
||||||
onLinkClick = { url, customTab -> onLinkClick(Link(url), customTab) },
|
onLinkClick = { url, customTab -> onLinkClick(Link(url), customTab) },
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -424,52 +432,59 @@ private fun MessagesViewContent(
|
||||||
private fun MessagesViewComposerBottomSheetContents(
|
private fun MessagesViewComposerBottomSheetContents(
|
||||||
subcomposing: Boolean,
|
subcomposing: Boolean,
|
||||||
state: MessagesState,
|
state: MessagesState,
|
||||||
|
onRoomSuccessorClicked: (RoomId) -> Unit,
|
||||||
onLinkClick: (String, Boolean) -> Unit,
|
onLinkClick: (String, Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
if (state.userEventPermissions.canSendMessage) {
|
when {
|
||||||
Column(modifier = Modifier.fillMaxWidth()) {
|
state.successorRoom != null -> {
|
||||||
SuggestionsPickerView(
|
SuccessorRoomBanner(roomSuccessor = state.successorRoom, onRoomSuccessorClicked = onRoomSuccessorClicked)
|
||||||
modifier = Modifier
|
}
|
||||||
.heightIn(max = 230.dp)
|
state.userEventPermissions.canSendMessage -> {
|
||||||
// Consume all scrolling, preventing the bottom sheet from being dragged when interacting with the list of suggestions
|
Column(modifier = Modifier.fillMaxWidth()) {
|
||||||
.nestedScroll(object : NestedScrollConnection {
|
SuggestionsPickerView(
|
||||||
override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset {
|
modifier = Modifier
|
||||||
return available
|
.heightIn(max = 230.dp)
|
||||||
}
|
// Consume all scrolling, preventing the bottom sheet from being dragged when interacting with the list of suggestions
|
||||||
}),
|
.nestedScroll(object : NestedScrollConnection {
|
||||||
roomId = state.roomId,
|
override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset {
|
||||||
roomName = state.roomName.dataOrNull(),
|
return available
|
||||||
roomAvatarData = state.roomAvatar.dataOrNull(),
|
}
|
||||||
suggestions = state.composerState.suggestions,
|
}),
|
||||||
onSelectSuggestion = {
|
roomId = state.roomId,
|
||||||
state.composerState.eventSink(MessageComposerEvents.InsertSuggestion(it))
|
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 {
|
else -> {
|
||||||
CantSendMessageBanner()
|
CantSendMessageBanner()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -493,8 +508,8 @@ private fun MessagesViewTopBar(
|
||||||
val roundedCornerShape = RoundedCornerShape(8.dp)
|
val roundedCornerShape = RoundedCornerShape(8.dp)
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clip(roundedCornerShape)
|
.clip(roundedCornerShape)
|
||||||
.clickable { onRoomDetailsClick() },
|
.clickable { onRoomDetailsClick() },
|
||||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
|
|
@ -572,9 +587,9 @@ private fun RoomAvatarAndNameRow(
|
||||||
private fun CantSendMessageBanner() {
|
private fun CantSendMessageBanner() {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.background(ElementTheme.colors.bgSubtleSecondary)
|
.background(ElementTheme.colors.bgSubtleSecondary)
|
||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.Center
|
horizontalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
|
|
@ -588,6 +603,22 @@ private fun CantSendMessageBanner() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SuccessorRoomBanner(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
roomSuccessor: SuccessorRoom,
|
||||||
|
onRoomSuccessorClicked: (RoomId) -> Unit
|
||||||
|
) {
|
||||||
|
ComposerAlertMolecule(
|
||||||
|
avatar = null,
|
||||||
|
content = "This room has been replaced and is no longer active".toAnnotatedString(),
|
||||||
|
onSubmitClick = { onRoomSuccessorClicked(roomSuccessor.roomId)},
|
||||||
|
modifier = modifier,
|
||||||
|
isCritical = false,
|
||||||
|
submitText = "Jump to new room"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
@Composable
|
@Composable
|
||||||
internal fun MessagesViewPreview(@PreviewParameter(MessagesStateProvider::class) state: MessagesState) = ElementPreview {
|
internal fun MessagesViewPreview(@PreviewParameter(MessagesStateProvider::class) state: MessagesState) = ElementPreview {
|
||||||
|
|
@ -597,6 +628,7 @@ internal fun MessagesViewPreview(@PreviewParameter(MessagesStateProvider::class)
|
||||||
onRoomDetailsClick = {},
|
onRoomDetailsClick = {},
|
||||||
onEventContentClick = { _, _ -> false },
|
onEventContentClick = { _, _ -> false },
|
||||||
onUserDataClick = {},
|
onUserDataClick = {},
|
||||||
|
onRoomDataClick = { },
|
||||||
onLinkClick = { _, _ -> },
|
onLinkClick = { _, _ -> },
|
||||||
onSendLocationClick = {},
|
onSendLocationClick = {},
|
||||||
onCreatePollClick = {},
|
onCreatePollClick = {},
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ internal fun MessagesViewWithIdentityChangePreview(
|
||||||
onCreatePollClick = {},
|
onCreatePollClick = {},
|
||||||
onJoinCallClick = {},
|
onJoinCallClick = {},
|
||||||
onViewAllPinnedMessagesClick = {},
|
onViewAllPinnedMessagesClick = {},
|
||||||
|
onRoomDataClick = {},
|
||||||
knockRequestsBannerView = {}
|
knockRequestsBannerView = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,8 @@ class PinnedMessagesListPresenter @AssistedInject constructor(
|
||||||
renderTypingNotifications = false,
|
renderTypingNotifications = false,
|
||||||
typingMembers = persistentListOf(),
|
typingMembers = persistentListOf(),
|
||||||
reserveSpace = false,
|
reserveSpace = false,
|
||||||
)
|
),
|
||||||
|
predecessorRoom = room.predecessorRoom(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val timelineProtectionState = timelineProtectionPresenter.present()
|
val timelineProtectionState = timelineProtectionPresenter.present()
|
||||||
|
|
|
||||||
|
|
@ -257,8 +257,9 @@ class TimelinePresenter @AssistedInject constructor(
|
||||||
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
|
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
|
||||||
userHasPermissionToSendReaction = userHasPermissionToSendReaction,
|
userHasPermissionToSendReaction = userHasPermissionToSendReaction,
|
||||||
roomCallState = roomCallState,
|
roomCallState = roomCallState,
|
||||||
pinnedEventIds = roomInfo.pinnedEventIds.orEmpty(),
|
pinnedEventIds = roomInfo.pinnedEventIds,
|
||||||
typingNotificationState = typingNotificationState,
|
typingNotificationState = typingNotificationState,
|
||||||
|
predecessorRoom = room.predecessorRoom(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.features.roomcall.api.RoomCallState
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
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.core.UniqueId
|
||||||
|
import io.element.android.libraries.matrix.api.room.tombstone.PredecessorRoom
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield
|
import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
@ -77,4 +78,5 @@ data class TimelineRoomInfo(
|
||||||
val roomCallState: RoomCallState,
|
val roomCallState: RoomCallState,
|
||||||
val pinnedEventIds: List<EventId>,
|
val pinnedEventIds: List<EventId>,
|
||||||
val typingNotificationState: TypingNotificationState,
|
val typingNotificationState: TypingNotificationState,
|
||||||
|
val predecessorRoom: PredecessorRoom?,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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.TransactionId
|
||||||
import io.element.android.libraries.matrix.api.core.UniqueId
|
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.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.TimelineItemDebugInfo
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
|
import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield
|
import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield
|
||||||
|
|
@ -246,6 +247,7 @@ internal fun aTimelineRoomInfo(
|
||||||
userHasPermissionToSendMessage: Boolean = true,
|
userHasPermissionToSendMessage: Boolean = true,
|
||||||
pinnedEventIds: List<EventId> = emptyList(),
|
pinnedEventIds: List<EventId> = emptyList(),
|
||||||
typingNotificationState: TypingNotificationState = aTypingNotificationState(),
|
typingNotificationState: TypingNotificationState = aTypingNotificationState(),
|
||||||
|
predecessorRoom: PredecessorRoom? = null,
|
||||||
) = TimelineRoomInfo(
|
) = TimelineRoomInfo(
|
||||||
isDm = isDm,
|
isDm = isDm,
|
||||||
name = name,
|
name = name,
|
||||||
|
|
@ -254,4 +256,5 @@ internal fun aTimelineRoomInfo(
|
||||||
roomCallState = aStandByCallState(),
|
roomCallState = aStandByCallState(),
|
||||||
pinnedEventIds = pinnedEventIds,
|
pinnedEventIds = pinnedEventIds,
|
||||||
typingNotificationState = typingNotificationState,
|
typingNotificationState = typingNotificationState,
|
||||||
|
predecessorRoom = predecessorRoom,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ fun TimelineItemVirtualRow(
|
||||||
when (virtual.model) {
|
when (virtual.model) {
|
||||||
is TimelineItemDaySeparatorModel -> TimelineItemDaySeparatorView(virtual.model)
|
is TimelineItemDaySeparatorModel -> TimelineItemDaySeparatorView(virtual.model)
|
||||||
TimelineItemReadMarkerModel -> TimelineItemReadMarkerView()
|
TimelineItemReadMarkerModel -> TimelineItemReadMarkerView()
|
||||||
TimelineItemRoomBeginningModel -> TimelineItemRoomBeginningView(roomName = timelineRoomInfo.name)
|
TimelineItemRoomBeginningModel -> TimelineItemRoomBeginningView(predecessorRoom = timelineRoomInfo.predecessorRoom, roomName = timelineRoomInfo.name)
|
||||||
is TimelineItemLoadingIndicatorModel -> {
|
is TimelineItemLoadingIndicatorModel -> {
|
||||||
TimelineLoadingMoreIndicator(virtual.model.direction)
|
TimelineLoadingMoreIndicator(virtual.model.direction)
|
||||||
val latestEventSink by rememberUpdatedState(eventSink)
|
val latestEventSink by rememberUpdatedState(eventSink)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.timeline.components.virtual
|
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.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
|
@ -19,43 +20,71 @@ import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.messages.impl.R
|
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.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
|
import io.element.android.libraries.designsystem.text.toAnnotatedString
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
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
|
@Composable
|
||||||
fun TimelineItemRoomBeginningView(
|
fun TimelineItemRoomBeginningView(
|
||||||
|
predecessorRoom: PredecessorRoom?,
|
||||||
roomName: String?,
|
roomName: String?,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
Box(
|
Column(
|
||||||
modifier = modifier
|
modifier =modifier.fillMaxWidth()
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
|
||||||
contentAlignment = Alignment.Center,
|
|
||||||
) {
|
) {
|
||||||
val text = if (roomName == null) {
|
if(predecessorRoom != null) {
|
||||||
stringResource(id = R.string.screen_room_timeline_beginning_of_room_no_name)
|
ComposerAlertMolecule(
|
||||||
} else {
|
avatar = null,
|
||||||
stringResource(id = R.string.screen_room_timeline_beginning_of_room, roomName)
|
content = "This room is a continuation of another room".toAnnotatedString(),
|
||||||
|
onSubmitClick = { },
|
||||||
|
modifier = modifier,
|
||||||
|
isCritical = false,
|
||||||
|
submitText = "See old messages"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
@PreviewsDayNight
|
||||||
@Composable
|
@Composable
|
||||||
internal fun TimelineItemRoomBeginningViewPreview() = ElementPreview {
|
internal fun TimelineItemRoomBeginningViewPreview() = ElementPreview {
|
||||||
Column {
|
Column(verticalArrangement = spacedBy(16.dp)){
|
||||||
TimelineItemRoomBeginningView(
|
TimelineItemRoomBeginningView(
|
||||||
|
predecessorRoom = null,
|
||||||
roomName = null,
|
roomName = null,
|
||||||
)
|
)
|
||||||
TimelineItemRoomBeginningView(
|
TimelineItemRoomBeginningView(
|
||||||
|
predecessorRoom = null,
|
||||||
|
roomName = "Room Name",
|
||||||
|
)
|
||||||
|
TimelineItemRoomBeginningView(
|
||||||
|
predecessorRoom = PredecessorRoom(RoomId("!roomId:matrix.org"),EventId("\$eventId:matrix.org") ),
|
||||||
roomName = "Room Name",
|
roomName = "Room Name",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ComposerAlertMolecule(
|
fun ComposerAlertMolecule(
|
||||||
avatar: AvatarData,
|
avatar: AvatarData?,
|
||||||
content: AnnotatedString,
|
content: AnnotatedString,
|
||||||
onSubmitClick: () -> Unit,
|
onSubmitClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
|
@ -71,9 +71,9 @@ fun ComposerAlertMolecule(
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
Avatar(
|
if(avatar != null) {
|
||||||
avatarData = avatar,
|
Avatar(avatarData = avatar)
|
||||||
)
|
}
|
||||||
Text(
|
Text(
|
||||||
text = content,
|
text = content,
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
|
|
@ -101,7 +101,7 @@ fun ComposerAlertMolecule(
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ComposerAlertMoleculePreview(@PreviewParameter(BooleanProvider::class) isCritical: Boolean) = ElementPreview {
|
internal fun ComposerAlertMoleculePreview(@PreviewParameter(BooleanProvider::class) isCritical: Boolean) = ElementPreview {
|
||||||
ComposerAlertMolecule(
|
ComposerAlertMolecule(
|
||||||
avatar = anAvatarData(size = AvatarSize.ComposerAlert),
|
avatar = null,
|
||||||
content = "Alice’s verified identity has changed. Learn more".toAnnotatedString(),
|
content = "Alice’s verified identity has changed. Learn more".toAnnotatedString(),
|
||||||
isCritical = isCritical,
|
isCritical = isCritical,
|
||||||
onSubmitClick = {},
|
onSubmitClick = {},
|
||||||
|
|
|
||||||
|
|
@ -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.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
|
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.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.roomdirectory.RoomVisibility
|
||||||
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
|
@ -55,6 +56,8 @@ interface BaseRoom : Closeable {
|
||||||
*/
|
*/
|
||||||
fun info(): RoomInfo = roomInfoFlow.value
|
fun info(): RoomInfo = roomInfoFlow.value
|
||||||
|
|
||||||
|
fun predecessorRoom(): PredecessorRoom?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A one-to-one is a room with exactly 2 members.
|
* 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).
|
* See [the Matrix spec](https://spec.matrix.org/latest/client-server-api/#default-underride-rules).
|
||||||
|
|
@ -234,7 +237,6 @@ interface BaseRoom : Closeable {
|
||||||
* @param reason - The reason the room is being reported.
|
* @param reason - The reason the room is being reported.
|
||||||
*/
|
*/
|
||||||
suspend fun reportRoom(reason: String?): Result<Unit>
|
suspend fun reportRoom(reason: String?): Result<Unit>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy the room and release all resources associated to it.
|
* Destroy the room and release all resources associated to it.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
|
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.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.api.user.MatrixUser
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.ImmutableMap
|
import kotlinx.collections.immutable.ImmutableMap
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
)
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* 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
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
||||||
|
|
@ -14,11 +14,12 @@ 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.CurrentUserMembership
|
||||||
import io.element.android.libraries.matrix.api.room.RoomInfo
|
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.RoomNotificationMode
|
||||||
import io.element.android.libraries.matrix.api.room.SuccessorRoom
|
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.api.user.MatrixUser
|
||||||
import io.element.android.libraries.matrix.impl.room.history.map
|
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.join.map
|
||||||
import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper
|
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.ImmutableMap
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.collections.immutable.toPersistentMap
|
import kotlinx.collections.immutable.toPersistentMap
|
||||||
|
|
@ -88,11 +89,6 @@ fun RustRoomNotificationMode.map(): RoomNotificationMode = when (this) {
|
||||||
RustRoomNotificationMode.MUTE -> RoomNotificationMode.MUTE
|
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.
|
* Map a RoomHero to a MatrixUser. There is not need to create a RoomHero type on the application side.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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.StateEventType
|
||||||
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
|
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.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.roomdirectory.RoomVisibility
|
||||||
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
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.draft.into
|
||||||
import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher
|
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.member.RoomMemberMapper
|
||||||
import io.element.android.libraries.matrix.impl.room.powerlevels.RoomPowerLevelsMapper
|
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.roomdirectory.map
|
||||||
import io.element.android.libraries.matrix.impl.timeline.toRustReceiptType
|
import io.element.android.libraries.matrix.impl.timeline.toRustReceiptType
|
||||||
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
||||||
|
|
@ -78,6 +80,10 @@ class RustBaseRoom(
|
||||||
})
|
})
|
||||||
}.stateIn(roomCoroutineScope, started = SharingStarted.Lazily, initialValue = initialRoomInfo)
|
}.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 subscribeToSync() = roomSyncSubscriber.subscribe(roomId)
|
||||||
|
|
||||||
override suspend fun updateMembers() {
|
override suspend fun updateMembers() {
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -27,6 +27,11 @@ import org.matrix.rustcomponents.sdk.RoomListService
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList
|
import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList
|
||||||
|
|
||||||
|
private val ROOM_LIST_RUST_FILTERS = listOf(
|
||||||
|
RoomListEntriesDynamicFilterKind.NonLeft,
|
||||||
|
RoomListEntriesDynamicFilterKind.DeduplicateVersions
|
||||||
|
)
|
||||||
|
|
||||||
internal class RoomListFactory(
|
internal class RoomListFactory(
|
||||||
private val innerRoomListService: RoomListService,
|
private val innerRoomListService: RoomListService,
|
||||||
private val sessionCoroutineScope: CoroutineScope,
|
private val sessionCoroutineScope: CoroutineScope,
|
||||||
|
|
@ -55,11 +60,11 @@ internal class RoomListFactory(
|
||||||
|
|
||||||
coroutineScope.launch(coroutineContext) {
|
coroutineScope.launch(coroutineContext) {
|
||||||
innerRoomList = innerProvider()
|
innerRoomList = innerProvider()
|
||||||
innerRoomList?.let { innerRoomList ->
|
innerRoomList.let { innerRoomList ->
|
||||||
innerRoomList.entriesFlow(
|
innerRoomList.entriesFlow(
|
||||||
pageSize = pageSize,
|
pageSize = pageSize,
|
||||||
roomListDynamicEvents = dynamicEvents,
|
roomListDynamicEvents = dynamicEvents,
|
||||||
initialFilterKind = RoomListEntriesDynamicFilterKind.NonLeft
|
initialFilterKind = RoomListEntriesDynamicFilterKind.All(ROOM_LIST_RUST_FILTERS),
|
||||||
).onEach { update ->
|
).onEach { update ->
|
||||||
processor.postUpdate(update)
|
processor.postUpdate(update)
|
||||||
}.launchIn(this)
|
}.launchIn(this)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ 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.RoomInfo
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
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.RoomNotificationMode
|
||||||
import io.element.android.libraries.matrix.api.room.SuccessorRoom
|
import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom
|
||||||
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
|
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.join.JoinRule
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ 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.RoomInfo
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
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.RoomNotificationMode
|
||||||
import io.element.android.libraries.matrix.api.room.SuccessorRoom
|
import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom
|
||||||
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
|
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.join.JoinRule
|
||||||
import io.element.android.libraries.matrix.api.room.message.RoomMessage
|
import io.element.android.libraries.matrix.api.room.message.RoomMessage
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue