Browse Source

Render State Event in the timeline.

feature/jme/open-room-member-details-when-clicking-on-user-data
Benoit Marty 1 year ago committed by Benoit Marty
parent
commit
75f6c99ea9
  1. 4
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt
  2. 24
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt
  3. 52
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt
  4. 1
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt
  5. 99
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt
  6. 5
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt
  7. 58
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStateView.kt
  8. 4
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt
  9. 5
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemProfileChangeContent.kt
  10. 5
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRoomMembershipContent.kt
  11. 21
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStateContent.kt
  12. 5
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStateEventContent.kt

4
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt

@ -20,9 +20,9 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider @@ -20,9 +20,9 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.messages.impl.actionlist.anActionListState
import io.element.android.features.messages.impl.textcomposer.AttachmentSourcePicker
import io.element.android.features.messages.impl.textcomposer.aMessageComposerState
import io.element.android.features.messages.impl.timeline.aTimelineItemContent
import io.element.android.features.messages.impl.timeline.aTimelineItemList
import io.element.android.features.messages.impl.timeline.aTimelineState
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
import io.element.android.libraries.core.data.StableCharSequence
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.matrix.api.core.RoomId
@ -48,7 +48,7 @@ fun aMessagesState() = MessagesState( @@ -48,7 +48,7 @@ fun aMessagesState() = MessagesState(
mode = MessageComposerMode.Normal("Hello"),
),
timelineState = aTimelineState().copy(
timelineItems = aTimelineItemList(aTimelineItemContent()),
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
),
actionListState = anActionListState(),
hasNetworkConnection = true,

24
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt

@ -21,7 +21,8 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem @@ -21,7 +21,8 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition
import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UserId
@ -55,6 +56,12 @@ internal fun aTimelineItemList(content: TimelineItemEventContent): ImmutableList @@ -55,6 +56,12 @@ internal fun aTimelineItemList(content: TimelineItemEventContent): ImmutableList
content = content,
groupPosition = TimelineItemGroupPosition.First
),
// A state event on top of it
aTimelineItemEvent(
isMine = false,
content = aTimelineItemStateEventContent(),
groupPosition = TimelineItemGroupPosition.None
),
// 3 items (First Middle Last) with isMine = true
aTimelineItemEvent(
isMine = true,
@ -71,12 +78,18 @@ internal fun aTimelineItemList(content: TimelineItemEventContent): ImmutableList @@ -71,12 +78,18 @@ internal fun aTimelineItemList(content: TimelineItemEventContent): ImmutableList
content = content,
groupPosition = TimelineItemGroupPosition.First
),
// A state event on top of it
aTimelineItemEvent(
isMine = true,
content = aTimelineItemStateEventContent(),
groupPosition = TimelineItemGroupPosition.None
),
)
}
internal fun aTimelineItemEvent(
isMine: Boolean = false,
content: TimelineItemEventContent = aTimelineItemContent(),
content: TimelineItemEventContent = aTimelineItemTextContent(),
groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.First
): TimelineItem.Event {
val randomId = "\$" + Random.nextInt().toString()
@ -96,10 +109,3 @@ internal fun aTimelineItemEvent( @@ -96,10 +109,3 @@ internal fun aTimelineItemEvent(
groupPosition = groupPosition,
)
}
internal fun aTimelineItemContent(): TimelineItemEventContent {
return TimelineItemTextContent(
body = "Text",
htmlDocument = null
)
}

52
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt

@ -55,6 +55,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter @@ -55,6 +55,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import io.element.android.features.messages.impl.timeline.components.MessageEventBubble
import io.element.android.features.messages.impl.timeline.components.MessageStateEventContainer
import io.element.android.features.messages.impl.timeline.components.TimelineItemReactionsView
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
import io.element.android.features.messages.impl.timeline.components.virtual.TimelineItemDaySeparatorView
@ -63,6 +64,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem @@ -63,6 +64,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentProvider
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemDaySeparatorModel
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemLoadingModel
import io.element.android.libraries.designsystem.components.avatar.Avatar
@ -130,11 +132,12 @@ fun TimelineItemRow( @@ -130,11 +132,12 @@ fun TimelineItemRow(
onLongClick: (TimelineItem.Event) -> Unit,
) {
when (timelineItem) {
is TimelineItem.Virtual -> TimelineItemVirtualRow(
is TimelineItem.Virtual -> {
TimelineItemVirtualRow(
virtual = timelineItem
)
}
is TimelineItem.Event -> {
fun onClick() {
onClick(timelineItem)
}
@ -143,6 +146,14 @@ fun TimelineItemRow( @@ -143,6 +146,14 @@ fun TimelineItemRow(
onLongClick(timelineItem)
}
if (timelineItem.content is TimelineItemStateContent) {
TimelineItemStateEventRow(
event = timelineItem,
isHighlighted = isHighlighted,
onClick = ::onClick,
onLongClick = ::onLongClick
)
} else {
TimelineItemEventRow(
event = timelineItem,
isHighlighted = isHighlighted,
@ -151,6 +162,7 @@ fun TimelineItemRow( @@ -151,6 +162,7 @@ fun TimelineItemRow(
)
}
}
}
}
@Composable
@ -232,6 +244,42 @@ fun TimelineItemEventRow( @@ -232,6 +244,42 @@ fun TimelineItemEventRow(
}
}
@Composable
fun TimelineItemStateEventRow(
event: TimelineItem.Event,
isHighlighted: Boolean,
onClick: () -> Unit,
onLongClick: () -> Unit,
modifier: Modifier = Modifier
) {
val interactionSource = remember { MutableInteractionSource() }
Box(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight(),
contentAlignment = Alignment.Center
) {
MessageStateEventContainer(
isHighlighted = isHighlighted,
interactionSource = interactionSource,
onClick = onClick,
onLongClick = onLongClick,
modifier = Modifier
.zIndex(-1f)
.widthIn(max = 320.dp)
) {
val contentModifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp)
TimelineItemEventContentView(
content = event.content,
interactionSource = interactionSource,
onClick = onClick,
onLongClick = onLongClick,
modifier = contentModifier
)
}
}
}
@Composable
private fun MessageSenderInformation(
sender: String,

1
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt

@ -84,6 +84,7 @@ fun MessageEventBubble( @@ -84,6 +84,7 @@ fun MessageEventBubble(
fun Modifier.offsetForItem(): Modifier {
return if (state.isMine) {
// FIXME setting y offset to -12.dp can overlap a state event displayed above.
offset(y = -(12.dp))
} else {
offset(x = 20.dp, y = -(12.dp))

99
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageStateEventContainer.kt

@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.messages.impl.timeline.components
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.ElementTheme
import io.element.android.libraries.designsystem.theme.components.Surface
private val CORNER_RADIUS = 8.dp
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MessageStateEventContainer(
isHighlighted: Boolean,
interactionSource: MutableInteractionSource,
modifier: Modifier = Modifier,
onClick: () -> Unit = {},
onLongClick: () -> Unit = {},
content: @Composable () -> Unit = {},
) {
val backgroundColor = if (isHighlighted) {
ElementTheme.colors.messageHighlightedBackground
} else {
Color.Companion.Transparent
}
val shape = RoundedCornerShape(CORNER_RADIUS)
Surface(
modifier = modifier
.widthIn(min = 80.dp)
.clip(shape)
.combinedClickable(
onClick = onClick,
onLongClick = onLongClick,
indication = rememberRipple(),
interactionSource = interactionSource
),
color = backgroundColor,
shape = shape,
content = content
)
}
@Preview
@Composable
internal fun MessageStateEventContainerLightPreview() =
ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun MessageStateEventContainerDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
Column {
MessageStateEventContainer(
isHighlighted = false,
interactionSource = MutableInteractionSource(),
) {
Spacer(modifier = Modifier.size(width = 120.dp, height = 32.dp))
}
MessageStateEventContainer(
isHighlighted = true,
interactionSource = MutableInteractionSource(),
) {
Spacer(modifier = Modifier.size(width = 120.dp, height = 32.dp))
}
}
}

5
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt

@ -23,6 +23,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt @@ -23,6 +23,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
@ -58,5 +59,9 @@ fun TimelineItemEventContentView( @@ -58,5 +59,9 @@ fun TimelineItemEventContentView(
content = content,
modifier = modifier
)
is TimelineItemStateContent -> TimelineItemStateView(
content = content,
modifier = modifier
)
}
}

58
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStateView.kt

@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.messages.impl.timeline.components.event
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.sp
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.theme.components.Text
@Composable
fun TimelineItemStateView(
content: TimelineItemStateContent,
modifier: Modifier = Modifier
) {
Text(
modifier = modifier,
color = MaterialTheme.colorScheme.secondary,
fontSize = 13.sp,
text = content.body,
textAlign = TextAlign.Center,
)
}
@Preview
@Composable
internal fun TimelineItemStateViewLightPreview() = ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun TimelineItemStateViewDarkPreview() = ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
TimelineItemStateView(
content = aTimelineItemStateEventContent(),
)
}

4
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt

@ -65,3 +65,7 @@ fun aTimelineItemTextContent() = TimelineItemTextContent( @@ -65,3 +65,7 @@ fun aTimelineItemTextContent() = TimelineItemTextContent(
)
fun aTimelineItemUnknownContent() = TimelineItemUnknownContent
fun aTimelineItemStateEventContent() = TimelineItemStateEventContent(
body = "A state event",
)

5
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemProfileChangeContent.kt

@ -16,11 +16,8 @@ @@ -16,11 +16,8 @@
package io.element.android.features.messages.impl.timeline.model.event
import org.jsoup.nodes.Document
data class TimelineItemProfileChangeContent(
override val body: String,
override val htmlDocument: Document? = null
) : TimelineItemTextBasedContent {
) : TimelineItemStateContent {
override val type: String = "TimelineItemProfileChangeContent"
}

5
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemRoomMembershipContent.kt

@ -16,11 +16,8 @@ @@ -16,11 +16,8 @@
package io.element.android.features.messages.impl.timeline.model.event
import org.jsoup.nodes.Document
data class TimelineItemRoomMembershipContent(
override val body: String,
override val htmlDocument: Document? = null
) : TimelineItemTextBasedContent {
) : TimelineItemStateContent {
override val type: String = "TimelineItemRoomMembershipContent"
}

21
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStateContent.kt

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.messages.impl.timeline.model.event
sealed interface TimelineItemStateContent : TimelineItemEventContent {
val body: String
}

5
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemStateEventContent.kt

@ -16,11 +16,8 @@ @@ -16,11 +16,8 @@
package io.element.android.features.messages.impl.timeline.model.event
import org.jsoup.nodes.Document
data class TimelineItemStateEventContent(
override val body: String,
override val htmlDocument: Document? = null
) : TimelineItemTextBasedContent {
) : TimelineItemStateContent {
override val type: String = "TimelineItemStateEventContent"
}

Loading…
Cancel
Save