Merge pull request #443 from vector-im/feature/bma/timelineStateEventFormatting
Timeline state event formatting
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_create_room_action_create_room">"Neuer Raum"</string>
|
||||
<string name="screen_create_room_action_invite_people">"Personen einladen"</string>
|
||||
<string name="screen_create_room_add_people_title">"Personen hinzufügen"</string>
|
||||
<string name="screen_create_room_private_option_title">"Privater Raum (nur auf Einladung)"</string>
|
||||
<string name="screen_create_room_room_name_label">"Raumname"</string>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_change_server_error_invalid_homeserver">"Wir konnten diesen Homeserver nicht erreichen. Bitte überprüfen Sie, ob Sie die Homeserver-URL korrekt eingegeben haben. Wenn die URL korrekt ist, wenden Sie sich an Ihren Homeserver-Administrator, um weitere Hilfe zu erhalten."</string>
|
||||
<string name="screen_change_server_error_no_sliding_sync_message">"Dieser Server unterstützt derzeit kein Sliding Sync."</string>
|
||||
<string name="screen_change_server_form_header">"Homeserver-URL"</string>
|
||||
<string name="screen_change_server_form_notice">"Sie können nur eine Verbindung zu einem vorhandenen Server herstellen, der Sliding Sync unterstützt. Ihr Homeserver-Administrator muss dies konfigurieren. %1$s"</string>
|
||||
<string name="screen_change_server_subtitle">"Wie lautet die Adresse deines Servers?"</string>
|
||||
<string name="screen_login_title">"Willkommen zurück!"</string>
|
||||
<string name="screen_login_password_hint">"Passwort"</string>
|
||||
|
||||
@@ -41,6 +41,7 @@ dependencies {
|
||||
implementation(projects.libraries.textcomposer)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
implementation(projects.libraries.dateformatter.api)
|
||||
implementation(projects.libraries.eventformatter.api)
|
||||
implementation(projects.libraries.mediapickers.api)
|
||||
implementation(projects.libraries.featureflag.api)
|
||||
implementation(projects.libraries.mediaupload.api)
|
||||
|
||||
@@ -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(
|
||||
mode = MessageComposerMode.Normal("Hello"),
|
||||
),
|
||||
timelineState = aTimelineState().copy(
|
||||
timelineItems = aTimelineItemList(aTimelineItemContent()),
|
||||
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
|
||||
),
|
||||
actionListState = anActionListState(),
|
||||
hasNetworkConnection = true,
|
||||
|
||||
@@ -63,6 +63,7 @@ import io.element.android.features.messages.impl.actionlist.model.TimelineItemAc
|
||||
import io.element.android.features.messages.impl.textcomposer.AttachmentSourcePicker
|
||||
import io.element.android.features.messages.impl.textcomposer.MessageComposerEvents
|
||||
import io.element.android.features.messages.impl.textcomposer.MessageComposerView
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelineView
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView
|
||||
@@ -139,6 +140,11 @@ fun MessagesView(
|
||||
}
|
||||
}
|
||||
|
||||
fun onExpandGroupClick(event: TimelineItem.GroupedEvents) {
|
||||
Timber.v("onExpandGroupClick= ${event.id}")
|
||||
state.timelineState.eventSink(TimelineEvents.ToggleExpandGroup(event))
|
||||
}
|
||||
|
||||
fun onActionSelected(action: TimelineItemAction, event: TimelineItem.Event) {
|
||||
state.eventSink(MessagesEvents.HandleAction(action, event))
|
||||
}
|
||||
@@ -189,7 +195,8 @@ fun MessagesView(
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding),
|
||||
onMessageClicked = ::onMessageClicked,
|
||||
onMessageLongClicked = ::onMessageLongClicked
|
||||
onMessageLongClicked = ::onMessageLongClicked,
|
||||
onExpandGroupClick = ::onExpandGroupClick,
|
||||
)
|
||||
},
|
||||
snackbarHost = {
|
||||
@@ -214,6 +221,7 @@ fun MessagesViewContent(
|
||||
modifier: Modifier = Modifier,
|
||||
onMessageClicked: (TimelineItem.Event) -> Unit = {},
|
||||
onMessageLongClicked: (TimelineItem.Event) -> Unit = {},
|
||||
onExpandGroupClick: (TimelineItem.GroupedEvents) -> Unit = {},
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
@@ -227,7 +235,8 @@ fun MessagesViewContent(
|
||||
state = state.timelineState,
|
||||
modifier = Modifier.weight(1f),
|
||||
onMessageClicked = onMessageClicked,
|
||||
onMessageLongClicked = onMessageLongClicked
|
||||
onMessageLongClicked = onMessageLongClicked,
|
||||
onExpandGroupClick = onExpandGroupClick,
|
||||
)
|
||||
}
|
||||
MessageComposerView(
|
||||
|
||||
@@ -24,6 +24,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
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.libraries.architecture.Presenter
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -53,20 +54,25 @@ class ActionListPresenter @Inject constructor() : Presenter<ActionListState> {
|
||||
)
|
||||
}
|
||||
|
||||
fun CoroutineScope.computeForMessage(timelineItem: TimelineItem.Event, target: MutableState<ActionListState.Target>) = launch {
|
||||
private fun CoroutineScope.computeForMessage(timelineItem: TimelineItem.Event, target: MutableState<ActionListState.Target>) = launch {
|
||||
target.value = ActionListState.Target.Loading(timelineItem)
|
||||
val actions =
|
||||
if (timelineItem.content is TimelineItemRedactedContent) {
|
||||
emptyList()
|
||||
} else {
|
||||
mutableListOf(
|
||||
TimelineItemAction.Reply,
|
||||
TimelineItemAction.Forward,
|
||||
TimelineItemAction.Copy,
|
||||
).also {
|
||||
if (timelineItem.isMine) {
|
||||
it.add(TimelineItemAction.Edit)
|
||||
it.add(TimelineItemAction.Redact)
|
||||
when (timelineItem.content) {
|
||||
is TimelineItemRedactedContent,
|
||||
is TimelineItemStateContent -> {
|
||||
// TODO Add Share action (also) here, and developer options
|
||||
emptyList()
|
||||
}
|
||||
else -> {
|
||||
mutableListOf(
|
||||
TimelineItemAction.Reply,
|
||||
TimelineItemAction.Forward,
|
||||
TimelineItemAction.Copy,
|
||||
).also {
|
||||
if (timelineItem.isMine) {
|
||||
it.add(TimelineItemAction.Edit)
|
||||
it.add(TimelineItemAction.Redact)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
|
||||
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
|
||||
|
||||
sealed interface TimelineEvents {
|
||||
object LoadMore : TimelineEvents
|
||||
data class SetHighlightedEvent(val eventId: EventId?) : TimelineEvents
|
||||
data class ToggleExpandGroup(val event: TimelineItem.GroupedEvents) : TimelineEvents
|
||||
}
|
||||
|
||||
@@ -17,15 +17,18 @@
|
||||
package io.element.android.features.messages.impl.timeline
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
|
||||
import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.bool.orFalse
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||
@@ -42,6 +45,7 @@ private const val backPaginationPageSize = 50
|
||||
|
||||
class TimelinePresenter @Inject constructor(
|
||||
private val timelineItemsFactory: TimelineItemsFactory,
|
||||
private val timelineItemGrouper: TimelineItemGrouper,
|
||||
room: MatrixRoom,
|
||||
) : Presenter<TimelineState> {
|
||||
|
||||
@@ -53,6 +57,8 @@ class TimelinePresenter @Inject constructor(
|
||||
val highlightedEventId: MutableState<EventId?> = rememberSaveable {
|
||||
mutableStateOf(null)
|
||||
}
|
||||
val expandedGroups = remember { mutableStateMapOf<String, Boolean>() }
|
||||
|
||||
val timelineItems = timelineItemsFactory
|
||||
.flow()
|
||||
.collectAsState()
|
||||
@@ -65,6 +71,9 @@ class TimelinePresenter @Inject constructor(
|
||||
when (event) {
|
||||
TimelineEvents.LoadMore -> localCoroutineScope.loadMore(paginationState.value)
|
||||
is TimelineEvents.SetHighlightedEvent -> highlightedEventId.value = event.eventId
|
||||
is TimelineEvents.ToggleExpandGroup -> {
|
||||
expandedGroups[event.event.identifier()] = expandedGroups[event.event.identifier()].orFalse().not()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +92,7 @@ class TimelinePresenter @Inject constructor(
|
||||
return TimelineState(
|
||||
highlightedEventId = highlightedEventId.value,
|
||||
paginationState = paginationState.value,
|
||||
timelineItems = timelineItems.value.toImmutableList(),
|
||||
timelineItems = timelineItemGrouper.group(timelineItems.value, expandedGroups).toImmutableList(),
|
||||
eventSink = ::handleEvents
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
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
|
||||
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(
|
||||
groupPosition = groupPosition,
|
||||
)
|
||||
}
|
||||
|
||||
internal fun aTimelineItemContent(): TimelineItemEventContent {
|
||||
return TimelineItemTextContent(
|
||||
body = "Text",
|
||||
htmlDocument = null
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package io.element.android.features.messages.impl.timeline
|
||||
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -50,19 +50,24 @@ import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.LastBaseline
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
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.R
|
||||
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.group.GroupHeaderView
|
||||
import io.element.android.features.messages.impl.timeline.components.virtual.TimelineItemDaySeparatorView
|
||||
import io.element.android.features.messages.impl.timeline.components.virtual.TimelineLoadingMoreIndicator
|
||||
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
|
||||
@@ -82,6 +87,7 @@ fun TimelineView(
|
||||
modifier: Modifier = Modifier,
|
||||
onMessageClicked: (TimelineItem.Event) -> Unit = {},
|
||||
onMessageLongClicked: (TimelineItem.Event) -> Unit = {},
|
||||
onExpandGroupClick: (TimelineItem.GroupedEvents) -> Unit = {},
|
||||
) {
|
||||
|
||||
fun onReachedLoadMore() {
|
||||
@@ -93,8 +99,6 @@ fun TimelineView(
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = lazyListState,
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.Bottom,
|
||||
reverseLayout = true
|
||||
) {
|
||||
itemsIndexed(
|
||||
@@ -104,9 +108,10 @@ fun TimelineView(
|
||||
) { index, timelineItem ->
|
||||
TimelineItemRow(
|
||||
timelineItem = timelineItem,
|
||||
isHighlighted = timelineItem.identifier() == state.highlightedEventId?.value,
|
||||
highlightedItem = state.highlightedEventId?.value,
|
||||
onClick = onMessageClicked,
|
||||
onLongClick = onMessageLongClicked
|
||||
onLongClick = onMessageLongClicked,
|
||||
onExpandGroupClick = onExpandGroupClick,
|
||||
)
|
||||
if (index == state.timelineItems.lastIndex) {
|
||||
onReachedLoadMore()
|
||||
@@ -125,16 +130,20 @@ fun TimelineView(
|
||||
@Composable
|
||||
fun TimelineItemRow(
|
||||
timelineItem: TimelineItem,
|
||||
isHighlighted: Boolean,
|
||||
highlightedItem: String?,
|
||||
onClick: (TimelineItem.Event) -> Unit,
|
||||
onLongClick: (TimelineItem.Event) -> Unit,
|
||||
onExpandGroupClick: (TimelineItem.GroupedEvents) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
when (timelineItem) {
|
||||
is TimelineItem.Virtual -> TimelineItemVirtualRow(
|
||||
virtual = timelineItem
|
||||
)
|
||||
is TimelineItem.Virtual -> {
|
||||
TimelineItemVirtualRow(
|
||||
virtual = timelineItem,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
is TimelineItem.Event -> {
|
||||
|
||||
fun onClick() {
|
||||
onClick(timelineItem)
|
||||
}
|
||||
@@ -143,12 +152,54 @@ fun TimelineItemRow(
|
||||
onLongClick(timelineItem)
|
||||
}
|
||||
|
||||
TimelineItemEventRow(
|
||||
event = timelineItem,
|
||||
isHighlighted = isHighlighted,
|
||||
onClick = ::onClick,
|
||||
onLongClick = ::onLongClick
|
||||
)
|
||||
if (timelineItem.content is TimelineItemStateContent) {
|
||||
TimelineItemStateEventRow(
|
||||
event = timelineItem,
|
||||
isHighlighted = highlightedItem == timelineItem.identifier(),
|
||||
onClick = ::onClick,
|
||||
onLongClick = ::onLongClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
TimelineItemEventRow(
|
||||
event = timelineItem,
|
||||
isHighlighted = highlightedItem == timelineItem.identifier(),
|
||||
onClick = ::onClick,
|
||||
onLongClick = ::onLongClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
is TimelineItem.GroupedEvents -> {
|
||||
fun onExpandGroupClick() {
|
||||
onExpandGroupClick(timelineItem)
|
||||
}
|
||||
|
||||
Column(modifier = modifier.animateContentSize()) {
|
||||
GroupHeaderView(
|
||||
text = pluralStringResource(
|
||||
id = R.plurals.room_timeline_state_changes,
|
||||
count = timelineItem.events.size,
|
||||
timelineItem.events.size
|
||||
),
|
||||
isExpanded = timelineItem.expanded,
|
||||
isHighlighted = !timelineItem.expanded && timelineItem.events.any { it.identifier() == highlightedItem },
|
||||
onClick = ::onExpandGroupClick,
|
||||
)
|
||||
if (timelineItem.expanded) {
|
||||
Column {
|
||||
timelineItem.events.forEach { subGroupEvent ->
|
||||
TimelineItemRow(
|
||||
timelineItem = subGroupEvent,
|
||||
highlightedItem = highlightedItem,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
onExpandGroupClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,6 +283,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,
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
content = content,
|
||||
modifier = modifier
|
||||
)
|
||||
is TimelineItemStateContent -> TimelineItemStateView(
|
||||
content = content,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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.group
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ExpandLess
|
||||
import androidx.compose.material.icons.filled.ExpandMore
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
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.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.Surface
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
|
||||
private val CORNER_RADIUS = 8.dp
|
||||
|
||||
@Composable
|
||||
fun GroupHeaderView(
|
||||
text: String,
|
||||
isExpanded: Boolean,
|
||||
isHighlighted: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val backgroundColor = if (isHighlighted) {
|
||||
ElementTheme.colors.messageHighlightedBackground
|
||||
} else {
|
||||
Color.Companion.Transparent
|
||||
}
|
||||
val shape = RoundedCornerShape(CORNER_RADIUS)
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.clip(shape)
|
||||
.clickable(onClick = onClick),
|
||||
color = backgroundColor,
|
||||
shape = shape,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
fontSize = 13.sp
|
||||
)
|
||||
val icon = if (isExpanded) {
|
||||
Icons.Default.ExpandLess
|
||||
} else {
|
||||
Icons.Default.ExpandMore
|
||||
}
|
||||
Icon(icon, "", tint = MaterialTheme.colorScheme.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun GroupHeaderViewLightPreview() =
|
||||
ElementPreviewLight { ContentToPreview() }
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun GroupHeaderViewDarkPreview() =
|
||||
ElementPreviewDark { ContentToPreview() }
|
||||
|
||||
@Composable
|
||||
private fun ContentToPreview() {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
GroupHeaderView(
|
||||
text = "8 room changes (expanded)",
|
||||
isExpanded = true,
|
||||
isHighlighted = false,
|
||||
onClick = {}
|
||||
)
|
||||
GroupHeaderView(
|
||||
text = "8 room changes (not expanded)",
|
||||
isExpanded = false,
|
||||
isHighlighted = false,
|
||||
onClick = {}
|
||||
)
|
||||
GroupHeaderView(
|
||||
text = "8 room changes (expanded/h)",
|
||||
isExpanded = true,
|
||||
isHighlighted = true,
|
||||
onClick = {}
|
||||
)
|
||||
GroupHeaderView(
|
||||
text = "8 room changes (not expanded/h)",
|
||||
isExpanded = false,
|
||||
isHighlighted = true,
|
||||
onClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline.factories.event
|
||||
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
|
||||
@@ -26,7 +27,6 @@ import io.element.android.libraries.matrix.api.timeline.item.event.RedactedConte
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
|
||||
import javax.inject.Inject
|
||||
@@ -43,15 +43,15 @@ class TimelineItemContentFactory @Inject constructor(
|
||||
private val failedToParseStateFactory: TimelineItemContentFailedToParseStateFactory
|
||||
) {
|
||||
|
||||
fun create(itemContent: EventContent): TimelineItemEventContent {
|
||||
return when (itemContent) {
|
||||
fun create(eventTimelineItem: EventTimelineItem): TimelineItemEventContent {
|
||||
return when (val itemContent = eventTimelineItem.content) {
|
||||
is FailedToParseMessageLikeContent -> failedToParseMessageFactory.create(itemContent)
|
||||
is FailedToParseStateContent -> failedToParseStateFactory.create(itemContent)
|
||||
is MessageContent -> messageFactory.create(itemContent)
|
||||
is ProfileChangeContent -> profileChangeFactory.create(itemContent)
|
||||
is ProfileChangeContent -> profileChangeFactory.create(eventTimelineItem)
|
||||
is RedactedContent -> redactedMessageFactory.create(itemContent)
|
||||
is RoomMembershipContent -> roomMembershipFactory.create(itemContent)
|
||||
is StateContent -> stateFactory.create(itemContent)
|
||||
is RoomMembershipContent -> roomMembershipFactory.create(eventTimelineItem)
|
||||
is StateContent -> stateFactory.create(eventTimelineItem)
|
||||
is StickerContent -> stickerFactory.create(itemContent)
|
||||
is UnableToDecryptContent -> utdFactory.create(itemContent)
|
||||
is UnknownContent -> TimelineItemUnknownContent
|
||||
|
||||
@@ -17,13 +17,18 @@
|
||||
package io.element.android.features.messages.impl.timeline.factories.event
|
||||
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemProfileChangeContent
|
||||
import io.element.android.libraries.core.extensions.orEmpty
|
||||
import io.element.android.libraries.eventformatter.api.TimelineEventFormatter
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimelineItemContentProfileChangeFactory @Inject constructor() {
|
||||
class TimelineItemContentProfileChangeFactory @Inject constructor(
|
||||
private val timelineEventFormatter: TimelineEventFormatter,
|
||||
) {
|
||||
|
||||
fun create(content: ProfileChangeContent): TimelineItemEventContent {
|
||||
return TimelineItemUnknownContent
|
||||
fun create(eventTimelineItem: EventTimelineItem): TimelineItemEventContent {
|
||||
val text = timelineEventFormatter.format(eventTimelineItem)
|
||||
return TimelineItemProfileChangeContent(text.orEmpty().toString())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,18 @@
|
||||
package io.element.android.features.messages.impl.timeline.factories.event
|
||||
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRoomMembershipContent
|
||||
import io.element.android.libraries.core.extensions.orEmpty
|
||||
import io.element.android.libraries.eventformatter.api.TimelineEventFormatter
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimelineItemContentRoomMembershipFactory @Inject constructor() {
|
||||
class TimelineItemContentRoomMembershipFactory @Inject constructor(
|
||||
private val timelineEventFormatter: TimelineEventFormatter,
|
||||
) {
|
||||
|
||||
fun create(content: RoomMembershipContent): TimelineItemEventContent {
|
||||
return TimelineItemUnknownContent
|
||||
fun create(eventTimelineItem: EventTimelineItem): TimelineItemEventContent {
|
||||
val text = timelineEventFormatter.format(eventTimelineItem)
|
||||
return TimelineItemRoomMembershipContent(text.orEmpty().toString())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,18 @@
|
||||
package io.element.android.features.messages.impl.timeline.factories.event
|
||||
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateEventContent
|
||||
import io.element.android.libraries.core.extensions.orEmpty
|
||||
import io.element.android.libraries.eventformatter.api.TimelineEventFormatter
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimelineItemContentStateFactory @Inject constructor() {
|
||||
class TimelineItemContentStateFactory @Inject constructor(
|
||||
private val timelineEventFormatter: TimelineEventFormatter,
|
||||
) {
|
||||
|
||||
fun create(content: StateContent): TimelineItemEventContent {
|
||||
return TimelineItemUnknownContent
|
||||
fun create(eventTimelineItem: EventTimelineItem): TimelineItemEventContent {
|
||||
val text = timelineEventFormatter.format(eventTimelineItem)
|
||||
return TimelineItemStateEventContent(text.orEmpty().toString())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ class TimelineItemEventFactory @Inject constructor(
|
||||
senderId = currentSender,
|
||||
senderDisplayName = senderDisplayName,
|
||||
senderAvatar = senderAvatarData,
|
||||
content = contentFactory.create(currentTimelineItem.event.content),
|
||||
content = contentFactory.create(currentTimelineItem.event),
|
||||
isMine = currentTimelineItem.event.isOwn,
|
||||
groupPosition = groupPosition,
|
||||
reactionsState = currentTimelineItem.computeReactionsState()
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.groups
|
||||
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEmoteContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemNoticeContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemProfileChangeContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRoomMembershipContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateEventContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
|
||||
import io.element.android.libraries.core.bool.orFalse
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimelineItemGrouper @Inject constructor() {
|
||||
/**
|
||||
* Create a new list of [TimelineItem] by grouping some of them into [TimelineItem.GroupedEvents].
|
||||
*/
|
||||
fun group(from: List<TimelineItem>, expandedGroups: Map<String, Boolean>): List<TimelineItem> {
|
||||
val result = mutableListOf<TimelineItem>()
|
||||
val currentGroup = mutableListOf<TimelineItem.Event>()
|
||||
from.forEach { timelineItem ->
|
||||
if (timelineItem is TimelineItem.Event && timelineItem.canBeGrouped()) {
|
||||
currentGroup.add(0, timelineItem)
|
||||
} else {
|
||||
// timelineItem cannot be grouped
|
||||
if (currentGroup.isNotEmpty()) {
|
||||
// There is a pending group, create a TimelineItem.GroupedEvents if there is more than 1 Event in the pending group.
|
||||
result.addGroup(currentGroup, expandedGroups)
|
||||
currentGroup.clear()
|
||||
}
|
||||
result.add(timelineItem)
|
||||
}
|
||||
}
|
||||
if (currentGroup.isNotEmpty()) {
|
||||
result.addGroup(currentGroup, expandedGroups)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun TimelineItem.Event.canBeGrouped(): Boolean {
|
||||
return when (content) {
|
||||
is TimelineItemEncryptedContent,
|
||||
is TimelineItemImageContent,
|
||||
TimelineItemRedactedContent,
|
||||
is TimelineItemEmoteContent,
|
||||
is TimelineItemNoticeContent,
|
||||
is TimelineItemTextContent,
|
||||
TimelineItemUnknownContent -> false
|
||||
is TimelineItemProfileChangeContent,
|
||||
is TimelineItemRoomMembershipContent,
|
||||
is TimelineItemStateEventContent -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will add a group if there is more than 1 item, else add the item to the list.
|
||||
*/
|
||||
private fun MutableList<TimelineItem>.addGroup(
|
||||
group: MutableList<TimelineItem.Event>,
|
||||
expandedGroups: Map<String, Boolean>,
|
||||
) {
|
||||
if (group.size == 1) {
|
||||
// Do not create a group with just 1 item, just add the item to the result
|
||||
add(group.first())
|
||||
} else {
|
||||
add(
|
||||
TimelineItem.GroupedEvents(
|
||||
expanded = expandedGroups[group.first().id + "_group"].orFalse(),
|
||||
events = group.toImmutableList()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import io.element.android.features.messages.impl.timeline.model.virtual.Timeline
|
||||
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
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Immutable
|
||||
sealed interface TimelineItem {
|
||||
@@ -29,11 +30,13 @@ sealed interface TimelineItem {
|
||||
fun identifier(): String = when (this) {
|
||||
is Event -> id
|
||||
is Virtual -> id
|
||||
is GroupedEvents -> id
|
||||
}
|
||||
|
||||
fun contentType(): String = when (this) {
|
||||
is Event -> content.type
|
||||
is Virtual -> model.type
|
||||
is GroupedEvents -> "groupedEvent"
|
||||
}
|
||||
|
||||
@Immutable
|
||||
@@ -60,4 +63,13 @@ sealed interface TimelineItem {
|
||||
|
||||
val safeSenderName: String = senderDisplayName ?: senderId.value
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class GroupedEvents(
|
||||
val expanded: Boolean,
|
||||
val events: ImmutableList<Event>,
|
||||
) : TimelineItem {
|
||||
// use first id with a suffix
|
||||
val id = events.first().id + "_group"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,3 +65,7 @@ fun aTimelineItemTextContent() = TimelineItemTextContent(
|
||||
)
|
||||
|
||||
fun aTimelineItemUnknownContent() = TimelineItemUnknownContent
|
||||
|
||||
fun aTimelineItemStateEventContent() = TimelineItemStateEventContent(
|
||||
body = "A state event",
|
||||
)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2022 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
|
||||
|
||||
data class TimelineItemProfileChangeContent(
|
||||
override val body: String,
|
||||
) : TimelineItemStateContent {
|
||||
override val type: String = "TimelineItemProfileChangeContent"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2022 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
|
||||
|
||||
data class TimelineItemRoomMembershipContent(
|
||||
override val body: String,
|
||||
) : TimelineItemStateContent {
|
||||
override val type: String = "TimelineItemRoomMembershipContent"
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2022 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
|
||||
|
||||
data class TimelineItemStateEventContent(
|
||||
override val body: String,
|
||||
) : TimelineItemStateContent {
|
||||
override val type: String = "TimelineItemStateEventContent"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="room_timeline_state_changes">
|
||||
<item quantity="one">"%1$d cambio en la sala"</item>
|
||||
<item quantity="other">"%1$d cambios en la sala"</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="room_timeline_state_changes">
|
||||
<item quantity="one">"%1$d modifica alla stanza"</item>
|
||||
<item quantity="other">"%1$d modifiche alla stanza"</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="room_timeline_state_changes">
|
||||
<item quantity="one">"%1$d schimbare a camerii"</item>
|
||||
<item quantity="few">"%1$d schimbări ale camerei"</item>
|
||||
<item quantity="other">"%1$d schimbări ale camerei"</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
@@ -1,5 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<plurals name="room_timeline_state_changes">
|
||||
<item quantity="one">"%1$d room change"</item>
|
||||
<item quantity="other">"%1$d room changes"</item>
|
||||
</plurals>
|
||||
<string name="screen_room_attachment_source_camera">"Camera"</string>
|
||||
<string name="screen_room_attachment_source_camera_photo">"Take photo"</string>
|
||||
<string name="screen_room_attachment_source_camera_video">"Record a video"</string>
|
||||
|
||||
@@ -28,6 +28,7 @@ import io.element.android.features.messages.impl.actionlist.ActionListPresenter
|
||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||
import io.element.android.features.messages.impl.textcomposer.MessageComposerPresenter
|
||||
import io.element.android.features.messages.impl.timeline.TimelinePresenter
|
||||
import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper
|
||||
import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
|
||||
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
|
||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||
@@ -138,6 +139,7 @@ class MessagesPresenterTest {
|
||||
)
|
||||
val timelinePresenter = TimelinePresenter(
|
||||
timelineItemsFactory = aTimelineItemsFactory(),
|
||||
timelineItemGrouper = TimelineItemGrouper(),
|
||||
room = matrixRoom,
|
||||
)
|
||||
val actionListPresenter = ActionListPresenter()
|
||||
|
||||
@@ -31,26 +31,39 @@ import io.element.android.features.messages.impl.timeline.factories.event.Timeli
|
||||
import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemDaySeparatorFactory
|
||||
import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory
|
||||
import io.element.android.libraries.dateformatter.test.FakeDaySeparatorFormatter
|
||||
import io.element.android.libraries.eventformatter.api.TimelineEventFormatter
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||
|
||||
internal fun aTimelineItemsFactory() = TimelineItemsFactory(
|
||||
dispatchers = testCoroutineDispatchers(),
|
||||
eventItemFactory = TimelineItemEventFactory(
|
||||
TimelineItemContentFactory(
|
||||
messageFactory = TimelineItemContentMessageFactory(),
|
||||
redactedMessageFactory = TimelineItemContentRedactedFactory(),
|
||||
stickerFactory = TimelineItemContentStickerFactory(),
|
||||
utdFactory = TimelineItemContentUTDFactory(),
|
||||
roomMembershipFactory = TimelineItemContentRoomMembershipFactory(),
|
||||
profileChangeFactory = TimelineItemContentProfileChangeFactory(),
|
||||
stateFactory = TimelineItemContentStateFactory(),
|
||||
failedToParseMessageFactory = TimelineItemContentFailedToParseMessageFactory(),
|
||||
failedToParseStateFactory = TimelineItemContentFailedToParseStateFactory()
|
||||
)
|
||||
),
|
||||
virtualItemFactory = TimelineItemVirtualFactory(
|
||||
daySeparatorFactory = TimelineItemDaySeparatorFactory(
|
||||
FakeDaySeparatorFormatter()
|
||||
internal fun aTimelineItemsFactory(): TimelineItemsFactory {
|
||||
val timelineEventFormatter = aTimelineEventFormatter()
|
||||
return TimelineItemsFactory(
|
||||
dispatchers = testCoroutineDispatchers(),
|
||||
eventItemFactory = TimelineItemEventFactory(
|
||||
TimelineItemContentFactory(
|
||||
messageFactory = TimelineItemContentMessageFactory(),
|
||||
redactedMessageFactory = TimelineItemContentRedactedFactory(),
|
||||
stickerFactory = TimelineItemContentStickerFactory(),
|
||||
utdFactory = TimelineItemContentUTDFactory(),
|
||||
roomMembershipFactory = TimelineItemContentRoomMembershipFactory(timelineEventFormatter),
|
||||
profileChangeFactory = TimelineItemContentProfileChangeFactory(timelineEventFormatter),
|
||||
stateFactory = TimelineItemContentStateFactory(timelineEventFormatter),
|
||||
failedToParseMessageFactory = TimelineItemContentFailedToParseMessageFactory(),
|
||||
failedToParseStateFactory = TimelineItemContentFailedToParseStateFactory()
|
||||
)
|
||||
),
|
||||
virtualItemFactory = TimelineItemVirtualFactory(
|
||||
daySeparatorFactory = TimelineItemDaySeparatorFactory(
|
||||
FakeDaySeparatorFormatter()
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
internal fun aTimelineEventFormatter(): TimelineEventFormatter {
|
||||
return object : TimelineEventFormatter {
|
||||
override fun format(event: EventTimelineItem): CharSequence {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
||||
package io.element.android.features.messages.textcomposer
|
||||
|
||||
import app.cash.molecule.RecompositionClock
|
||||
@@ -50,6 +52,7 @@ import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
||||
import io.element.android.libraries.textcomposer.MessageComposerMode
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
||||
@@ -23,8 +23,13 @@ import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.fixtures.aTimelineItemsFactory
|
||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
||||
import io.element.android.features.messages.impl.timeline.TimelinePresenter
|
||||
import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.matrix.test.room.anEventTimelineItem
|
||||
import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
@@ -33,6 +38,7 @@ class TimelinePresenterTest {
|
||||
fun `present - initial state`() = runTest {
|
||||
val presenter = TimelinePresenter(
|
||||
timelineItemsFactory = aTimelineItemsFactory(),
|
||||
timelineItemGrouper = TimelineItemGrouper(),
|
||||
room = FakeMatrixRoom(),
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
@@ -49,6 +55,7 @@ class TimelinePresenterTest {
|
||||
fun `present - load more`() = runTest {
|
||||
val presenter = TimelinePresenter(
|
||||
timelineItemsFactory = aTimelineItemsFactory(),
|
||||
timelineItemGrouper = TimelineItemGrouper(),
|
||||
room = FakeMatrixRoom(),
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
@@ -71,6 +78,7 @@ class TimelinePresenterTest {
|
||||
fun `present - set highlighted event`() = runTest {
|
||||
val presenter = TimelinePresenter(
|
||||
timelineItemsFactory = aTimelineItemsFactory(),
|
||||
timelineItemGrouper = TimelineItemGrouper(),
|
||||
room = FakeMatrixRoom(),
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
@@ -87,4 +95,37 @@ class TimelinePresenterTest {
|
||||
assertThat(withoutHighlightedState.highlightedEventId).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - expand and collapse grouped events`() = runTest {
|
||||
val fakeTimeline = FakeMatrixTimeline(
|
||||
initialTimelineItems = listOf(
|
||||
MatrixTimelineItem.Event(anEventTimelineItem() /* This is a groupable event */),
|
||||
MatrixTimelineItem.Event(anEventTimelineItem() /* This is a groupable event */),
|
||||
)
|
||||
)
|
||||
val fakeRoom = FakeMatrixRoom(matrixTimeline = fakeTimeline)
|
||||
val presenter = TimelinePresenter(
|
||||
timelineItemsFactory = aTimelineItemsFactory(),
|
||||
timelineItemGrouper = TimelineItemGrouper(),
|
||||
room = fakeRoom,
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
fakeTimeline.updateTimelineItems { it }
|
||||
val loadedState = awaitItem()
|
||||
val group1 = loadedState.timelineItems.first() as TimelineItem.GroupedEvents
|
||||
assertThat(group1.expanded).isFalse()
|
||||
loadedState.eventSink.invoke(TimelineEvents.ToggleExpandGroup(group1))
|
||||
val withExpandedGroup = awaitItem()
|
||||
val group2 = withExpandedGroup.timelineItems.first() as TimelineItem.GroupedEvents
|
||||
assertThat(group2.expanded).isTrue()
|
||||
withExpandedGroup.eventSink.invoke(TimelineEvents.ToggleExpandGroup(group2))
|
||||
val withCollapsedGroup = awaitItem()
|
||||
val group3 = withCollapsedGroup.timelineItems.first() as TimelineItem.GroupedEvents
|
||||
assertThat(group3.expanded).isFalse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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.timeline.groups
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.messages.fixtures.aMessageEvent
|
||||
import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper
|
||||
import io.element.android.features.messages.impl.timeline.model.AggregatedReaction
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions
|
||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateEventContent
|
||||
import io.element.android.features.messages.impl.timeline.model.virtual.aTimelineItemDaySeparatorModel
|
||||
import io.element.android.libraries.designsystem.components.avatar.anAvatarData
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID_2
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import org.junit.Test
|
||||
|
||||
class TimelineItemGrouperTest {
|
||||
private val sut = TimelineItemGrouper()
|
||||
|
||||
private val aGroupableItem = TimelineItem.Event(
|
||||
id = AN_EVENT_ID.value,
|
||||
senderId = A_USER_ID,
|
||||
senderAvatar = anAvatarData(),
|
||||
senderDisplayName = "",
|
||||
content = TimelineItemStateEventContent(body = "a state event"),
|
||||
reactionsState = TimelineItemReactions(emptyList<AggregatedReaction>().toImmutableList())
|
||||
)
|
||||
private val aNonGroupableItem = aMessageEvent()
|
||||
private val aNonGroupableItemNoEvent = TimelineItem.Virtual("virtual", aTimelineItemDaySeparatorModel("Today"))
|
||||
|
||||
@Test
|
||||
fun `test empty`() {
|
||||
val result = sut.group(emptyList(), emptyMap())
|
||||
assertThat(result).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test non groupables`() {
|
||||
val result = sut.group(
|
||||
listOf(
|
||||
aNonGroupableItem,
|
||||
aNonGroupableItem,
|
||||
),
|
||||
emptyMap()
|
||||
)
|
||||
assertThat(result).isEqualTo(
|
||||
listOf(
|
||||
aNonGroupableItem,
|
||||
aNonGroupableItem,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test groupables and ensure reordering`() {
|
||||
val result = sut.group(
|
||||
listOf(
|
||||
aGroupableItem.copy(id = AN_EVENT_ID_2.value),
|
||||
aGroupableItem,
|
||||
),
|
||||
emptyMap()
|
||||
)
|
||||
assertThat(result).isEqualTo(
|
||||
listOf(
|
||||
TimelineItem.GroupedEvents(
|
||||
expanded = false,
|
||||
events = listOf(
|
||||
aGroupableItem,
|
||||
aGroupableItem.copy(id = AN_EVENT_ID_2.value),
|
||||
).toImmutableList()
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test groupables expanded`() {
|
||||
val result = sut.group(
|
||||
listOf(
|
||||
aGroupableItem,
|
||||
aGroupableItem.copy(id = AN_EVENT_ID_2.value),
|
||||
),
|
||||
mapOf("${AN_EVENT_ID_2.value}_group" to true)
|
||||
)
|
||||
assertThat(result).isEqualTo(
|
||||
listOf(
|
||||
TimelineItem.GroupedEvents(
|
||||
expanded = true,
|
||||
events = listOf(
|
||||
aGroupableItem.copy(id = AN_EVENT_ID_2.value),
|
||||
aGroupableItem,
|
||||
).toImmutableList()
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test 1 groupable, not group must be created`() {
|
||||
val listsToTest = listOf(
|
||||
listOf(aGroupableItem),
|
||||
listOf(aGroupableItem, aNonGroupableItem),
|
||||
listOf(aGroupableItem, aNonGroupableItemNoEvent),
|
||||
listOf(aNonGroupableItem, aGroupableItem),
|
||||
listOf(aNonGroupableItemNoEvent, aGroupableItem),
|
||||
listOf(aNonGroupableItem, aGroupableItem, aNonGroupableItem),
|
||||
listOf(aNonGroupableItemNoEvent, aGroupableItem, aNonGroupableItemNoEvent),
|
||||
listOf(aGroupableItem, aNonGroupableItem, aGroupableItem),
|
||||
listOf(aGroupableItem, aNonGroupableItemNoEvent, aGroupableItem),
|
||||
listOf(aNonGroupableItem),
|
||||
listOf(aNonGroupableItemNoEvent),
|
||||
)
|
||||
listsToTest.forEach { listToTest ->
|
||||
val result = sut.group(listToTest, emptyMap())
|
||||
assertThat(result).isEqualTo(listToTest)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test 3 blocks`() {
|
||||
val result = sut.group(
|
||||
listOf(
|
||||
aGroupableItem,
|
||||
aGroupableItem,
|
||||
aNonGroupableItem,
|
||||
aGroupableItem,
|
||||
aGroupableItem,
|
||||
aGroupableItem,
|
||||
),
|
||||
emptyMap()
|
||||
)
|
||||
assertThat(result).isEqualTo(
|
||||
listOf(
|
||||
TimelineItem.GroupedEvents(
|
||||
expanded = false,
|
||||
events = listOf(
|
||||
aGroupableItem,
|
||||
aGroupableItem,
|
||||
).toImmutableList()
|
||||
),
|
||||
aNonGroupableItem,
|
||||
TimelineItem.GroupedEvents(
|
||||
expanded = false,
|
||||
events = listOf(
|
||||
aGroupableItem,
|
||||
aGroupableItem,
|
||||
aGroupableItem,
|
||||
).toImmutableList()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,13 @@
|
||||
<item quantity="one">"1 Person"</item>
|
||||
<item quantity="other">"%1$d Personen"</item>
|
||||
</plurals>
|
||||
<string name="screen_room_details_already_invited">"Bereits eingeladen"</string>
|
||||
<string name="screen_room_details_share_room_title">"Raum teilen"</string>
|
||||
<string name="screen_dm_details_block_alert_action">"Blockieren"</string>
|
||||
<string name="screen_dm_details_block_user">"Nutzer blockieren"</string>
|
||||
<string name="screen_dm_details_unblock_alert_action">"Blockierung aufheben"</string>
|
||||
<string name="screen_dm_details_unblock_user">"Nutzer entblockieren"</string>
|
||||
<string name="screen_room_details_invite_people_title">"Personen einladen"</string>
|
||||
<string name="screen_room_details_leave_room_title">"Raum verlassen"</string>
|
||||
<string name="screen_room_details_security_title">"Sicherheit"</string>
|
||||
<string name="screen_room_details_topic_title">"Thema"</string>
|
||||
|
||||
@@ -48,6 +48,7 @@ dependencies {
|
||||
implementation(projects.libraries.testtags)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
implementation(projects.libraries.dateformatter.api)
|
||||
implementation(projects.libraries.eventformatter.api)
|
||||
implementation(projects.features.invitelist.api)
|
||||
implementation(projects.features.networkmonitor.api)
|
||||
implementation(projects.features.leaveroom.api)
|
||||
@@ -63,6 +64,7 @@ dependencies {
|
||||
testImplementation(libs.test.robolectric)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.dateformatter.test)
|
||||
testImplementation(projects.libraries.eventformatter.test)
|
||||
testImplementation(projects.libraries.permissions.noop)
|
||||
testImplementation(projects.features.invitelist.test)
|
||||
testImplementation(projects.features.networkmonitor.test)
|
||||
|
||||
@@ -1,330 +0,0 @@
|
||||
/*
|
||||
* 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.roomlist.impl
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.di.ApplicationContext
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.OtherState
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import io.element.android.libraries.ui.strings.R as StringR
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultRoomLastMessageFormatter @Inject constructor(
|
||||
// TODO replace with StringProvider
|
||||
@ApplicationContext private val context: Context,
|
||||
private val matrixClient: MatrixClient,
|
||||
) : RoomLastMessageFormatter {
|
||||
|
||||
override fun processMessageItem(event: EventTimelineItem, isDmRoom: Boolean): CharSequence? {
|
||||
val isOutgoing = event.sender == matrixClient.sessionId
|
||||
val senderDisplayName = (event.senderProfile as? ProfileTimelineDetails.Ready)?.displayName ?: event.sender.value
|
||||
return when (val content = event.content) {
|
||||
is MessageContent -> processMessageContents(content, senderDisplayName, isDmRoom)
|
||||
RedactedContent -> {
|
||||
val message = context.getString(StringR.string.common_message_removed)
|
||||
if (!isDmRoom) {
|
||||
prefix(message, senderDisplayName)
|
||||
} else {
|
||||
message
|
||||
}
|
||||
}
|
||||
is StickerContent -> {
|
||||
content.body
|
||||
}
|
||||
is UnableToDecryptContent -> {
|
||||
val message = context.getString(StringR.string.common_decryption_error)
|
||||
if (!isDmRoom) {
|
||||
prefix(message, senderDisplayName)
|
||||
} else {
|
||||
message
|
||||
}
|
||||
}
|
||||
is RoomMembershipContent -> {
|
||||
processRoomMembershipChange(content, senderDisplayName, isOutgoing)
|
||||
}
|
||||
is ProfileChangeContent -> {
|
||||
processProfileChangeContent(content, senderDisplayName, isOutgoing)
|
||||
}
|
||||
is StateContent -> {
|
||||
processRoomStateChange(content, senderDisplayName, isOutgoing)
|
||||
}
|
||||
is FailedToParseMessageLikeContent, is FailedToParseStateContent, is UnknownContent -> {
|
||||
prefixIfNeeded(context.getString(StringR.string.common_unsupported_event), senderDisplayName, isDmRoom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processMessageContents(messageContent: MessageContent, senderDisplayName: String, isDmRoom: Boolean): CharSequence? {
|
||||
val messageType: MessageType = messageContent.type ?: return null
|
||||
|
||||
val internalMessage = when (messageType) {
|
||||
// Doesn't need a prefix
|
||||
is EmoteMessageType -> {
|
||||
return "- $senderDisplayName ${messageType.body}"
|
||||
}
|
||||
is TextMessageType -> {
|
||||
messageType.body
|
||||
}
|
||||
is VideoMessageType -> {
|
||||
context.getString(StringR.string.common_video)
|
||||
}
|
||||
is ImageMessageType -> {
|
||||
context.getString(StringR.string.common_image)
|
||||
}
|
||||
is FileMessageType -> {
|
||||
context.getString(StringR.string.common_file)
|
||||
}
|
||||
is AudioMessageType -> {
|
||||
context.getString(StringR.string.common_audio)
|
||||
}
|
||||
UnknownMessageType -> {
|
||||
context.getString(StringR.string.common_unsupported_event)
|
||||
}
|
||||
is NoticeMessageType -> {
|
||||
messageType.body
|
||||
}
|
||||
}
|
||||
return prefixIfNeeded(internalMessage, senderDisplayName, isDmRoom)
|
||||
}
|
||||
|
||||
private fun processRoomMembershipChange(membershipContent: RoomMembershipContent, senderDisplayName: String, senderIsYou: Boolean): CharSequence? {
|
||||
val userId = membershipContent.userId
|
||||
val memberIsYou = userId == matrixClient.sessionId
|
||||
return when (val change = membershipContent.change) {
|
||||
MembershipChange.JOINED -> if (memberIsYou) {
|
||||
context.getString(R.string.state_event_room_join_by_you)
|
||||
} else {
|
||||
context.getString(R.string.state_event_room_join, userId.value)
|
||||
}
|
||||
MembershipChange.LEFT -> if (memberIsYou) {
|
||||
context.getString(R.string.state_event_room_leave_by_you)
|
||||
} else {
|
||||
context.getString(R.string.state_event_room_leave, userId.value)
|
||||
}
|
||||
MembershipChange.BANNED, MembershipChange.KICKED_AND_BANNED -> if (senderIsYou) {
|
||||
context.getString(R.string.state_event_room_ban_by_you, userId.value)
|
||||
} else {
|
||||
context.getString(R.string.state_event_room_ban, senderDisplayName, userId.value)
|
||||
}
|
||||
MembershipChange.UNBANNED -> if (senderIsYou) {
|
||||
context.getString(R.string.state_event_room_unban_by_you, userId.value)
|
||||
} else {
|
||||
context.getString(R.string.state_event_room_unban, senderDisplayName, userId.value)
|
||||
}
|
||||
MembershipChange.KICKED -> if (senderIsYou) {
|
||||
context.getString(R.string.state_event_room_remove_by_you, userId.value)
|
||||
} else {
|
||||
context.getString(R.string.state_event_room_remove, senderDisplayName, userId.value)
|
||||
}
|
||||
MembershipChange.INVITED -> if (senderIsYou) {
|
||||
context.getString(R.string.state_event_room_invite_by_you, userId.value)
|
||||
} else if (memberIsYou) {
|
||||
context.getString(R.string.state_event_room_invite_you, senderDisplayName)
|
||||
} else {
|
||||
context.getString(R.string.state_event_room_invite, senderDisplayName, userId.value)
|
||||
}
|
||||
MembershipChange.INVITATION_ACCEPTED -> if (memberIsYou) {
|
||||
context.getString(R.string.state_event_room_invite_accepted_by_you)
|
||||
} else {
|
||||
context.getString(R.string.state_event_room_invite_accepted, userId.value)
|
||||
}
|
||||
MembershipChange.INVITATION_REJECTED -> if (memberIsYou) {
|
||||
context.getString(R.string.state_event_room_reject_by_you)
|
||||
} else {
|
||||
context.getString(R.string.state_event_room_reject, userId.value)
|
||||
}
|
||||
MembershipChange.INVITATION_REVOKED -> if (senderIsYou) {
|
||||
context.getString(R.string.state_event_room_third_party_revoked_invite_by_you, userId.value)
|
||||
} else {
|
||||
context.getString(R.string.state_event_room_third_party_revoked_invite, senderDisplayName, userId.value)
|
||||
}
|
||||
MembershipChange.KNOCKED -> if (memberIsYou) {
|
||||
context.getString(R.string.state_event_room_knock_by_you)
|
||||
} else {
|
||||
context.getString(R.string.state_event_room_knock, userId.value)
|
||||
}
|
||||
MembershipChange.KNOCK_ACCEPTED -> if (senderIsYou) {
|
||||
context.getString(R.string.state_event_room_knock_accepted_by_you, userId.value)
|
||||
} else {
|
||||
context.getString(R.string.state_event_room_knock_accepted, senderDisplayName, userId.value)
|
||||
}
|
||||
MembershipChange.KNOCK_RETRACTED -> if (memberIsYou) {
|
||||
context.getString(R.string.state_event_room_knock_retracted_by_you)
|
||||
} else {
|
||||
context.getString(R.string.state_event_room_knock_retracted, userId.value)
|
||||
}
|
||||
MembershipChange.KNOCK_DENIED -> if (senderIsYou) {
|
||||
context.getString(R.string.state_event_room_knock_denied_by_you, userId.value)
|
||||
} else if (memberIsYou) {
|
||||
context.getString(R.string.state_event_room_knock_denied_you, senderDisplayName)
|
||||
} else {
|
||||
context.getString(R.string.state_event_room_knock_denied, senderDisplayName, userId.value)
|
||||
}
|
||||
else -> {
|
||||
Timber.v("Filtering timeline item for room membership: $membershipContent")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processRoomStateChange(stateContent: StateContent, senderDisplayName: String, senderIsYou: Boolean): CharSequence? {
|
||||
return when (val content = stateContent.content) {
|
||||
is OtherState.RoomAvatar -> {
|
||||
val hasAvatarUrl = content.url != null
|
||||
when {
|
||||
senderIsYou && hasAvatarUrl -> context.getString(R.string.state_event_room_avatar_changed_by_you)
|
||||
senderIsYou && !hasAvatarUrl -> context.getString(R.string.state_event_room_avatar_removed_by_you)
|
||||
!senderIsYou && hasAvatarUrl -> context.getString(R.string.state_event_room_avatar_changed, senderDisplayName)
|
||||
else -> context.getString(R.string.state_event_room_avatar_removed, senderDisplayName)
|
||||
}
|
||||
}
|
||||
is OtherState.RoomCreate -> {
|
||||
if (senderIsYou) {
|
||||
context.getString(R.string.state_event_room_created_by_you)
|
||||
} else {
|
||||
context.getString(R.string.state_event_room_created, senderDisplayName)
|
||||
}
|
||||
}
|
||||
is OtherState.RoomEncryption -> context.getString(StringR.string.common_encryption_enabled)
|
||||
is OtherState.RoomName -> {
|
||||
val hasRoomName = content.name != null
|
||||
when {
|
||||
senderIsYou && hasRoomName -> context.getString(R.string.state_event_room_name_changed_by_you, content.name)
|
||||
senderIsYou && !hasRoomName -> context.getString(R.string.state_event_room_name_removed_by_you)
|
||||
!senderIsYou && hasRoomName -> context.getString(R.string.state_event_room_name_changed, senderDisplayName, content.name)
|
||||
else -> context.getString(R.string.state_event_room_name_removed, senderDisplayName)
|
||||
}
|
||||
}
|
||||
is OtherState.RoomThirdPartyInvite -> {
|
||||
if (content.displayName == null) {
|
||||
Timber.e("RoomThirdPartyInvite undisplayable due to missing name")
|
||||
return null
|
||||
}
|
||||
if (senderIsYou) {
|
||||
context.getString(R.string.state_event_room_third_party_invite_by_you, content.displayName)
|
||||
} else {
|
||||
context.getString(R.string.state_event_room_third_party_invite, senderDisplayName, content.displayName)
|
||||
}
|
||||
}
|
||||
is OtherState.RoomTopic -> {
|
||||
val hasRoomTopic = content.topic != null
|
||||
when {
|
||||
senderIsYou && hasRoomTopic -> context.getString(R.string.state_event_room_topic_changed_by_you, content.topic)
|
||||
senderIsYou && !hasRoomTopic -> context.getString(R.string.state_event_room_topic_removed_by_you)
|
||||
!senderIsYou && hasRoomTopic -> context.getString(R.string.state_event_room_topic_changed, senderDisplayName, content.topic)
|
||||
else -> context.getString(R.string.state_event_room_topic_removed, senderDisplayName)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Timber.v("Filtering timeline item for room state change: $content")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processProfileChangeContent(
|
||||
profileChangeContent: ProfileChangeContent,
|
||||
senderDisplayName: String,
|
||||
senderIsYou: Boolean
|
||||
): String? = profileChangeContent.run {
|
||||
val displayNameChanged = displayName != prevDisplayName
|
||||
val avatarChanged = avatarUrl != prevAvatarUrl
|
||||
return when {
|
||||
avatarChanged && displayNameChanged -> {
|
||||
val message = processProfileChangeContent(profileChangeContent.copy(avatarUrl = null, prevAvatarUrl = null), senderDisplayName, senderIsYou)
|
||||
val avatarChangedToo = context.getString(R.string.state_event_avatar_changed_too)
|
||||
"$message\n$avatarChangedToo"
|
||||
}
|
||||
displayNameChanged -> {
|
||||
if (displayName != null && prevDisplayName != null) {
|
||||
if (senderIsYou) {
|
||||
context.getString(R.string.state_event_display_name_changed_from_by_you, prevDisplayName, displayName)
|
||||
} else {
|
||||
context.getString(R.string.state_event_display_name_changed_from, senderDisplayName, prevDisplayName, displayName)
|
||||
}
|
||||
} else if (displayName != null) {
|
||||
if (senderIsYou) {
|
||||
context.getString(R.string.state_event_display_name_set_by_you, displayName)
|
||||
} else {
|
||||
context.getString(R.string.state_event_display_name_set, senderDisplayName, displayName)
|
||||
}
|
||||
} else {
|
||||
if (senderIsYou) {
|
||||
context.getString(R.string.state_event_display_name_removed_by_you, prevDisplayName)
|
||||
} else {
|
||||
context.getString(R.string.state_event_display_name_removed, senderDisplayName, prevDisplayName)
|
||||
}
|
||||
}
|
||||
}
|
||||
avatarChanged -> {
|
||||
if (senderIsYou) {
|
||||
context.getString(R.string.state_event_avatar_url_changed_by_you)
|
||||
} else {
|
||||
context.getString(R.string.state_event_avatar_url_changed, senderDisplayName)
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun prefixIfNeeded(message: String, senderDisplayName: String, isDmRoom: Boolean): CharSequence = if (isDmRoom) {
|
||||
message
|
||||
} else {
|
||||
prefix(message, senderDisplayName)
|
||||
}
|
||||
|
||||
private fun prefix(message: String, senderDisplayName: String): AnnotatedString {
|
||||
return buildAnnotatedString {
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(senderDisplayName)
|
||||
}
|
||||
append(": ")
|
||||
append(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormat
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
|
||||
import io.element.android.libraries.designsystem.utils.handleSnackbarMessage
|
||||
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
@@ -199,7 +200,7 @@ class RoomListPresenter @Inject constructor(
|
||||
hasUnread = roomSummary.details.unreadNotificationCount > 0,
|
||||
timestamp = lastMessageTimestampFormatter.format(roomSummary.details.lastMessageTimestamp),
|
||||
lastMessage = roomSummary.details.lastMessage?.let { message ->
|
||||
roomLastMessageFormatter.processMessageItem(message.event, roomSummary.details.isDirect)
|
||||
roomLastMessageFormatter.format(message.event, roomSummary.details.isDirect)
|
||||
}.orEmpty(),
|
||||
avatarData = avatarData,
|
||||
)
|
||||
|
||||
@@ -1,40 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_roomlist_main_space_title">"Alle Chats"</string>
|
||||
<string name="state_event_avatar_changed_too">"(Avatar wurde ebenfalls geändert)"</string>
|
||||
<string name="state_event_avatar_url_changed">"%1$s hat seinen Avatar geändert"</string>
|
||||
<string name="state_event_avatar_url_changed_by_you">"Du hast deinen Avatar geändert"</string>
|
||||
<string name="state_event_display_name_changed_from">"%1$s hat den Anzeigenamen von %2$s in %3$s geändert"</string>
|
||||
<string name="state_event_display_name_changed_from_by_you">"Du hast deinen Anzeigenamen von %1$s in %2$s geändert"</string>
|
||||
<string name="state_event_display_name_removed">"%1$s hat den Anzeigenamen entfernt (war %2$s)"</string>
|
||||
<string name="state_event_display_name_removed_by_you">"Du hast deinen Anzeigenamen entfernt (war %1$s)"</string>
|
||||
<string name="state_event_display_name_set">"%1$s hat den Anzeigenamen auf %2$s gesetzt"</string>
|
||||
<string name="state_event_display_name_set_by_you">"Du hast deinen Anzeigenamen auf %1$s gesetzt"</string>
|
||||
<string name="state_event_room_avatar_changed">"%1$s hat den Raum-Avatar geändert"</string>
|
||||
<string name="state_event_room_avatar_changed_by_you">"Du hast den Raum-Avatar geändert"</string>
|
||||
<string name="state_event_room_avatar_removed">"%1$s hat den Raum-Avatar entfernt"</string>
|
||||
<string name="state_event_room_created">"%1$s hat den Raum erstellt"</string>
|
||||
<string name="state_event_room_created_by_you">"Du hast den Raum erstellt"</string>
|
||||
<string name="state_event_room_invite">"%1$s hat %2$s eingeladen"</string>
|
||||
<string name="state_event_room_invite_accepted">"%1$s hat die Einladung angenommen"</string>
|
||||
<string name="state_event_room_invite_accepted_by_you">"Du hast die Einladung angenommen"</string>
|
||||
<string name="state_event_room_invite_by_you">"Du hast %1$s eingeladen"</string>
|
||||
<string name="state_event_room_invite_you">"%1$s hat dich eingeladen"</string>
|
||||
<string name="state_event_room_join">"%1$s ist dem Raum beigetreten"</string>
|
||||
<string name="state_event_room_join_by_you">"Du bist dem Raum beigetreten"</string>
|
||||
<string name="state_event_room_knock_denied_you">"%1$s hat deine Beitrittsanfrage abgelehnt"</string>
|
||||
<string name="state_event_room_leave">"%1$s hat den Raum verlassen"</string>
|
||||
<string name="state_event_room_leave_by_you">"Du hast den Raum verlassen"</string>
|
||||
<string name="state_event_room_name_changed">"%1$s hat den Raumnamen geändert in: %2$s"</string>
|
||||
<string name="state_event_room_name_changed_by_you">"Sie haben den Raumnamen geändert in: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s hat den Raumnamen entfernt"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"Du hast den Raumnamen entfernt"</string>
|
||||
<string name="state_event_room_reject">"%1$s hat die Einladung abgelehnt"</string>
|
||||
<string name="state_event_room_reject_by_you">"Du hast die Einladung abgelehnt"</string>
|
||||
<string name="state_event_room_remove">"%1$s hat %2$s entfernt"</string>
|
||||
<string name="state_event_room_remove_by_you">"Du hast %1$s entfernt"</string>
|
||||
<string name="state_event_room_topic_changed">"%1$s hat das Thema geändert zu: %2$s"</string>
|
||||
<string name="state_event_room_topic_changed_by_you">"Sie haben das Thema geändert zu: %1$s"</string>
|
||||
<string name="state_event_room_topic_removed">"%1$s hat das Raumthema entfernt"</string>
|
||||
<string name="state_event_room_topic_removed_by_you">"Du hast das Raumthema entfernt"</string>
|
||||
</resources>
|
||||
@@ -4,58 +4,4 @@
|
||||
<string name="screen_roomlist_main_space_title">"Todos los chats"</string>
|
||||
<string name="session_verification_banner_message">"Parece que estás usando un nuevo dispositivo. Verifica que eres tú para acceder a tus mensajes cifrados."</string>
|
||||
<string name="session_verification_banner_title">"Accede a tu historial de mensajes"</string>
|
||||
<string name="state_event_avatar_changed_too">"(el avatar también cambió)"</string>
|
||||
<string name="state_event_avatar_url_changed">"%1$s cambió su avatar"</string>
|
||||
<string name="state_event_avatar_url_changed_by_you">"Cambiaste tu avatar"</string>
|
||||
<string name="state_event_display_name_changed_from">"%1$s cambió su nombre de %2$s a %3$s"</string>
|
||||
<string name="state_event_display_name_changed_from_by_you">"Cambiaste tu nombre de %1$s a %2$s"</string>
|
||||
<string name="state_event_display_name_removed">"%1$s eliminó su nombre (era %2$s)"</string>
|
||||
<string name="state_event_display_name_removed_by_you">"Eliminaste tu nombre (era %1$s)"</string>
|
||||
<string name="state_event_display_name_set">"%1$s cambió su nombre a %2$s"</string>
|
||||
<string name="state_event_display_name_set_by_you">"Cambiaste tu nombre a %1$s"</string>
|
||||
<string name="state_event_room_avatar_changed">"%1$s cambió el avatar de la sala"</string>
|
||||
<string name="state_event_room_avatar_changed_by_you">"Cambiaste el avatar de la sala"</string>
|
||||
<string name="state_event_room_avatar_removed">"%1$s eliminó el avatar de la sala"</string>
|
||||
<string name="state_event_room_avatar_removed_by_you">"Eliminaste el avatar de la sala"</string>
|
||||
<string name="state_event_room_ban">"%1$s expulsó permanentemente a %2$s"</string>
|
||||
<string name="state_event_room_ban_by_you">"Expulsaste permanentemente a %1$s"</string>
|
||||
<string name="state_event_room_created">"%1$s creó la sala"</string>
|
||||
<string name="state_event_room_created_by_you">"Tú creaste la sala"</string>
|
||||
<string name="state_event_room_invite">"%1$s invitó a %2$s"</string>
|
||||
<string name="state_event_room_invite_accepted">"%1$s aceptó la invitación"</string>
|
||||
<string name="state_event_room_invite_accepted_by_you">"Aceptaste la invitación"</string>
|
||||
<string name="state_event_room_invite_by_you">"Invitaste a %1$s"</string>
|
||||
<string name="state_event_room_invite_you">"%1$s te invitó."</string>
|
||||
<string name="state_event_room_join">"%1$s se unió a la sala"</string>
|
||||
<string name="state_event_room_join_by_you">"Te uniste a la sala"</string>
|
||||
<string name="state_event_room_knock">"%1$s solicitó unirse"</string>
|
||||
<string name="state_event_room_knock_accepted">"%1$s permitió que %2$s se uniera"</string>
|
||||
<string name="state_event_room_knock_accepted_by_you">"%1$s te permitió unirte"</string>
|
||||
<string name="state_event_room_knock_by_you">"Solicitaste unirte"</string>
|
||||
<string name="state_event_room_knock_denied">"%1$s rechazó la solicitud de %2$s para unirse"</string>
|
||||
<string name="state_event_room_knock_denied_by_you">"Rechazaste la solicitud de %1$s para unirte"</string>
|
||||
<string name="state_event_room_knock_denied_you">"%1$s rechazó su solicitud para unirte"</string>
|
||||
<string name="state_event_room_knock_retracted">"%1$s ya no está interesado en unirse"</string>
|
||||
<string name="state_event_room_knock_retracted_by_you">"Cancelaste tu solicitud de unirte"</string>
|
||||
<string name="state_event_room_leave">"%1$s salió de la sala"</string>
|
||||
<string name="state_event_room_leave_by_you">"Saliste de la sala"</string>
|
||||
<string name="state_event_room_name_changed">"%1$s cambió el nombre de la sala a: %2$s"</string>
|
||||
<string name="state_event_room_name_changed_by_you">"Cambiaste el nombre de la sala a: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s eliminó el nombre de la sala"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"Eliminaste el nombre de la sala"</string>
|
||||
<string name="state_event_room_reject">"%1$s rechazó la invitación"</string>
|
||||
<string name="state_event_room_reject_by_you">"Rechazaste la invitación"</string>
|
||||
<string name="state_event_room_remove">"%1$s echó a %2$s"</string>
|
||||
<string name="state_event_room_remove_by_you">"Echaste a %1$s"</string>
|
||||
<string name="state_event_room_third_party_invite">"%1$s envió una invitación a %2$s para unirse a la sala"</string>
|
||||
<string name="state_event_room_third_party_invite_by_you">"Enviaste una invitación a %1$s para unirse a la sala"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite">"%1$s revocó la invitación a %2$s para unirse a la sala"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite_by_you">"Revocaste la invitación de %1$s para unirse a la sala"</string>
|
||||
<string name="state_event_room_topic_changed">"%1$s cambió el tema a: %2$s"</string>
|
||||
<string name="state_event_room_topic_changed_by_you">"Cambiaste el tema a: %1$s"</string>
|
||||
<string name="state_event_room_topic_removed">"%1$s eliminó el tema de la sala"</string>
|
||||
<string name="state_event_room_topic_removed_by_you">"Eliminaste el tema de la sala"</string>
|
||||
<string name="state_event_room_unban">"%1$s readmitió a %2$s"</string>
|
||||
<string name="state_event_room_unban_by_you">"Readmitiste a %1$s"</string>
|
||||
<string name="state_event_room_unknown_membership_change">"%1$s realizó un cambio desconocido en su membresía"</string>
|
||||
</resources>
|
||||
@@ -4,58 +4,4 @@
|
||||
<string name="screen_roomlist_main_space_title">"Tutte le conversazioni"</string>
|
||||
<string name="session_verification_banner_message">"Sembra che tu stia utilizzando un nuovo dispositivo. Verifica di essere tu per accedere ai tuoi messaggi crittografati."</string>
|
||||
<string name="session_verification_banner_title">"Accedi alla cronologia dei messaggi"</string>
|
||||
<string name="state_event_avatar_changed_too">"(anche l\'avatar è stato cambiato)"</string>
|
||||
<string name="state_event_avatar_url_changed">"%1$s ha cambiato il proprio avatar"</string>
|
||||
<string name="state_event_avatar_url_changed_by_you">"Hai cambiato il tuo avatar"</string>
|
||||
<string name="state_event_display_name_changed_from">"%1$s ha cambiato il proprio nome visualizzato da %2$s a %3$s"</string>
|
||||
<string name="state_event_display_name_changed_from_by_you">"Hai cambiato il tuo nome visualizzato da %1$s a %2$s"</string>
|
||||
<string name="state_event_display_name_removed">"%1$s ha rimosso il proprio nome visualizzato (era %2$s)"</string>
|
||||
<string name="state_event_display_name_removed_by_you">"Hai rimosso il tuo nome visualizzato (era %1$s)"</string>
|
||||
<string name="state_event_display_name_set">"%1$s ha impostato il proprio nome visualizzato su %2$s"</string>
|
||||
<string name="state_event_display_name_set_by_you">"Hai impostato il tuo nome visualizzato su %1$s"</string>
|
||||
<string name="state_event_room_avatar_changed">"%1$s ha cambiato l\'avatar della stanza"</string>
|
||||
<string name="state_event_room_avatar_changed_by_you">"Hai cambiato l\'avatar della stanza"</string>
|
||||
<string name="state_event_room_avatar_removed">"%1$s ha rimosso l\'avatar della stanza"</string>
|
||||
<string name="state_event_room_avatar_removed_by_you">"Hai rimosso l\'avatar della stanza"</string>
|
||||
<string name="state_event_room_ban">"%1$s ha rimosso %2$s"</string>
|
||||
<string name="state_event_room_ban_by_you">"Hai rimosso %1$s"</string>
|
||||
<string name="state_event_room_created">"%1$s ha creato la stanza"</string>
|
||||
<string name="state_event_room_created_by_you">"Hai creato la stanza"</string>
|
||||
<string name="state_event_room_invite">"%1$s ha invitato %2$s"</string>
|
||||
<string name="state_event_room_invite_accepted">"%1$s ha accettato l\'invito"</string>
|
||||
<string name="state_event_room_invite_accepted_by_you">"Hai accettato l\'invito"</string>
|
||||
<string name="state_event_room_invite_by_you">"Hai invitato %1$s"</string>
|
||||
<string name="state_event_room_invite_you">"%1$s ti ha invitato"</string>
|
||||
<string name="state_event_room_join">"%1$s si è unito alla stanza"</string>
|
||||
<string name="state_event_room_join_by_you">"Ti sei unito alla stanza"</string>
|
||||
<string name="state_event_room_knock">"%1$s ha chiesto di unirsi"</string>
|
||||
<string name="state_event_room_knock_accepted">"%1$s ha permesso a %2$s di unirsi"</string>
|
||||
<string name="state_event_room_knock_accepted_by_you">"%1$s ti ha permesso di unirti"</string>
|
||||
<string name="state_event_room_knock_by_you">"Hai richiesto di unirti"</string>
|
||||
<string name="state_event_room_knock_denied">"%1$s ha rifiutato la richiesta di unirsi di %2$s"</string>
|
||||
<string name="state_event_room_knock_denied_by_you">"Hai rifiutato la richiesta di unirsi di %1$s"</string>
|
||||
<string name="state_event_room_knock_denied_you">"%1$s ha rifiutato la tua richiesta di unirti"</string>
|
||||
<string name="state_event_room_knock_retracted">"%1$s non è più interessato a partecipare"</string>
|
||||
<string name="state_event_room_knock_retracted_by_you">"Hai annullato la tua richiesta di unirti"</string>
|
||||
<string name="state_event_room_leave">"%1$s ha lasciato la stanza"</string>
|
||||
<string name="state_event_room_leave_by_you">"Hai lasciato la stanza"</string>
|
||||
<string name="state_event_room_name_changed">"%1$s ha cambiato il nome della stanza in: %2$s"</string>
|
||||
<string name="state_event_room_name_changed_by_you">"Hai cambiato il nome della stanza in: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s ha rimosso il nome della stanza"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"Hai rimosso il nome della stanza"</string>
|
||||
<string name="state_event_room_reject">"%1$s ha rifiutato l\'invito"</string>
|
||||
<string name="state_event_room_reject_by_you">"Hai rifiutato l\'invito"</string>
|
||||
<string name="state_event_room_remove">"%1$s ha rimosso %2$s"</string>
|
||||
<string name="state_event_room_remove_by_you">"Hai rimosso %1$s"</string>
|
||||
<string name="state_event_room_third_party_invite">"%1$s ha inviato un invito a %2$s per unirsi alla stanza"</string>
|
||||
<string name="state_event_room_third_party_invite_by_you">"Hai inviato un invito a %1$s per unirsi alla stanza"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite">"%1$s ha revocato l\'invito di %2$s ad unirsi alla stanza."</string>
|
||||
<string name="state_event_room_third_party_revoked_invite_by_you">"Hai revocato l\'invito a %1$s a universi alla stanza"</string>
|
||||
<string name="state_event_room_topic_changed">"%1$s ha cambiato l\'oggetto in: %2$s"</string>
|
||||
<string name="state_event_room_topic_changed_by_you">"Hai cambiato l\'oggetto in: %1$s"</string>
|
||||
<string name="state_event_room_topic_removed">"%1$s ha rimosso l\'oggetto della stanza"</string>
|
||||
<string name="state_event_room_topic_removed_by_you">"Hai rimosso l\'oggetto della stanza"</string>
|
||||
<string name="state_event_room_unban">"%1$s ha sbloccato %2$s"</string>
|
||||
<string name="state_event_room_unban_by_you">"Hai sbloccato %1$s"</string>
|
||||
<string name="state_event_room_unknown_membership_change">"%1$s ha apportato una modifica sconosciuta alla propria iscrizione"</string>
|
||||
</resources>
|
||||
@@ -4,58 +4,4 @@
|
||||
<string name="screen_roomlist_main_space_title">"Toate conversatiile"</string>
|
||||
<string name="session_verification_banner_message">"Se pare că folosiți un dispozitiv nou. Verificați-vă identitatea pentru acces la mesajele dumneavoastră criptate."</string>
|
||||
<string name="session_verification_banner_title">"Accesați istoricul mesajelor"</string>
|
||||
<string name="state_event_avatar_changed_too">"(s-a schimbat si avatarul)"</string>
|
||||
<string name="state_event_avatar_url_changed">"%1$s și-a schimbat avatarul"</string>
|
||||
<string name="state_event_avatar_url_changed_by_you">"V-ați schimbat avatarul"</string>
|
||||
<string name="state_event_display_name_changed_from">"%1$s și-a schimbat numele din %2$s în %3$s"</string>
|
||||
<string name="state_event_display_name_changed_from_by_you">"V-ați schimbat numele din %1$s în %2$s"</string>
|
||||
<string name="state_event_display_name_removed">"%1$s și-a sters numele (era %2$s)"</string>
|
||||
<string name="state_event_display_name_removed_by_you">"V-ați sters numele (era %1$s)"</string>
|
||||
<string name="state_event_display_name_set">"%1$s și-a schimbat numele %2$s"</string>
|
||||
<string name="state_event_display_name_set_by_you">"V-ați schimbat numele în %1$s"</string>
|
||||
<string name="state_event_room_avatar_changed">"%1$s a schimbat avatarul camerei"</string>
|
||||
<string name="state_event_room_avatar_changed_by_you">"Ați schimbat avatarul camerei"</string>
|
||||
<string name="state_event_room_avatar_removed">"%1$s a șters avatarul camerei"</string>
|
||||
<string name="state_event_room_avatar_removed_by_you">"Ați șters avatarul camerei"</string>
|
||||
<string name="state_event_room_ban">"%1$s a adăugat o interdicție pentru %2$s"</string>
|
||||
<string name="state_event_room_ban_by_you">"Ați adăugat o interdicție pentru %1$s"</string>
|
||||
<string name="state_event_room_created">"%1$s a creat camera"</string>
|
||||
<string name="state_event_room_created_by_you">"Ați creat camera"</string>
|
||||
<string name="state_event_room_invite">"%1$s l-a invitat pe %2$s"</string>
|
||||
<string name="state_event_room_invite_accepted">"%1$s a acceptat invitația"</string>
|
||||
<string name="state_event_room_invite_accepted_by_you">"Ați acceptat invitația"</string>
|
||||
<string name="state_event_room_invite_by_you">"L-ați invitat pe %1$s"</string>
|
||||
<string name="state_event_room_invite_you">"%1$s v-a invitat"</string>
|
||||
<string name="state_event_room_join">"%1$s a intrat în cameră"</string>
|
||||
<string name="state_event_room_join_by_you">"Ați intrat în cameră"</string>
|
||||
<string name="state_event_room_knock">"%1$s a solicitat să se alăture camerei"</string>
|
||||
<string name="state_event_room_knock_accepted">"%1$s i-a permis lui %2$s să se alăture camerei"</string>
|
||||
<string name="state_event_room_knock_accepted_by_you">"%1$s v-a permis să vă alăturați camerei"</string>
|
||||
<string name="state_event_room_knock_by_you">"Ați solicitat să vă alăturați camerei"</string>
|
||||
<string name="state_event_room_knock_denied">"%1$s a respins solicitarea de alăturare a lui %2$s"</string>
|
||||
<string name="state_event_room_knock_denied_by_you">"Ați respins solicitarea de alăturare a lui %1$s"</string>
|
||||
<string name="state_event_room_knock_denied_you">"%1$s a respins cererea dumneavoastră de alăturare"</string>
|
||||
<string name="state_event_room_knock_retracted">"%1$s nu mai este interesat să se alăture camerei"</string>
|
||||
<string name="state_event_room_knock_retracted_by_you">"Ați anulat cererea de alăturare"</string>
|
||||
<string name="state_event_room_leave">"%1$s a părăsit camera"</string>
|
||||
<string name="state_event_room_leave_by_you">"Ați părăsit camera"</string>
|
||||
<string name="state_event_room_name_changed">"%1$s a schimbat numele camerei în: %2$s"</string>
|
||||
<string name="state_event_room_name_changed_by_you">"Ați schimbat numele camerei în: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s a sters numele camerei"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"Ați șters numele camerei"</string>
|
||||
<string name="state_event_room_reject">"%1$s a respins invitația"</string>
|
||||
<string name="state_event_room_reject_by_you">"Ați respins invitația"</string>
|
||||
<string name="state_event_room_remove">"%1$s l-a îndepărtat pe %2$s"</string>
|
||||
<string name="state_event_room_remove_by_you">"L-ați îndepărtat pe %1$s"</string>
|
||||
<string name="state_event_room_third_party_invite">"%1$s a trimis o invitație către %2$s pentru a se alătura camerei"</string>
|
||||
<string name="state_event_room_third_party_invite_by_you">"Ați trimis o invitație către %1$s pentru a se alătura camerei"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite">"%1$s a revocat invitația pentru %2$s de a se alătura camerei"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite_by_you">"Ați revocat invitația pentru %1$s de a se alătura camerei"</string>
|
||||
<string name="state_event_room_topic_changed">"%1$s a schimbat subiectul în: %2$s"</string>
|
||||
<string name="state_event_room_topic_changed_by_you">"Ați schimbat subiectul în: %1$s"</string>
|
||||
<string name="state_event_room_topic_removed">"%1$s a șters subiectul camerei"</string>
|
||||
<string name="state_event_room_topic_removed_by_you">"Ați șters subiectul camerei"</string>
|
||||
<string name="state_event_room_unban">"%1$s a anulat interdicția pentru %2$s"</string>
|
||||
<string name="state_event_room_unban_by_you">"Ați anulat interdicția pentru %1$s"</string>
|
||||
<string name="state_event_room_unknown_membership_change">"%1$s a făcut o modificare necunoscută asupra calității sale de membru"</string>
|
||||
</resources>
|
||||
@@ -4,58 +4,4 @@
|
||||
<string name="screen_roomlist_main_space_title">"All Chats"</string>
|
||||
<string name="session_verification_banner_message">"Looks like you’re using a new device. Verify it’s you to access your encrypted messages."</string>
|
||||
<string name="session_verification_banner_title">"Access your message history"</string>
|
||||
<string name="state_event_avatar_changed_too">"(avatar was changed too)"</string>
|
||||
<string name="state_event_avatar_url_changed">"%1$s changed their avatar"</string>
|
||||
<string name="state_event_avatar_url_changed_by_you">"You changed your avatar"</string>
|
||||
<string name="state_event_display_name_changed_from">"%1$s changed their display name from %2$s to %3$s"</string>
|
||||
<string name="state_event_display_name_changed_from_by_you">"You changed your display name from %1$s to %2$s"</string>
|
||||
<string name="state_event_display_name_removed">"%1$s removed their display name (it was %2$s)"</string>
|
||||
<string name="state_event_display_name_removed_by_you">"You removed your display name (it was %1$s)"</string>
|
||||
<string name="state_event_display_name_set">"%1$s set their display name to %2$s"</string>
|
||||
<string name="state_event_display_name_set_by_you">"You set your display name to %1$s"</string>
|
||||
<string name="state_event_room_avatar_changed">"%1$s changed the room avatar"</string>
|
||||
<string name="state_event_room_avatar_changed_by_you">"You changed the room avatar"</string>
|
||||
<string name="state_event_room_avatar_removed">"%1$s removed the room avatar"</string>
|
||||
<string name="state_event_room_avatar_removed_by_you">"You removed the room avatar"</string>
|
||||
<string name="state_event_room_ban">"%1$s banned %2$s"</string>
|
||||
<string name="state_event_room_ban_by_you">"You banned %1$s"</string>
|
||||
<string name="state_event_room_created">"%1$s created the room"</string>
|
||||
<string name="state_event_room_created_by_you">"You created the room"</string>
|
||||
<string name="state_event_room_invite">"%1$s invited %2$s"</string>
|
||||
<string name="state_event_room_invite_accepted">"%1$s accepted the invite"</string>
|
||||
<string name="state_event_room_invite_accepted_by_you">"You accepted the invite"</string>
|
||||
<string name="state_event_room_invite_by_you">"You invited %1$s"</string>
|
||||
<string name="state_event_room_invite_you">"%1$s invited you"</string>
|
||||
<string name="state_event_room_join">"%1$s joined the room"</string>
|
||||
<string name="state_event_room_join_by_you">"You joined the room"</string>
|
||||
<string name="state_event_room_knock">"%1$s requested to join"</string>
|
||||
<string name="state_event_room_knock_accepted">"%1$s allowed %2$s to join"</string>
|
||||
<string name="state_event_room_knock_accepted_by_you">"%1$s allowed you to join"</string>
|
||||
<string name="state_event_room_knock_by_you">"You requested to join"</string>
|
||||
<string name="state_event_room_knock_denied">"%1$s rejected %2$s\'s request to join"</string>
|
||||
<string name="state_event_room_knock_denied_by_you">"You rejected %1$s\'s request to join"</string>
|
||||
<string name="state_event_room_knock_denied_you">"%1$s rejected your request to join"</string>
|
||||
<string name="state_event_room_knock_retracted">"%1$s is no longer interested in joining"</string>
|
||||
<string name="state_event_room_knock_retracted_by_you">"You cancelled your request to join"</string>
|
||||
<string name="state_event_room_leave">"%1$s left the room"</string>
|
||||
<string name="state_event_room_leave_by_you">"You left the room"</string>
|
||||
<string name="state_event_room_name_changed">"%1$s changed the room name to: %2$s"</string>
|
||||
<string name="state_event_room_name_changed_by_you">"You changed the room name to: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s removed the room name"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"You removed the room name"</string>
|
||||
<string name="state_event_room_reject">"%1$s rejected the invitation"</string>
|
||||
<string name="state_event_room_reject_by_you">"You rejected the invitation"</string>
|
||||
<string name="state_event_room_remove">"%1$s removed %2$s"</string>
|
||||
<string name="state_event_room_remove_by_you">"You removed %1$s"</string>
|
||||
<string name="state_event_room_third_party_invite">"%1$s sent an invitation to %2$s to join the room"</string>
|
||||
<string name="state_event_room_third_party_invite_by_you">"You sent an invitation to %1$s to join the room"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite">"%1$s revoked the invitation for %2$s to join the room"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite_by_you">"You revoked the invitation for %1$s to join the room"</string>
|
||||
<string name="state_event_room_topic_changed">"%1$s changed the topic to: %2$s"</string>
|
||||
<string name="state_event_room_topic_changed_by_you">"You changed the topic to: %1$s"</string>
|
||||
<string name="state_event_room_topic_removed">"%1$s removed the room topic"</string>
|
||||
<string name="state_event_room_topic_removed_by_you">"You removed the room topic"</string>
|
||||
<string name="state_event_room_unban">"%1$s unbanned %2$s"</string>
|
||||
<string name="state_event_room_unban_by_you">"You unbanned %1$s"</string>
|
||||
<string name="state_event_room_unknown_membership_change">"%1$s made an unknown change to their membership"</string>
|
||||
</resources>
|
||||
@@ -29,6 +29,7 @@ import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormat
|
||||
import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
|
||||
import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||
|
||||
27
libraries/eventformatter/api/build.gradle.kts
Normal file
27
libraries/eventformatter/api/build.gradle.kts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2022 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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("io.element.android-library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.libraries.eventformatter.api"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.libraries.matrix.api)
|
||||
}
|
||||
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.roomlist.impl
|
||||
package io.element.android.libraries.eventformatter.api
|
||||
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
|
||||
interface RoomLastMessageFormatter {
|
||||
fun processMessageItem(event: EventTimelineItem, isDmRoom: Boolean): CharSequence?
|
||||
fun format(event: EventTimelineItem, isDmRoom: Boolean): CharSequence?
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.libraries.eventformatter.api
|
||||
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
|
||||
interface TimelineEventFormatter {
|
||||
fun format(event: EventTimelineItem): CharSequence?
|
||||
}
|
||||
52
libraries/eventformatter/impl/build.gradle.kts
Normal file
52
libraries/eventformatter/impl/build.gradle.kts
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("io.element.android-compose-library")
|
||||
alias(libs.plugins.anvil)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.libraries.eventformatter.impl"
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
isIncludeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anvil {
|
||||
generateDaggerFactories.set(true)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
anvil(projects.anvilcodegen)
|
||||
implementation(projects.anvilannotations)
|
||||
|
||||
implementation(projects.libraries.core)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
implementation(projects.services.toolbox.api)
|
||||
api(projects.libraries.eventformatter.api)
|
||||
|
||||
testImplementation(projects.services.toolbox.impl)
|
||||
testImplementation(libs.test.junit)
|
||||
testImplementation(libs.test.robolectric)
|
||||
testImplementation(libs.test.truth)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.libraries.eventformatter.impl
|
||||
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
|
||||
import io.element.android.libraries.eventformatter.impl.mode.RenderingMode
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import javax.inject.Inject
|
||||
import io.element.android.libraries.ui.strings.R as StringR
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultRoomLastMessageFormatter @Inject constructor(
|
||||
private val sp: StringProvider,
|
||||
private val matrixClient: MatrixClient,
|
||||
private val roomMembershipContentFormatter: RoomMembershipContentFormatter,
|
||||
private val profileChangeContentFormatter: ProfileChangeContentFormatter,
|
||||
private val stateContentFormatter: StateContentFormatter,
|
||||
) : RoomLastMessageFormatter {
|
||||
|
||||
override fun format(event: EventTimelineItem, isDmRoom: Boolean): CharSequence? {
|
||||
val isOutgoing = event.sender == matrixClient.sessionId
|
||||
val senderDisplayName = (event.senderProfile as? ProfileTimelineDetails.Ready)?.displayName ?: event.sender.value
|
||||
return when (val content = event.content) {
|
||||
is MessageContent -> processMessageContents(content, senderDisplayName, isDmRoom)
|
||||
RedactedContent -> {
|
||||
val message = sp.getString(StringR.string.common_message_removed)
|
||||
if (!isDmRoom) {
|
||||
prefix(message, senderDisplayName)
|
||||
} else {
|
||||
message
|
||||
}
|
||||
}
|
||||
is StickerContent -> {
|
||||
content.body
|
||||
}
|
||||
is UnableToDecryptContent -> {
|
||||
val message = sp.getString(StringR.string.common_decryption_error)
|
||||
if (!isDmRoom) {
|
||||
prefix(message, senderDisplayName)
|
||||
} else {
|
||||
message
|
||||
}
|
||||
}
|
||||
is RoomMembershipContent -> {
|
||||
roomMembershipContentFormatter.format(content, senderDisplayName, isOutgoing)
|
||||
}
|
||||
is ProfileChangeContent -> {
|
||||
profileChangeContentFormatter.format(content, senderDisplayName, isOutgoing)
|
||||
}
|
||||
is StateContent -> {
|
||||
stateContentFormatter.format(content, senderDisplayName, isOutgoing, RenderingMode.RoomList)
|
||||
}
|
||||
is FailedToParseMessageLikeContent, is FailedToParseStateContent, is UnknownContent -> {
|
||||
prefixIfNeeded(sp.getString(StringR.string.common_unsupported_event), senderDisplayName, isDmRoom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processMessageContents(messageContent: MessageContent, senderDisplayName: String, isDmRoom: Boolean): CharSequence? {
|
||||
val messageType: MessageType = messageContent.type ?: return null
|
||||
|
||||
val internalMessage = when (messageType) {
|
||||
// Doesn't need a prefix
|
||||
is EmoteMessageType -> {
|
||||
return "- $senderDisplayName ${messageType.body}"
|
||||
}
|
||||
is TextMessageType -> {
|
||||
messageType.body
|
||||
}
|
||||
is VideoMessageType -> {
|
||||
sp.getString(StringR.string.common_video)
|
||||
}
|
||||
is ImageMessageType -> {
|
||||
sp.getString(StringR.string.common_image)
|
||||
}
|
||||
is FileMessageType -> {
|
||||
sp.getString(StringR.string.common_file)
|
||||
}
|
||||
is AudioMessageType -> {
|
||||
sp.getString(StringR.string.common_audio)
|
||||
}
|
||||
UnknownMessageType -> {
|
||||
sp.getString(StringR.string.common_unsupported_event)
|
||||
}
|
||||
is NoticeMessageType -> {
|
||||
messageType.body
|
||||
}
|
||||
}
|
||||
return prefixIfNeeded(internalMessage, senderDisplayName, isDmRoom)
|
||||
}
|
||||
|
||||
private fun prefixIfNeeded(message: String, senderDisplayName: String, isDmRoom: Boolean): CharSequence = if (isDmRoom) {
|
||||
message
|
||||
} else {
|
||||
prefix(message, senderDisplayName)
|
||||
}
|
||||
|
||||
private fun prefix(message: String, senderDisplayName: String): AnnotatedString {
|
||||
return buildAnnotatedString {
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append(senderDisplayName)
|
||||
}
|
||||
append(": ")
|
||||
append(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.libraries.eventformatter.impl
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
import io.element.android.libraries.eventformatter.api.TimelineEventFormatter
|
||||
import io.element.android.libraries.eventformatter.impl.mode.RenderingMode
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
|
||||
import io.element.android.libraries.ui.strings.R
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultTimelineEventFormatter @Inject constructor(
|
||||
private val sp: StringProvider,
|
||||
private val matrixClient: MatrixClient,
|
||||
private val buildMeta: BuildMeta,
|
||||
private val roomMembershipContentFormatter: RoomMembershipContentFormatter,
|
||||
private val profileChangeContentFormatter: ProfileChangeContentFormatter,
|
||||
private val stateContentFormatter: StateContentFormatter,
|
||||
) : TimelineEventFormatter {
|
||||
|
||||
override fun format(event: EventTimelineItem): CharSequence? {
|
||||
val isOutgoing = event.sender == matrixClient.sessionId
|
||||
val senderDisplayName = (event.senderProfile as? ProfileTimelineDetails.Ready)?.displayName ?: event.sender.value
|
||||
return when (val content = event.content) {
|
||||
is RoomMembershipContent -> {
|
||||
roomMembershipContentFormatter.format(content, senderDisplayName, isOutgoing)
|
||||
}
|
||||
is ProfileChangeContent -> {
|
||||
profileChangeContentFormatter.format(content, senderDisplayName, isOutgoing)
|
||||
}
|
||||
is StateContent -> {
|
||||
stateContentFormatter.format(content, senderDisplayName, isOutgoing, RenderingMode.Timeline)
|
||||
}
|
||||
RedactedContent,
|
||||
is StickerContent,
|
||||
is UnableToDecryptContent,
|
||||
is MessageContent,
|
||||
is FailedToParseMessageLikeContent,
|
||||
is FailedToParseStateContent,
|
||||
is UnknownContent -> {
|
||||
if (buildMeta.isDebuggable) {
|
||||
error("You should not use this formatter for this event: $event")
|
||||
}
|
||||
sp.getString(R.string.common_unsupported_event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.libraries.eventformatter.impl
|
||||
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
class ProfileChangeContentFormatter @Inject constructor(
|
||||
private val sp: StringProvider,
|
||||
) {
|
||||
fun format(
|
||||
profileChangeContent: ProfileChangeContent,
|
||||
senderDisplayName: String,
|
||||
senderIsYou: Boolean,
|
||||
): String? = profileChangeContent.run {
|
||||
val displayNameChanged = displayName != prevDisplayName
|
||||
val avatarChanged = avatarUrl != prevAvatarUrl
|
||||
return when {
|
||||
avatarChanged && displayNameChanged -> {
|
||||
val message = format(profileChangeContent.copy(avatarUrl = null, prevAvatarUrl = null), senderDisplayName, senderIsYou)
|
||||
val avatarChangedToo = sp.getString(R.string.state_event_avatar_changed_too)
|
||||
"$message\n$avatarChangedToo"
|
||||
}
|
||||
displayNameChanged -> {
|
||||
if (displayName != null && prevDisplayName != null) {
|
||||
if (senderIsYou) {
|
||||
sp.getString(R.string.state_event_display_name_changed_from_by_you, prevDisplayName, displayName)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_display_name_changed_from, senderDisplayName, prevDisplayName, displayName)
|
||||
}
|
||||
} else if (displayName != null) {
|
||||
if (senderIsYou) {
|
||||
sp.getString(R.string.state_event_display_name_set_by_you, displayName)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_display_name_set, senderDisplayName, displayName)
|
||||
}
|
||||
} else {
|
||||
if (senderIsYou) {
|
||||
sp.getString(R.string.state_event_display_name_removed_by_you, prevDisplayName)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_display_name_removed, senderDisplayName, prevDisplayName)
|
||||
}
|
||||
}
|
||||
}
|
||||
avatarChanged -> {
|
||||
if (senderIsYou) {
|
||||
sp.getString(R.string.state_event_avatar_url_changed_by_you)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_avatar_url_changed, senderDisplayName)
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.libraries.eventformatter.impl
|
||||
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomMembershipContentFormatter @Inject constructor(
|
||||
private val matrixClient: MatrixClient,
|
||||
private val sp: StringProvider,
|
||||
) {
|
||||
fun format(
|
||||
membershipContent: RoomMembershipContent,
|
||||
senderDisplayName: String,
|
||||
senderIsYou: Boolean,
|
||||
): CharSequence? {
|
||||
val userId = membershipContent.userId
|
||||
val memberIsYou = userId == matrixClient.sessionId
|
||||
return when (val change = membershipContent.change) {
|
||||
MembershipChange.JOINED -> if (memberIsYou) {
|
||||
sp.getString(R.string.state_event_room_join_by_you)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_room_join, userId.value)
|
||||
}
|
||||
MembershipChange.LEFT -> if (memberIsYou) {
|
||||
sp.getString(R.string.state_event_room_leave_by_you)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_room_leave, userId.value)
|
||||
}
|
||||
MembershipChange.BANNED, MembershipChange.KICKED_AND_BANNED -> if (senderIsYou) {
|
||||
sp.getString(R.string.state_event_room_ban_by_you, userId.value)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_room_ban, senderDisplayName, userId.value)
|
||||
}
|
||||
MembershipChange.UNBANNED -> if (senderIsYou) {
|
||||
sp.getString(R.string.state_event_room_unban_by_you, userId.value)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_room_unban, senderDisplayName, userId.value)
|
||||
}
|
||||
MembershipChange.KICKED -> if (senderIsYou) {
|
||||
sp.getString(R.string.state_event_room_remove_by_you, userId.value)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_room_remove, senderDisplayName, userId.value)
|
||||
}
|
||||
MembershipChange.INVITED -> if (senderIsYou) {
|
||||
sp.getString(R.string.state_event_room_invite_by_you, userId.value)
|
||||
} else if (memberIsYou) {
|
||||
sp.getString(R.string.state_event_room_invite_you, senderDisplayName)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_room_invite, senderDisplayName, userId.value)
|
||||
}
|
||||
MembershipChange.INVITATION_ACCEPTED -> if (memberIsYou) {
|
||||
sp.getString(R.string.state_event_room_invite_accepted_by_you)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_room_invite_accepted, userId.value)
|
||||
}
|
||||
MembershipChange.INVITATION_REJECTED -> if (memberIsYou) {
|
||||
sp.getString(R.string.state_event_room_reject_by_you)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_room_reject, userId.value)
|
||||
}
|
||||
MembershipChange.INVITATION_REVOKED -> if (senderIsYou) {
|
||||
sp.getString(R.string.state_event_room_third_party_revoked_invite_by_you, userId.value)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_room_third_party_revoked_invite, senderDisplayName, userId.value)
|
||||
}
|
||||
MembershipChange.KNOCKED -> if (memberIsYou) {
|
||||
sp.getString(R.string.state_event_room_knock_by_you)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_room_knock, userId.value)
|
||||
}
|
||||
MembershipChange.KNOCK_ACCEPTED -> if (senderIsYou) {
|
||||
sp.getString(R.string.state_event_room_knock_accepted_by_you, userId.value)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_room_knock_accepted, senderDisplayName, userId.value)
|
||||
}
|
||||
MembershipChange.KNOCK_RETRACTED -> if (memberIsYou) {
|
||||
sp.getString(R.string.state_event_room_knock_retracted_by_you)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_room_knock_retracted, userId.value)
|
||||
}
|
||||
MembershipChange.KNOCK_DENIED -> if (senderIsYou) {
|
||||
sp.getString(R.string.state_event_room_knock_denied_by_you, userId.value)
|
||||
} else if (memberIsYou) {
|
||||
sp.getString(R.string.state_event_room_knock_denied_you, senderDisplayName)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_room_knock_denied, senderDisplayName, userId.value)
|
||||
}
|
||||
else -> {
|
||||
Timber.v("Filtering timeline item for room membership: $membershipContent")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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.libraries.eventformatter.impl
|
||||
|
||||
import io.element.android.libraries.eventformatter.impl.mode.RenderingMode
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.OtherState
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class StateContentFormatter @Inject constructor(
|
||||
private val sp: StringProvider,
|
||||
) {
|
||||
fun format(
|
||||
stateContent: StateContent,
|
||||
senderDisplayName: String,
|
||||
senderIsYou: Boolean,
|
||||
renderingMode: RenderingMode,
|
||||
): CharSequence? {
|
||||
return when (val content = stateContent.content) {
|
||||
is OtherState.RoomAvatar -> {
|
||||
val hasAvatarUrl = content.url != null
|
||||
when {
|
||||
senderIsYou && hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_changed_by_you)
|
||||
senderIsYou && !hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_removed_by_you)
|
||||
!senderIsYou && hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_changed, senderDisplayName)
|
||||
else -> sp.getString(R.string.state_event_room_avatar_removed, senderDisplayName)
|
||||
}
|
||||
}
|
||||
is OtherState.RoomCreate -> {
|
||||
if (senderIsYou) {
|
||||
sp.getString(R.string.state_event_room_created_by_you)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_room_created, senderDisplayName)
|
||||
}
|
||||
}
|
||||
is OtherState.RoomEncryption -> sp.getString(io.element.android.libraries.ui.strings.R.string.common_encryption_enabled)
|
||||
is OtherState.RoomName -> {
|
||||
val hasRoomName = content.name != null
|
||||
when {
|
||||
senderIsYou && hasRoomName -> sp.getString(R.string.state_event_room_name_changed_by_you, content.name)
|
||||
senderIsYou && !hasRoomName -> sp.getString(R.string.state_event_room_name_removed_by_you)
|
||||
!senderIsYou && hasRoomName -> sp.getString(R.string.state_event_room_name_changed, senderDisplayName, content.name)
|
||||
else -> sp.getString(R.string.state_event_room_name_removed, senderDisplayName)
|
||||
}
|
||||
}
|
||||
is OtherState.RoomThirdPartyInvite -> {
|
||||
if (content.displayName == null) {
|
||||
Timber.e("RoomThirdPartyInvite undisplayable due to missing name")
|
||||
return null
|
||||
}
|
||||
if (senderIsYou) {
|
||||
sp.getString(R.string.state_event_room_third_party_invite_by_you, content.displayName)
|
||||
} else {
|
||||
sp.getString(R.string.state_event_room_third_party_invite, senderDisplayName, content.displayName)
|
||||
}
|
||||
}
|
||||
is OtherState.RoomTopic -> {
|
||||
val hasRoomTopic = content.topic != null
|
||||
when {
|
||||
senderIsYou && hasRoomTopic -> sp.getString(R.string.state_event_room_topic_changed_by_you, content.topic)
|
||||
senderIsYou && !hasRoomTopic -> sp.getString(R.string.state_event_room_topic_removed_by_you)
|
||||
!senderIsYou && hasRoomTopic -> sp.getString(R.string.state_event_room_topic_changed, senderDisplayName, content.topic)
|
||||
else -> sp.getString(R.string.state_event_room_topic_removed, senderDisplayName)
|
||||
}
|
||||
}
|
||||
is OtherState.Custom -> when (renderingMode) {
|
||||
RenderingMode.RoomList -> {
|
||||
Timber.v("Filtering timeline item for room state change: $content")
|
||||
null
|
||||
}
|
||||
RenderingMode.Timeline -> {
|
||||
"Custom event ${content.eventType}"
|
||||
}
|
||||
}
|
||||
OtherState.PolicyRuleRoom -> when (renderingMode) {
|
||||
RenderingMode.RoomList -> {
|
||||
Timber.v("Filtering timeline item for room state change: $content")
|
||||
null
|
||||
}
|
||||
RenderingMode.Timeline -> {
|
||||
"PolicyRuleRoom"
|
||||
}
|
||||
}
|
||||
OtherState.PolicyRuleServer -> when (renderingMode) {
|
||||
RenderingMode.RoomList -> {
|
||||
Timber.v("Filtering timeline item for room state change: $content")
|
||||
null
|
||||
}
|
||||
RenderingMode.Timeline -> {
|
||||
"PolicyRuleServer"
|
||||
}
|
||||
}
|
||||
OtherState.PolicyRuleUser -> when (renderingMode) {
|
||||
RenderingMode.RoomList -> {
|
||||
Timber.v("Filtering timeline item for room state change: $content")
|
||||
null
|
||||
}
|
||||
RenderingMode.Timeline -> {
|
||||
"PolicyRuleUser"
|
||||
}
|
||||
}
|
||||
OtherState.RoomAliases -> when (renderingMode) {
|
||||
RenderingMode.RoomList -> {
|
||||
Timber.v("Filtering timeline item for room state change: $content")
|
||||
null
|
||||
}
|
||||
RenderingMode.Timeline -> {
|
||||
"RoomAliases"
|
||||
}
|
||||
}
|
||||
OtherState.RoomCanonicalAlias -> when (renderingMode) {
|
||||
RenderingMode.RoomList -> {
|
||||
Timber.v("Filtering timeline item for room state change: $content")
|
||||
null
|
||||
}
|
||||
RenderingMode.Timeline -> {
|
||||
"RoomCanonicalAlias"
|
||||
}
|
||||
}
|
||||
OtherState.RoomGuestAccess -> when (renderingMode) {
|
||||
RenderingMode.RoomList -> {
|
||||
Timber.v("Filtering timeline item for room state change: $content")
|
||||
null
|
||||
}
|
||||
RenderingMode.Timeline -> {
|
||||
"RoomGuestAccess"
|
||||
}
|
||||
}
|
||||
OtherState.RoomHistoryVisibility -> when (renderingMode) {
|
||||
RenderingMode.RoomList -> {
|
||||
Timber.v("Filtering timeline item for room state change: $content")
|
||||
null
|
||||
}
|
||||
RenderingMode.Timeline -> {
|
||||
"RoomHistoryVisibility"
|
||||
}
|
||||
}
|
||||
OtherState.RoomJoinRules -> when (renderingMode) {
|
||||
RenderingMode.RoomList -> {
|
||||
Timber.v("Filtering timeline item for room state change: $content")
|
||||
null
|
||||
}
|
||||
RenderingMode.Timeline -> {
|
||||
"RoomJoinRules"
|
||||
}
|
||||
}
|
||||
OtherState.RoomPinnedEvents -> when (renderingMode) {
|
||||
RenderingMode.RoomList -> {
|
||||
Timber.v("Filtering timeline item for room state change: $content")
|
||||
null
|
||||
}
|
||||
RenderingMode.Timeline -> {
|
||||
"RoomPinnedEvents"
|
||||
}
|
||||
}
|
||||
OtherState.RoomPowerLevels -> when (renderingMode) {
|
||||
RenderingMode.RoomList -> {
|
||||
Timber.v("Filtering timeline item for room state change: $content")
|
||||
null
|
||||
}
|
||||
RenderingMode.Timeline -> {
|
||||
"RoomPowerLevels"
|
||||
}
|
||||
}
|
||||
OtherState.RoomServerAcl -> when (renderingMode) {
|
||||
RenderingMode.RoomList -> {
|
||||
Timber.v("Filtering timeline item for room state change: $content")
|
||||
null
|
||||
}
|
||||
RenderingMode.Timeline -> {
|
||||
"RoomServerAcl"
|
||||
}
|
||||
}
|
||||
OtherState.RoomTombstone -> when (renderingMode) {
|
||||
RenderingMode.RoomList -> {
|
||||
Timber.v("Filtering timeline item for room state change: $content")
|
||||
null
|
||||
}
|
||||
RenderingMode.Timeline -> {
|
||||
"RoomTombstone"
|
||||
}
|
||||
}
|
||||
OtherState.SpaceChild -> when (renderingMode) {
|
||||
RenderingMode.RoomList -> {
|
||||
Timber.v("Filtering timeline item for room state change: $content")
|
||||
null
|
||||
}
|
||||
RenderingMode.Timeline -> {
|
||||
"SpaceChild"
|
||||
}
|
||||
}
|
||||
OtherState.SpaceParent -> when (renderingMode) {
|
||||
RenderingMode.RoomList -> {
|
||||
Timber.v("Filtering timeline item for room state change: $content")
|
||||
null
|
||||
}
|
||||
RenderingMode.Timeline -> {
|
||||
"SpaceParent"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.libraries.eventformatter.impl.mode
|
||||
|
||||
enum class RenderingMode {
|
||||
RoomList,
|
||||
Timeline,
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="state_event_avatar_changed_too">"(Avatar wurde ebenfalls geändert)"</string>
|
||||
<string name="state_event_avatar_url_changed">"%1$s hat seinen Avatar geändert"</string>
|
||||
<string name="state_event_avatar_url_changed_by_you">"Du hast deinen Avatar geändert"</string>
|
||||
<string name="state_event_display_name_changed_from">"%1$s hat den Anzeigenamen von %2$s in %3$s geändert"</string>
|
||||
<string name="state_event_display_name_changed_from_by_you">"Du hast deinen Anzeigenamen von %1$s in %2$s geändert"</string>
|
||||
<string name="state_event_display_name_removed">"%1$s hat den Anzeigenamen entfernt (war %2$s)"</string>
|
||||
<string name="state_event_display_name_removed_by_you">"Du hast deinen Anzeigenamen entfernt (war %1$s)"</string>
|
||||
<string name="state_event_display_name_set">"%1$s hat den Anzeigenamen auf %2$s gesetzt"</string>
|
||||
<string name="state_event_display_name_set_by_you">"Du hast deinen Anzeigenamen auf %1$s gesetzt"</string>
|
||||
<string name="state_event_room_avatar_changed">"%1$s hat den Raum-Avatar geändert"</string>
|
||||
<string name="state_event_room_avatar_changed_by_you">"Du hast den Raum-Avatar geändert"</string>
|
||||
<string name="state_event_room_avatar_removed">"%1$s hat den Raum-Avatar entfernt"</string>
|
||||
<string name="state_event_room_created">"%1$s hat den Raum erstellt"</string>
|
||||
<string name="state_event_room_created_by_you">"Du hast den Raum erstellt"</string>
|
||||
<string name="state_event_room_invite">"%1$s hat %2$s eingeladen"</string>
|
||||
<string name="state_event_room_invite_accepted">"%1$s hat die Einladung angenommen"</string>
|
||||
<string name="state_event_room_invite_accepted_by_you">"Du hast die Einladung angenommen"</string>
|
||||
<string name="state_event_room_invite_by_you">"Du hast %1$s eingeladen"</string>
|
||||
<string name="state_event_room_invite_you">"%1$s hat dich eingeladen"</string>
|
||||
<string name="state_event_room_join">"%1$s ist dem Raum beigetreten"</string>
|
||||
<string name="state_event_room_join_by_you">"Du bist dem Raum beigetreten"</string>
|
||||
<string name="state_event_room_knock_denied_you">"%1$s hat deine Beitrittsanfrage abgelehnt"</string>
|
||||
<string name="state_event_room_leave">"%1$s hat den Raum verlassen"</string>
|
||||
<string name="state_event_room_leave_by_you">"Du hast den Raum verlassen"</string>
|
||||
<string name="state_event_room_name_changed">"%1$s hat den Raumnamen geändert in: %2$s"</string>
|
||||
<string name="state_event_room_name_changed_by_you">"Sie haben den Raumnamen geändert in: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s hat den Raumnamen entfernt"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"Du hast den Raumnamen entfernt"</string>
|
||||
<string name="state_event_room_reject">"%1$s hat die Einladung abgelehnt"</string>
|
||||
<string name="state_event_room_reject_by_you">"Du hast die Einladung abgelehnt"</string>
|
||||
<string name="state_event_room_remove">"%1$s hat %2$s entfernt"</string>
|
||||
<string name="state_event_room_remove_by_you">"Du hast %1$s entfernt"</string>
|
||||
<string name="state_event_room_topic_changed">"%1$s hat das Thema geändert zu: %2$s"</string>
|
||||
<string name="state_event_room_topic_changed_by_you">"Sie haben das Thema geändert zu: %1$s"</string>
|
||||
<string name="state_event_room_topic_removed">"%1$s hat das Raumthema entfernt"</string>
|
||||
<string name="state_event_room_topic_removed_by_you">"Du hast das Raumthema entfernt"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="state_event_avatar_changed_too">"(el avatar también cambió)"</string>
|
||||
<string name="state_event_avatar_url_changed">"%1$s cambió su avatar"</string>
|
||||
<string name="state_event_avatar_url_changed_by_you">"Cambiaste tu avatar"</string>
|
||||
<string name="state_event_display_name_changed_from">"%1$s cambió su nombre de %2$s a %3$s"</string>
|
||||
<string name="state_event_display_name_changed_from_by_you">"Cambiaste tu nombre de %1$s a %2$s"</string>
|
||||
<string name="state_event_display_name_removed">"%1$s eliminó su nombre (era %2$s)"</string>
|
||||
<string name="state_event_display_name_removed_by_you">"Eliminaste tu nombre (era %1$s)"</string>
|
||||
<string name="state_event_display_name_set">"%1$s cambió su nombre a %2$s"</string>
|
||||
<string name="state_event_display_name_set_by_you">"Cambiaste tu nombre a %1$s"</string>
|
||||
<string name="state_event_room_avatar_changed">"%1$s cambió el avatar de la sala"</string>
|
||||
<string name="state_event_room_avatar_changed_by_you">"Cambiaste el avatar de la sala"</string>
|
||||
<string name="state_event_room_avatar_removed">"%1$s eliminó el avatar de la sala"</string>
|
||||
<string name="state_event_room_avatar_removed_by_you">"Eliminaste el avatar de la sala"</string>
|
||||
<string name="state_event_room_ban">"%1$s expulsó permanentemente a %2$s"</string>
|
||||
<string name="state_event_room_ban_by_you">"Expulsaste permanentemente a %1$s"</string>
|
||||
<string name="state_event_room_created">"%1$s creó la sala"</string>
|
||||
<string name="state_event_room_created_by_you">"Tú creaste la sala"</string>
|
||||
<string name="state_event_room_invite">"%1$s invitó a %2$s"</string>
|
||||
<string name="state_event_room_invite_accepted">"%1$s aceptó la invitación"</string>
|
||||
<string name="state_event_room_invite_accepted_by_you">"Aceptaste la invitación"</string>
|
||||
<string name="state_event_room_invite_by_you">"Invitaste a %1$s"</string>
|
||||
<string name="state_event_room_invite_you">"%1$s te invitó."</string>
|
||||
<string name="state_event_room_join">"%1$s se unió a la sala"</string>
|
||||
<string name="state_event_room_join_by_you">"Te uniste a la sala"</string>
|
||||
<string name="state_event_room_knock">"%1$s solicitó unirse"</string>
|
||||
<string name="state_event_room_knock_accepted">"%1$s permitió que %2$s se uniera"</string>
|
||||
<string name="state_event_room_knock_accepted_by_you">"%1$s te permitió unirte"</string>
|
||||
<string name="state_event_room_knock_by_you">"Solicitaste unirte"</string>
|
||||
<string name="state_event_room_knock_denied">"%1$s rechazó la solicitud de %2$s para unirse"</string>
|
||||
<string name="state_event_room_knock_denied_by_you">"Rechazaste la solicitud de %1$s para unirte"</string>
|
||||
<string name="state_event_room_knock_denied_you">"%1$s rechazó su solicitud para unirte"</string>
|
||||
<string name="state_event_room_knock_retracted">"%1$s ya no está interesado en unirse"</string>
|
||||
<string name="state_event_room_knock_retracted_by_you">"Cancelaste tu solicitud de unirte"</string>
|
||||
<string name="state_event_room_leave">"%1$s salió de la sala"</string>
|
||||
<string name="state_event_room_leave_by_you">"Saliste de la sala"</string>
|
||||
<string name="state_event_room_name_changed">"%1$s cambió el nombre de la sala a: %2$s"</string>
|
||||
<string name="state_event_room_name_changed_by_you">"Cambiaste el nombre de la sala a: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s eliminó el nombre de la sala"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"Eliminaste el nombre de la sala"</string>
|
||||
<string name="state_event_room_reject">"%1$s rechazó la invitación"</string>
|
||||
<string name="state_event_room_reject_by_you">"Rechazaste la invitación"</string>
|
||||
<string name="state_event_room_remove">"%1$s echó a %2$s"</string>
|
||||
<string name="state_event_room_remove_by_you">"Echaste a %1$s"</string>
|
||||
<string name="state_event_room_third_party_invite">"%1$s envió una invitación a %2$s para unirse a la sala"</string>
|
||||
<string name="state_event_room_third_party_invite_by_you">"Enviaste una invitación a %1$s para unirse a la sala"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite">"%1$s revocó la invitación a %2$s para unirse a la sala"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite_by_you">"Revocaste la invitación de %1$s para unirse a la sala"</string>
|
||||
<string name="state_event_room_topic_changed">"%1$s cambió el tema a: %2$s"</string>
|
||||
<string name="state_event_room_topic_changed_by_you">"Cambiaste el tema a: %1$s"</string>
|
||||
<string name="state_event_room_topic_removed">"%1$s eliminó el tema de la sala"</string>
|
||||
<string name="state_event_room_topic_removed_by_you">"Eliminaste el tema de la sala"</string>
|
||||
<string name="state_event_room_unban">"%1$s readmitió a %2$s"</string>
|
||||
<string name="state_event_room_unban_by_you">"Readmitiste a %1$s"</string>
|
||||
<string name="state_event_room_unknown_membership_change">"%1$s realizó un cambio desconocido en su membresía"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="state_event_avatar_changed_too">"(anche l\'avatar è stato cambiato)"</string>
|
||||
<string name="state_event_avatar_url_changed">"%1$s ha cambiato il proprio avatar"</string>
|
||||
<string name="state_event_avatar_url_changed_by_you">"Hai cambiato il tuo avatar"</string>
|
||||
<string name="state_event_display_name_changed_from">"%1$s ha cambiato il proprio nome visualizzato da %2$s a %3$s"</string>
|
||||
<string name="state_event_display_name_changed_from_by_you">"Hai cambiato il tuo nome visualizzato da %1$s a %2$s"</string>
|
||||
<string name="state_event_display_name_removed">"%1$s ha rimosso il proprio nome visualizzato (era %2$s)"</string>
|
||||
<string name="state_event_display_name_removed_by_you">"Hai rimosso il tuo nome visualizzato (era %1$s)"</string>
|
||||
<string name="state_event_display_name_set">"%1$s ha impostato il proprio nome visualizzato su %2$s"</string>
|
||||
<string name="state_event_display_name_set_by_you">"Hai impostato il tuo nome visualizzato su %1$s"</string>
|
||||
<string name="state_event_room_avatar_changed">"%1$s ha cambiato l\'avatar della stanza"</string>
|
||||
<string name="state_event_room_avatar_changed_by_you">"Hai cambiato l\'avatar della stanza"</string>
|
||||
<string name="state_event_room_avatar_removed">"%1$s ha rimosso l\'avatar della stanza"</string>
|
||||
<string name="state_event_room_avatar_removed_by_you">"Hai rimosso l\'avatar della stanza"</string>
|
||||
<string name="state_event_room_ban">"%1$s ha rimosso %2$s"</string>
|
||||
<string name="state_event_room_ban_by_you">"Hai rimosso %1$s"</string>
|
||||
<string name="state_event_room_created">"%1$s ha creato la stanza"</string>
|
||||
<string name="state_event_room_created_by_you">"Hai creato la stanza"</string>
|
||||
<string name="state_event_room_invite">"%1$s ha invitato %2$s"</string>
|
||||
<string name="state_event_room_invite_accepted">"%1$s ha accettato l\'invito"</string>
|
||||
<string name="state_event_room_invite_accepted_by_you">"Hai accettato l\'invito"</string>
|
||||
<string name="state_event_room_invite_by_you">"Hai invitato %1$s"</string>
|
||||
<string name="state_event_room_invite_you">"%1$s ti ha invitato"</string>
|
||||
<string name="state_event_room_join">"%1$s si è unito alla stanza"</string>
|
||||
<string name="state_event_room_join_by_you">"Ti sei unito alla stanza"</string>
|
||||
<string name="state_event_room_knock">"%1$s ha chiesto di unirsi"</string>
|
||||
<string name="state_event_room_knock_accepted">"%1$s ha permesso a %2$s di unirsi"</string>
|
||||
<string name="state_event_room_knock_accepted_by_you">"%1$s ti ha permesso di unirti"</string>
|
||||
<string name="state_event_room_knock_by_you">"Hai richiesto di unirti"</string>
|
||||
<string name="state_event_room_knock_denied">"%1$s ha rifiutato la richiesta di unirsi di %2$s"</string>
|
||||
<string name="state_event_room_knock_denied_by_you">"Hai rifiutato la richiesta di unirsi di %1$s"</string>
|
||||
<string name="state_event_room_knock_denied_you">"%1$s ha rifiutato la tua richiesta di unirti"</string>
|
||||
<string name="state_event_room_knock_retracted">"%1$s non è più interessato a partecipare"</string>
|
||||
<string name="state_event_room_knock_retracted_by_you">"Hai annullato la tua richiesta di unirti"</string>
|
||||
<string name="state_event_room_leave">"%1$s ha lasciato la stanza"</string>
|
||||
<string name="state_event_room_leave_by_you">"Hai lasciato la stanza"</string>
|
||||
<string name="state_event_room_name_changed">"%1$s ha cambiato il nome della stanza in: %2$s"</string>
|
||||
<string name="state_event_room_name_changed_by_you">"Hai cambiato il nome della stanza in: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s ha rimosso il nome della stanza"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"Hai rimosso il nome della stanza"</string>
|
||||
<string name="state_event_room_reject">"%1$s ha rifiutato l\'invito"</string>
|
||||
<string name="state_event_room_reject_by_you">"Hai rifiutato l\'invito"</string>
|
||||
<string name="state_event_room_remove">"%1$s ha rimosso %2$s"</string>
|
||||
<string name="state_event_room_remove_by_you">"Hai rimosso %1$s"</string>
|
||||
<string name="state_event_room_third_party_invite">"%1$s ha inviato un invito a %2$s per unirsi alla stanza"</string>
|
||||
<string name="state_event_room_third_party_invite_by_you">"Hai inviato un invito a %1$s per unirsi alla stanza"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite">"%1$s ha revocato l\'invito di %2$s ad unirsi alla stanza."</string>
|
||||
<string name="state_event_room_third_party_revoked_invite_by_you">"Hai revocato l\'invito a %1$s a universi alla stanza"</string>
|
||||
<string name="state_event_room_topic_changed">"%1$s ha cambiato l\'oggetto in: %2$s"</string>
|
||||
<string name="state_event_room_topic_changed_by_you">"Hai cambiato l\'oggetto in: %1$s"</string>
|
||||
<string name="state_event_room_topic_removed">"%1$s ha rimosso l\'oggetto della stanza"</string>
|
||||
<string name="state_event_room_topic_removed_by_you">"Hai rimosso l\'oggetto della stanza"</string>
|
||||
<string name="state_event_room_unban">"%1$s ha sbloccato %2$s"</string>
|
||||
<string name="state_event_room_unban_by_you">"Hai sbloccato %1$s"</string>
|
||||
<string name="state_event_room_unknown_membership_change">"%1$s ha apportato una modifica sconosciuta alla propria iscrizione"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="state_event_avatar_changed_too">"(s-a schimbat si avatarul)"</string>
|
||||
<string name="state_event_avatar_url_changed">"%1$s și-a schimbat avatarul"</string>
|
||||
<string name="state_event_avatar_url_changed_by_you">"V-ați schimbat avatarul"</string>
|
||||
<string name="state_event_display_name_changed_from">"%1$s și-a schimbat numele din %2$s în %3$s"</string>
|
||||
<string name="state_event_display_name_changed_from_by_you">"V-ați schimbat numele din %1$s în %2$s"</string>
|
||||
<string name="state_event_display_name_removed">"%1$s și-a sters numele (era %2$s)"</string>
|
||||
<string name="state_event_display_name_removed_by_you">"V-ați sters numele (era %1$s)"</string>
|
||||
<string name="state_event_display_name_set">"%1$s și-a schimbat numele %2$s"</string>
|
||||
<string name="state_event_display_name_set_by_you">"V-ați schimbat numele în %1$s"</string>
|
||||
<string name="state_event_room_avatar_changed">"%1$s a schimbat avatarul camerei"</string>
|
||||
<string name="state_event_room_avatar_changed_by_you">"Ați schimbat avatarul camerei"</string>
|
||||
<string name="state_event_room_avatar_removed">"%1$s a șters avatarul camerei"</string>
|
||||
<string name="state_event_room_avatar_removed_by_you">"Ați șters avatarul camerei"</string>
|
||||
<string name="state_event_room_ban">"%1$s a adăugat o interdicție pentru %2$s"</string>
|
||||
<string name="state_event_room_ban_by_you">"Ați adăugat o interdicție pentru %1$s"</string>
|
||||
<string name="state_event_room_created">"%1$s a creat camera"</string>
|
||||
<string name="state_event_room_created_by_you">"Ați creat camera"</string>
|
||||
<string name="state_event_room_invite">"%1$s l-a invitat pe %2$s"</string>
|
||||
<string name="state_event_room_invite_accepted">"%1$s a acceptat invitația"</string>
|
||||
<string name="state_event_room_invite_accepted_by_you">"Ați acceptat invitația"</string>
|
||||
<string name="state_event_room_invite_by_you">"L-ați invitat pe %1$s"</string>
|
||||
<string name="state_event_room_invite_you">"%1$s v-a invitat"</string>
|
||||
<string name="state_event_room_join">"%1$s a intrat în cameră"</string>
|
||||
<string name="state_event_room_join_by_you">"Ați intrat în cameră"</string>
|
||||
<string name="state_event_room_knock">"%1$s a solicitat să se alăture camerei"</string>
|
||||
<string name="state_event_room_knock_accepted">"%1$s i-a permis lui %2$s să se alăture camerei"</string>
|
||||
<string name="state_event_room_knock_accepted_by_you">"%1$s v-a permis să vă alăturați camerei"</string>
|
||||
<string name="state_event_room_knock_by_you">"Ați solicitat să vă alăturați camerei"</string>
|
||||
<string name="state_event_room_knock_denied">"%1$s a respins solicitarea de alăturare a lui %2$s"</string>
|
||||
<string name="state_event_room_knock_denied_by_you">"Ați respins solicitarea de alăturare a lui %1$s"</string>
|
||||
<string name="state_event_room_knock_denied_you">"%1$s a respins cererea dumneavoastră de alăturare"</string>
|
||||
<string name="state_event_room_knock_retracted">"%1$s nu mai este interesat să se alăture camerei"</string>
|
||||
<string name="state_event_room_knock_retracted_by_you">"Ați anulat cererea de alăturare"</string>
|
||||
<string name="state_event_room_leave">"%1$s a părăsit camera"</string>
|
||||
<string name="state_event_room_leave_by_you">"Ați părăsit camera"</string>
|
||||
<string name="state_event_room_name_changed">"%1$s a schimbat numele camerei în: %2$s"</string>
|
||||
<string name="state_event_room_name_changed_by_you">"Ați schimbat numele camerei în: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s a sters numele camerei"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"Ați șters numele camerei"</string>
|
||||
<string name="state_event_room_reject">"%1$s a respins invitația"</string>
|
||||
<string name="state_event_room_reject_by_you">"Ați respins invitația"</string>
|
||||
<string name="state_event_room_remove">"%1$s l-a îndepărtat pe %2$s"</string>
|
||||
<string name="state_event_room_remove_by_you">"L-ați îndepărtat pe %1$s"</string>
|
||||
<string name="state_event_room_third_party_invite">"%1$s a trimis o invitație către %2$s pentru a se alătura camerei"</string>
|
||||
<string name="state_event_room_third_party_invite_by_you">"Ați trimis o invitație către %1$s pentru a se alătura camerei"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite">"%1$s a revocat invitația pentru %2$s de a se alătura camerei"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite_by_you">"Ați revocat invitația pentru %1$s de a se alătura camerei"</string>
|
||||
<string name="state_event_room_topic_changed">"%1$s a schimbat subiectul în: %2$s"</string>
|
||||
<string name="state_event_room_topic_changed_by_you">"Ați schimbat subiectul în: %1$s"</string>
|
||||
<string name="state_event_room_topic_removed">"%1$s a șters subiectul camerei"</string>
|
||||
<string name="state_event_room_topic_removed_by_you">"Ați șters subiectul camerei"</string>
|
||||
<string name="state_event_room_unban">"%1$s a anulat interdicția pentru %2$s"</string>
|
||||
<string name="state_event_room_unban_by_you">"Ați anulat interdicția pentru %1$s"</string>
|
||||
<string name="state_event_room_unknown_membership_change">"%1$s a făcut o modificare necunoscută asupra calității sale de membru"</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="state_event_avatar_changed_too">"(avatar was changed too)"</string>
|
||||
<string name="state_event_avatar_url_changed">"%1$s changed their avatar"</string>
|
||||
<string name="state_event_avatar_url_changed_by_you">"You changed your avatar"</string>
|
||||
<string name="state_event_display_name_changed_from">"%1$s changed their display name from %2$s to %3$s"</string>
|
||||
<string name="state_event_display_name_changed_from_by_you">"You changed your display name from %1$s to %2$s"</string>
|
||||
<string name="state_event_display_name_removed">"%1$s removed their display name (it was %2$s)"</string>
|
||||
<string name="state_event_display_name_removed_by_you">"You removed your display name (it was %1$s)"</string>
|
||||
<string name="state_event_display_name_set">"%1$s set their display name to %2$s"</string>
|
||||
<string name="state_event_display_name_set_by_you">"You set your display name to %1$s"</string>
|
||||
<string name="state_event_room_avatar_changed">"%1$s changed the room avatar"</string>
|
||||
<string name="state_event_room_avatar_changed_by_you">"You changed the room avatar"</string>
|
||||
<string name="state_event_room_avatar_removed">"%1$s removed the room avatar"</string>
|
||||
<string name="state_event_room_avatar_removed_by_you">"You removed the room avatar"</string>
|
||||
<string name="state_event_room_ban">"%1$s banned %2$s"</string>
|
||||
<string name="state_event_room_ban_by_you">"You banned %1$s"</string>
|
||||
<string name="state_event_room_created">"%1$s created the room"</string>
|
||||
<string name="state_event_room_created_by_you">"You created the room"</string>
|
||||
<string name="state_event_room_invite">"%1$s invited %2$s"</string>
|
||||
<string name="state_event_room_invite_accepted">"%1$s accepted the invite"</string>
|
||||
<string name="state_event_room_invite_accepted_by_you">"You accepted the invite"</string>
|
||||
<string name="state_event_room_invite_by_you">"You invited %1$s"</string>
|
||||
<string name="state_event_room_invite_you">"%1$s invited you"</string>
|
||||
<string name="state_event_room_join">"%1$s joined the room"</string>
|
||||
<string name="state_event_room_join_by_you">"You joined the room"</string>
|
||||
<string name="state_event_room_knock">"%1$s requested to join"</string>
|
||||
<string name="state_event_room_knock_accepted">"%1$s allowed %2$s to join"</string>
|
||||
<string name="state_event_room_knock_accepted_by_you">"%1$s allowed you to join"</string>
|
||||
<string name="state_event_room_knock_by_you">"You requested to join"</string>
|
||||
<string name="state_event_room_knock_denied">"%1$s rejected %2$s\'s request to join"</string>
|
||||
<string name="state_event_room_knock_denied_by_you">"You rejected %1$s\'s request to join"</string>
|
||||
<string name="state_event_room_knock_denied_you">"%1$s rejected your request to join"</string>
|
||||
<string name="state_event_room_knock_retracted">"%1$s is no longer interested in joining"</string>
|
||||
<string name="state_event_room_knock_retracted_by_you">"You cancelled your request to join"</string>
|
||||
<string name="state_event_room_leave">"%1$s left the room"</string>
|
||||
<string name="state_event_room_leave_by_you">"You left the room"</string>
|
||||
<string name="state_event_room_name_changed">"%1$s changed the room name to: %2$s"</string>
|
||||
<string name="state_event_room_name_changed_by_you">"You changed the room name to: %1$s"</string>
|
||||
<string name="state_event_room_name_removed">"%1$s removed the room name"</string>
|
||||
<string name="state_event_room_name_removed_by_you">"You removed the room name"</string>
|
||||
<string name="state_event_room_reject">"%1$s rejected the invitation"</string>
|
||||
<string name="state_event_room_reject_by_you">"You rejected the invitation"</string>
|
||||
<string name="state_event_room_remove">"%1$s removed %2$s"</string>
|
||||
<string name="state_event_room_remove_by_you">"You removed %1$s"</string>
|
||||
<string name="state_event_room_third_party_invite">"%1$s sent an invitation to %2$s to join the room"</string>
|
||||
<string name="state_event_room_third_party_invite_by_you">"You sent an invitation to %1$s to join the room"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite">"%1$s revoked the invitation for %2$s to join the room"</string>
|
||||
<string name="state_event_room_third_party_revoked_invite_by_you">"You revoked the invitation for %1$s to join the room"</string>
|
||||
<string name="state_event_room_topic_changed">"%1$s changed the topic to: %2$s"</string>
|
||||
<string name="state_event_room_topic_changed_by_you">"You changed the topic to: %1$s"</string>
|
||||
<string name="state_event_room_topic_removed">"%1$s removed the room topic"</string>
|
||||
<string name="state_event_room_topic_removed_by_you">"You removed the room topic"</string>
|
||||
<string name="state_event_room_unban">"%1$s unbanned %2$s"</string>
|
||||
<string name="state_event_room_unban_by_you">"You unbanned %1$s"</string>
|
||||
<string name="state_event_room_unknown_membership_change">"%1$s made an unknown change to their membership"</string>
|
||||
</resources>
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.roomlist.impl
|
||||
package io.element.android.libraries.eventformatter.impl
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
@@ -48,6 +48,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.room.aProfileChangeMessageContent
|
||||
import io.element.android.libraries.matrix.test.room.anEventTimelineItem
|
||||
import io.element.android.services.toolbox.impl.strings.AndroidStringProvider
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
@@ -66,7 +67,14 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
fun setup() {
|
||||
context = RuntimeEnvironment.getApplication() as Context
|
||||
fakeMatrixClient = FakeMatrixClient()
|
||||
formatter = DefaultRoomLastMessageFormatter(context, fakeMatrixClient)
|
||||
val stringProvider = AndroidStringProvider(context.resources)
|
||||
formatter = DefaultRoomLastMessageFormatter(
|
||||
sp = AndroidStringProvider(context.resources),
|
||||
matrixClient = fakeMatrixClient,
|
||||
roomMembershipContentFormatter = RoomMembershipContentFormatter(fakeMatrixClient, stringProvider),
|
||||
profileChangeContentFormatter = ProfileChangeContentFormatter(stringProvider),
|
||||
stateContentFormatter = StateContentFormatter(stringProvider)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -76,7 +84,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val senderName = "Someone"
|
||||
sequenceOf(false, true).forEach { isDm ->
|
||||
val message = createRoomEvent(false, senderName, RedactedContent)
|
||||
val result = formatter.processMessageItem(message, isDm)
|
||||
val result = formatter.format(message, isDm)
|
||||
if (isDm) {
|
||||
Truth.assertThat(result).isEqualTo(expected)
|
||||
} else {
|
||||
@@ -92,7 +100,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val body = "body"
|
||||
val info = ImageInfo(null, null, null, null, null, null, null)
|
||||
val message = createRoomEvent(false, null, StickerContent(body, info, "url"))
|
||||
val result = formatter.processMessageItem(message, false)
|
||||
val result = formatter.format(message, false)
|
||||
Truth.assertThat(result).isEqualTo(body)
|
||||
}
|
||||
|
||||
@@ -103,7 +111,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val senderName = "Someone"
|
||||
sequenceOf(false, true).forEach { isDm ->
|
||||
val message = createRoomEvent(false, senderName, UnableToDecryptContent(UnableToDecryptContent.Data.Unknown))
|
||||
val result = formatter.processMessageItem(message, isDm)
|
||||
val result = formatter.format(message, isDm)
|
||||
if (isDm) {
|
||||
Truth.assertThat(result).isEqualTo(expected)
|
||||
} else {
|
||||
@@ -125,7 +133,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
UnknownContent,
|
||||
).forEach { type ->
|
||||
val message = createRoomEvent(false, senderName, type)
|
||||
val result = formatter.processMessageItem(message, isDm)
|
||||
val result = formatter.format(message, isDm)
|
||||
if (isDm) {
|
||||
Truth.assertWithMessage("$type was not properly handled").that(result).isEqualTo(expected)
|
||||
} else {
|
||||
@@ -145,6 +153,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
fun createMessageContent(type: MessageType): MessageContent {
|
||||
return MessageContent(body, null, false, type)
|
||||
}
|
||||
|
||||
val sharedContentMessagesTypes = arrayOf(
|
||||
TextMessageType(body, null),
|
||||
VideoMessageType(body, "url", null),
|
||||
@@ -163,7 +172,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
sharedContentMessagesTypes.forEach { type ->
|
||||
val content = createMessageContent(type)
|
||||
val message = createRoomEvent(sentByYou = false, senderDisplayName = "Someone", content = content)
|
||||
val result = formatter.processMessageItem(message, isDmRoom = isDm)
|
||||
val result = formatter.format(message, isDmRoom = isDm)
|
||||
if (isDm) {
|
||||
resultsInDm.add(type to result)
|
||||
} else {
|
||||
@@ -171,7 +180,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
}
|
||||
}
|
||||
val unknownMessage = createRoomEvent(sentByYou = false, senderDisplayName = "Someone", content = createMessageContent(UnknownMessageType))
|
||||
val result = UnknownMessageType to formatter.processMessageItem(unknownMessage, isDmRoom = isDm)
|
||||
val result = UnknownMessageType to formatter.format(unknownMessage, isDmRoom = isDm)
|
||||
if (isDm) {
|
||||
resultsInDm.add(result)
|
||||
} else {
|
||||
@@ -235,11 +244,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.JOINED)
|
||||
|
||||
val youJoinedRoomEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
||||
val youJoinedRoom = formatter.processMessageItem(youJoinedRoomEvent, false)
|
||||
val youJoinedRoom = formatter.format(youJoinedRoomEvent, false)
|
||||
Truth.assertThat(youJoinedRoom).isEqualTo("You joined the room")
|
||||
|
||||
val someoneJoinedRoomEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
||||
val someoneJoinedRoom = formatter.processMessageItem(someoneJoinedRoomEvent, false)
|
||||
val someoneJoinedRoom = formatter.format(someoneJoinedRoomEvent, false)
|
||||
Truth.assertThat(someoneJoinedRoom).isEqualTo("${someoneContent.userId} joined the room")
|
||||
}
|
||||
|
||||
@@ -251,11 +260,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.LEFT)
|
||||
|
||||
val youLeftRoomEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
||||
val youLeftRoom = formatter.processMessageItem(youLeftRoomEvent, false)
|
||||
val youLeftRoom = formatter.format(youLeftRoomEvent, false)
|
||||
Truth.assertThat(youLeftRoom).isEqualTo("You left the room")
|
||||
|
||||
val someoneLeftRoomEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
||||
val someoneLeftRoom = formatter.processMessageItem(someoneLeftRoomEvent, false)
|
||||
val someoneLeftRoom = formatter.format(someoneLeftRoomEvent, false)
|
||||
Truth.assertThat(someoneLeftRoom).isEqualTo("${someoneContent.userId} left the room")
|
||||
}
|
||||
|
||||
@@ -269,19 +278,19 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val someoneKickedContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.KICKED_AND_BANNED)
|
||||
|
||||
val youBannedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
||||
val youBanned = formatter.processMessageItem(youBannedEvent, false)
|
||||
val youBanned = formatter.format(youBannedEvent, false)
|
||||
Truth.assertThat(youBanned).isEqualTo("You banned ${youContent.userId}")
|
||||
|
||||
val youKickBannedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youKickedContent)
|
||||
val youKickedBanned = formatter.processMessageItem(youKickBannedEvent, false)
|
||||
val youKickedBanned = formatter.format(youKickBannedEvent, false)
|
||||
Truth.assertThat(youKickedBanned).isEqualTo("You banned ${youContent.userId}")
|
||||
|
||||
val someoneBannedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
||||
val someoneBanned = formatter.processMessageItem(someoneBannedEvent, false)
|
||||
val someoneBanned = formatter.format(someoneBannedEvent, false)
|
||||
Truth.assertThat(someoneBanned).isEqualTo("$otherName banned ${someoneContent.userId}")
|
||||
|
||||
val someoneKickBannedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneKickedContent)
|
||||
val someoneKickBanned = formatter.processMessageItem(someoneKickBannedEvent, false)
|
||||
val someoneKickBanned = formatter.format(someoneKickBannedEvent, false)
|
||||
Truth.assertThat(someoneKickBanned).isEqualTo("$otherName banned ${someoneContent.userId}")
|
||||
}
|
||||
|
||||
@@ -293,11 +302,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.UNBANNED)
|
||||
|
||||
val youUnbannedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
||||
val youUnbanned = formatter.processMessageItem(youUnbannedEvent, false)
|
||||
val youUnbanned = formatter.format(youUnbannedEvent, false)
|
||||
Truth.assertThat(youUnbanned).isEqualTo("You unbanned ${youContent.userId}")
|
||||
|
||||
val someoneUnbannedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
||||
val someoneUnbanned = formatter.processMessageItem(someoneUnbannedEvent, false)
|
||||
val someoneUnbanned = formatter.format(someoneUnbannedEvent, false)
|
||||
Truth.assertThat(someoneUnbanned).isEqualTo("$otherName unbanned ${someoneContent.userId}")
|
||||
}
|
||||
|
||||
@@ -309,11 +318,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.KICKED)
|
||||
|
||||
val youKickedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
||||
val youKicked = formatter.processMessageItem(youKickedEvent, false)
|
||||
val youKicked = formatter.format(youKickedEvent, false)
|
||||
Truth.assertThat(youKicked).isEqualTo("You removed ${youContent.userId}")
|
||||
|
||||
val someoneKickedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
||||
val someoneKicked = formatter.processMessageItem(someoneKickedEvent, false)
|
||||
val someoneKicked = formatter.format(someoneKickedEvent, false)
|
||||
Truth.assertThat(someoneKicked).isEqualTo("$otherName removed ${someoneContent.userId}")
|
||||
}
|
||||
|
||||
@@ -325,15 +334,15 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.INVITED)
|
||||
|
||||
val youWereInvitedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = youContent)
|
||||
val youWereInvited = formatter.processMessageItem(youWereInvitedEvent, false)
|
||||
val youWereInvited = formatter.format(youWereInvitedEvent, false)
|
||||
Truth.assertThat(youWereInvited).isEqualTo("$otherName invited you")
|
||||
|
||||
val youInvitedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = someoneContent)
|
||||
val youInvited = formatter.processMessageItem(youInvitedEvent, false)
|
||||
val youInvited = formatter.format(youInvitedEvent, false)
|
||||
Truth.assertThat(youInvited).isEqualTo("You invited ${someoneContent.userId}")
|
||||
|
||||
val someoneInvitedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
||||
val someoneInvited = formatter.processMessageItem(someoneInvitedEvent, false)
|
||||
val someoneInvited = formatter.format(someoneInvitedEvent, false)
|
||||
Truth.assertThat(someoneInvited).isEqualTo("$otherName invited ${someoneContent.userId}")
|
||||
}
|
||||
|
||||
@@ -345,11 +354,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.INVITATION_ACCEPTED)
|
||||
|
||||
val youAcceptedInviteEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
||||
val youAcceptedInvite = formatter.processMessageItem(youAcceptedInviteEvent, false)
|
||||
val youAcceptedInvite = formatter.format(youAcceptedInviteEvent, false)
|
||||
Truth.assertThat(youAcceptedInvite).isEqualTo("You accepted the invite")
|
||||
|
||||
val someoneAcceptedInviteEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
||||
val someoneAcceptedInvite = formatter.processMessageItem(someoneAcceptedInviteEvent, false)
|
||||
val someoneAcceptedInvite = formatter.format(someoneAcceptedInviteEvent, false)
|
||||
Truth.assertThat(someoneAcceptedInvite).isEqualTo("${someoneContent.userId} accepted the invite")
|
||||
}
|
||||
|
||||
@@ -361,11 +370,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.INVITATION_REJECTED)
|
||||
|
||||
val youRejectedInviteEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
||||
val youRejectedInvite = formatter.processMessageItem(youRejectedInviteEvent, false)
|
||||
val youRejectedInvite = formatter.format(youRejectedInviteEvent, false)
|
||||
Truth.assertThat(youRejectedInvite).isEqualTo("You rejected the invitation")
|
||||
|
||||
val someoneRejectedInviteEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
||||
val someoneRejectedInvite = formatter.processMessageItem(someoneRejectedInviteEvent, false)
|
||||
val someoneRejectedInvite = formatter.format(someoneRejectedInviteEvent, false)
|
||||
Truth.assertThat(someoneRejectedInvite).isEqualTo("${someoneContent.userId} rejected the invitation")
|
||||
}
|
||||
|
||||
@@ -376,11 +385,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.INVITATION_REVOKED)
|
||||
|
||||
val youRevokedInviteEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = someoneContent)
|
||||
val youRevokedInvite = formatter.processMessageItem(youRevokedInviteEvent, false)
|
||||
val youRevokedInvite = formatter.format(youRevokedInviteEvent, false)
|
||||
Truth.assertThat(youRevokedInvite).isEqualTo("You revoked the invitation for ${someoneContent.userId} to join the room")
|
||||
|
||||
val someoneRevokedInviteEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
||||
val someoneRevokedInvite = formatter.processMessageItem(someoneRevokedInviteEvent, false)
|
||||
val someoneRevokedInvite = formatter.format(someoneRevokedInviteEvent, false)
|
||||
Truth.assertThat(someoneRevokedInvite).isEqualTo("$otherName revoked the invitation for ${someoneContent.userId} to join the room")
|
||||
}
|
||||
|
||||
@@ -392,11 +401,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.KNOCKED)
|
||||
|
||||
val youKnockedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
||||
val youKnocked = formatter.processMessageItem(youKnockedEvent, false)
|
||||
val youKnocked = formatter.format(youKnockedEvent, false)
|
||||
Truth.assertThat(youKnocked).isEqualTo("You requested to join")
|
||||
|
||||
val someoneKnockedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
||||
val someoneKnocked = formatter.processMessageItem(someoneKnockedEvent, false)
|
||||
val someoneKnocked = formatter.format(someoneKnockedEvent, false)
|
||||
Truth.assertThat(someoneKnocked).isEqualTo("${someoneContent.userId} requested to join")
|
||||
}
|
||||
|
||||
@@ -407,11 +416,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.KNOCK_ACCEPTED)
|
||||
|
||||
val youAcceptedKnockEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = someoneContent)
|
||||
val youAcceptedKnock = formatter.processMessageItem(youAcceptedKnockEvent, false)
|
||||
val youAcceptedKnock = formatter.format(youAcceptedKnockEvent, false)
|
||||
Truth.assertThat(youAcceptedKnock).isEqualTo("${someoneContent.userId} allowed you to join")
|
||||
|
||||
val someoneAcceptedKnockEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
||||
val someoneAcceptedKnock = formatter.processMessageItem(someoneAcceptedKnockEvent, false)
|
||||
val someoneAcceptedKnock = formatter.format(someoneAcceptedKnockEvent, false)
|
||||
Truth.assertThat(someoneAcceptedKnock).isEqualTo("$otherName allowed ${someoneContent.userId} to join")
|
||||
}
|
||||
|
||||
@@ -423,11 +432,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.KNOCK_RETRACTED)
|
||||
|
||||
val youRetractedKnockEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
||||
val youRetractedKnock = formatter.processMessageItem(youRetractedKnockEvent, false)
|
||||
val youRetractedKnock = formatter.format(youRetractedKnockEvent, false)
|
||||
Truth.assertThat(youRetractedKnock).isEqualTo("You cancelled your request to join")
|
||||
|
||||
val someoneRetractedKnockEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
||||
val someoneRetractedKnock = formatter.processMessageItem(someoneRetractedKnockEvent, false)
|
||||
val someoneRetractedKnock = formatter.format(someoneRetractedKnockEvent, false)
|
||||
Truth.assertThat(someoneRetractedKnock).isEqualTo("${someoneContent.userId} is no longer interested in joining")
|
||||
}
|
||||
|
||||
@@ -439,15 +448,15 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.KNOCK_DENIED)
|
||||
|
||||
val youDeniedKnockEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = someoneContent)
|
||||
val youDeniedKnock = formatter.processMessageItem(youDeniedKnockEvent, false)
|
||||
val youDeniedKnock = formatter.format(youDeniedKnockEvent, false)
|
||||
Truth.assertThat(youDeniedKnock).isEqualTo("You rejected ${someoneContent.userId}'s request to join")
|
||||
|
||||
val someoneDeniedKnockEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
||||
val someoneDeniedKnock = formatter.processMessageItem(someoneDeniedKnockEvent, false)
|
||||
val someoneDeniedKnock = formatter.format(someoneDeniedKnockEvent, false)
|
||||
Truth.assertThat(someoneDeniedKnock).isEqualTo("$otherName rejected ${someoneContent.userId}'s request to join")
|
||||
|
||||
val someoneDeniedYourKnockEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = youContent)
|
||||
val someoneDeniedYourKnock = formatter.processMessageItem(someoneDeniedYourKnockEvent, false)
|
||||
val someoneDeniedYourKnock = formatter.format(someoneDeniedYourKnockEvent, false)
|
||||
Truth.assertThat(someoneDeniedYourKnock).isEqualTo("$otherName rejected your request to join")
|
||||
}
|
||||
|
||||
@@ -459,7 +468,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val results = otherChanges.map { change ->
|
||||
val content = RoomMembershipContent(A_USER_ID, change)
|
||||
val event = createRoomEvent(sentByYou = false, senderDisplayName = "Someone", content = content)
|
||||
val result = formatter.processMessageItem(event, false)
|
||||
val result = formatter.format(event, false)
|
||||
change to result
|
||||
}
|
||||
val expected = otherChanges.map { it to null }
|
||||
@@ -478,19 +487,19 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val removedContent = StateContent("", OtherState.RoomAvatar(null))
|
||||
|
||||
val youChangedRoomAvatarEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent)
|
||||
val youChangedRoomAvatar = formatter.processMessageItem(youChangedRoomAvatarEvent, false)
|
||||
val youChangedRoomAvatar = formatter.format(youChangedRoomAvatarEvent, false)
|
||||
Truth.assertThat(youChangedRoomAvatar).isEqualTo("You changed the room avatar")
|
||||
|
||||
val someoneChangedRoomAvatarEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent)
|
||||
val someoneChangedRoomAvatar = formatter.processMessageItem(someoneChangedRoomAvatarEvent, false)
|
||||
val someoneChangedRoomAvatar = formatter.format(someoneChangedRoomAvatarEvent, false)
|
||||
Truth.assertThat(someoneChangedRoomAvatar).isEqualTo("$otherName changed the room avatar")
|
||||
|
||||
val youRemovedRoomAvatarEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent)
|
||||
val youRemovedRoomAvatar = formatter.processMessageItem(youRemovedRoomAvatarEvent, false)
|
||||
val youRemovedRoomAvatar = formatter.format(youRemovedRoomAvatarEvent, false)
|
||||
Truth.assertThat(youRemovedRoomAvatar).isEqualTo("You removed the room avatar")
|
||||
|
||||
val someoneRemovedRoomAvatarEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent)
|
||||
val someoneRemovedRoomAvatar = formatter.processMessageItem(someoneRemovedRoomAvatarEvent, false)
|
||||
val someoneRemovedRoomAvatar = formatter.format(someoneRemovedRoomAvatarEvent, false)
|
||||
Truth.assertThat(someoneRemovedRoomAvatar).isEqualTo("$otherName removed the room avatar")
|
||||
}
|
||||
|
||||
@@ -501,11 +510,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val content = StateContent("", OtherState.RoomCreate)
|
||||
|
||||
val youCreatedRoomMessage = createRoomEvent(sentByYou = true, senderDisplayName = null, content = content)
|
||||
val youCreatedRoom = formatter.processMessageItem(youCreatedRoomMessage, false)
|
||||
val youCreatedRoom = formatter.format(youCreatedRoomMessage, false)
|
||||
Truth.assertThat(youCreatedRoom).isEqualTo("You created the room")
|
||||
|
||||
val someoneCreatedRoomEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = content)
|
||||
val someoneCreatedRoom = formatter.processMessageItem(someoneCreatedRoomEvent, false)
|
||||
val someoneCreatedRoom = formatter.format(someoneCreatedRoomEvent, false)
|
||||
Truth.assertThat(someoneCreatedRoom).isEqualTo("$otherName created the room")
|
||||
}
|
||||
|
||||
@@ -516,11 +525,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val content = StateContent("", OtherState.RoomEncryption)
|
||||
|
||||
val youCreatedRoomMessage = createRoomEvent(sentByYou = true, senderDisplayName = null, content = content)
|
||||
val youCreatedRoom = formatter.processMessageItem(youCreatedRoomMessage, false)
|
||||
val youCreatedRoom = formatter.format(youCreatedRoomMessage, false)
|
||||
Truth.assertThat(youCreatedRoom).isEqualTo("Encryption enabled")
|
||||
|
||||
val someoneCreatedRoomEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = content)
|
||||
val someoneCreatedRoom = formatter.processMessageItem(someoneCreatedRoomEvent, false)
|
||||
val someoneCreatedRoom = formatter.format(someoneCreatedRoomEvent, false)
|
||||
Truth.assertThat(someoneCreatedRoom).isEqualTo("Encryption enabled")
|
||||
}
|
||||
|
||||
@@ -533,19 +542,19 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val removedContent = StateContent("", OtherState.RoomName(null))
|
||||
|
||||
val youChangedRoomNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent)
|
||||
val youChangedRoomName = formatter.processMessageItem(youChangedRoomNameEvent, false)
|
||||
val youChangedRoomName = formatter.format(youChangedRoomNameEvent, false)
|
||||
Truth.assertThat(youChangedRoomName).isEqualTo("You changed the room name to: $newName")
|
||||
|
||||
val someoneChangedRoomNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent)
|
||||
val someoneChangedRoomName = formatter.processMessageItem(someoneChangedRoomNameEvent, false)
|
||||
val someoneChangedRoomName = formatter.format(someoneChangedRoomNameEvent, false)
|
||||
Truth.assertThat(someoneChangedRoomName).isEqualTo("$otherName changed the room name to: $newName")
|
||||
|
||||
val youRemovedRoomNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent)
|
||||
val youRemovedRoomName = formatter.processMessageItem(youRemovedRoomNameEvent, false)
|
||||
val youRemovedRoomName = formatter.format(youRemovedRoomNameEvent, false)
|
||||
Truth.assertThat(youRemovedRoomName).isEqualTo("You removed the room name")
|
||||
|
||||
val someoneRemovedRoomNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent)
|
||||
val someoneRemovedRoomName = formatter.processMessageItem(someoneRemovedRoomNameEvent, false)
|
||||
val someoneRemovedRoomName = formatter.format(someoneRemovedRoomNameEvent, false)
|
||||
Truth.assertThat(someoneRemovedRoomName).isEqualTo("$otherName removed the room name")
|
||||
}
|
||||
|
||||
@@ -558,19 +567,19 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val removedContent = StateContent("", OtherState.RoomThirdPartyInvite(null))
|
||||
|
||||
val youInvitedSomeoneEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent)
|
||||
val youInvitedSomeone = formatter.processMessageItem(youInvitedSomeoneEvent, false)
|
||||
val youInvitedSomeone = formatter.format(youInvitedSomeoneEvent, false)
|
||||
Truth.assertThat(youInvitedSomeone).isEqualTo("You sent an invitation to $inviteeName to join the room")
|
||||
|
||||
val someoneInvitedSomeoneEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent)
|
||||
val someoneInvitedSomeone = formatter.processMessageItem(someoneInvitedSomeoneEvent, false)
|
||||
val someoneInvitedSomeone = formatter.format(someoneInvitedSomeoneEvent, false)
|
||||
Truth.assertThat(someoneInvitedSomeone).isEqualTo("$otherName sent an invitation to $inviteeName to join the room")
|
||||
|
||||
val youInvitedNoOneEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent)
|
||||
val youInvitedNoOne = formatter.processMessageItem(youInvitedNoOneEvent, false)
|
||||
val youInvitedNoOne = formatter.format(youInvitedNoOneEvent, false)
|
||||
Truth.assertThat(youInvitedNoOne).isNull()
|
||||
|
||||
val someoneInvitedNoOneEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent)
|
||||
val someoneInvitedNoOne = formatter.processMessageItem(someoneInvitedNoOneEvent, false)
|
||||
val someoneInvitedNoOne = formatter.format(someoneInvitedNoOneEvent, false)
|
||||
Truth.assertThat(someoneInvitedNoOne).isNull()
|
||||
}
|
||||
|
||||
@@ -583,19 +592,19 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val removedContent = StateContent("", OtherState.RoomTopic(null))
|
||||
|
||||
val youChangedRoomTopicEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent)
|
||||
val youChangedRoomTopic = formatter.processMessageItem(youChangedRoomTopicEvent, false)
|
||||
val youChangedRoomTopic = formatter.format(youChangedRoomTopicEvent, false)
|
||||
Truth.assertThat(youChangedRoomTopic).isEqualTo("You changed the topic to: $roomTopic")
|
||||
|
||||
val someoneChangedRoomTopicEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent)
|
||||
val someoneChangedRoomTopic = formatter.processMessageItem(someoneChangedRoomTopicEvent, false)
|
||||
val someoneChangedRoomTopic = formatter.format(someoneChangedRoomTopicEvent, false)
|
||||
Truth.assertThat(someoneChangedRoomTopic).isEqualTo("$otherName changed the topic to: $roomTopic")
|
||||
|
||||
val youRemovedRoomTopicEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent)
|
||||
val youRemovedRoomTopic = formatter.processMessageItem(youRemovedRoomTopicEvent, false)
|
||||
val youRemovedRoomTopic = formatter.format(youRemovedRoomTopicEvent, false)
|
||||
Truth.assertThat(youRemovedRoomTopic).isEqualTo("You removed the room topic")
|
||||
|
||||
val someoneRemovedRoomTopicEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent)
|
||||
val someoneRemovedRoomTopic = formatter.processMessageItem(someoneRemovedRoomTopicEvent, false)
|
||||
val someoneRemovedRoomTopic = formatter.format(someoneRemovedRoomTopicEvent, false)
|
||||
Truth.assertThat(someoneRemovedRoomTopic).isEqualTo("$otherName removed the room topic")
|
||||
}
|
||||
|
||||
@@ -611,7 +620,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val results = otherStates.map { state ->
|
||||
val content = StateContent("", state)
|
||||
val event = createRoomEvent(sentByYou = false, senderDisplayName = "Someone", content = content)
|
||||
val result = formatter.processMessageItem(event, false)
|
||||
val result = formatter.format(event, false)
|
||||
state to result
|
||||
}
|
||||
val expected = otherStates.map { it to null }
|
||||
@@ -633,35 +642,35 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val sameContent = aProfileChangeMessageContent(avatarUrl = "same_avatar_url", prevAvatarUrl = "same_avatar_url")
|
||||
|
||||
val youChangedAvatarEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent)
|
||||
val youChangedAvatar = formatter.processMessageItem(youChangedAvatarEvent, false)
|
||||
val youChangedAvatar = formatter.format(youChangedAvatarEvent, false)
|
||||
Truth.assertThat(youChangedAvatar).isEqualTo("You changed your avatar")
|
||||
|
||||
val someoneChangeAvatarEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent)
|
||||
val someoneChangeAvatar = formatter.processMessageItem(someoneChangeAvatarEvent, false)
|
||||
val someoneChangeAvatar = formatter.format(someoneChangeAvatarEvent, false)
|
||||
Truth.assertThat(someoneChangeAvatar).isEqualTo("$otherName changed their avatar")
|
||||
|
||||
val youSetAvatarEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = setContent)
|
||||
val youSetAvatar = formatter.processMessageItem(youSetAvatarEvent, false)
|
||||
val youSetAvatar = formatter.format(youSetAvatarEvent, false)
|
||||
Truth.assertThat(youSetAvatar).isEqualTo("You changed your avatar")
|
||||
|
||||
val someoneSetAvatarEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = setContent)
|
||||
val someoneSetAvatar = formatter.processMessageItem(someoneSetAvatarEvent, false)
|
||||
val someoneSetAvatar = formatter.format(someoneSetAvatarEvent, false)
|
||||
Truth.assertThat(someoneSetAvatar).isEqualTo("$otherName changed their avatar")
|
||||
|
||||
val youRemovedAvatarEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent)
|
||||
val youRemovedAvatar = formatter.processMessageItem(youRemovedAvatarEvent, false)
|
||||
val youRemovedAvatar = formatter.format(youRemovedAvatarEvent, false)
|
||||
Truth.assertThat(youRemovedAvatar).isEqualTo("You changed your avatar")
|
||||
|
||||
val someoneRemovedAvatarEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent)
|
||||
val someoneRemovedAvatar = formatter.processMessageItem(someoneRemovedAvatarEvent, false)
|
||||
val someoneRemovedAvatar = formatter.format(someoneRemovedAvatarEvent, false)
|
||||
Truth.assertThat(someoneRemovedAvatar).isEqualTo("$otherName changed their avatar")
|
||||
|
||||
val unchangedEvent = createRoomEvent(sentByYou = true, senderDisplayName = otherName, content = sameContent)
|
||||
val unchangedResult = formatter.processMessageItem(unchangedEvent, false)
|
||||
val unchangedResult = formatter.format(unchangedEvent, false)
|
||||
Truth.assertThat(unchangedResult).isNull()
|
||||
|
||||
val invalidEvent = createRoomEvent(sentByYou = true, senderDisplayName = otherName, content = invalidContent)
|
||||
val invalidResult = formatter.processMessageItem(invalidEvent, false)
|
||||
val invalidResult = formatter.format(invalidEvent, false)
|
||||
Truth.assertThat(invalidResult).isNull()
|
||||
}
|
||||
|
||||
@@ -678,35 +687,35 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
val invalidContent = aProfileChangeMessageContent(displayName = null, prevDisplayName = null)
|
||||
|
||||
val youChangedDisplayNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent)
|
||||
val youChangedDisplayName = formatter.processMessageItem(youChangedDisplayNameEvent, false)
|
||||
val youChangedDisplayName = formatter.format(youChangedDisplayNameEvent, false)
|
||||
Truth.assertThat(youChangedDisplayName).isEqualTo("You changed your display name from $oldDisplayName to $newDisplayName")
|
||||
|
||||
val someoneChangedDisplayNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent)
|
||||
val someoneChangedDisplayName = formatter.processMessageItem(someoneChangedDisplayNameEvent, false)
|
||||
val someoneChangedDisplayName = formatter.format(someoneChangedDisplayNameEvent, false)
|
||||
Truth.assertThat(someoneChangedDisplayName).isEqualTo("$otherName changed their display name from $oldDisplayName to $newDisplayName")
|
||||
|
||||
val youSetDisplayNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = setContent)
|
||||
val youSetDisplayName = formatter.processMessageItem(youSetDisplayNameEvent, false)
|
||||
val youSetDisplayName = formatter.format(youSetDisplayNameEvent, false)
|
||||
Truth.assertThat(youSetDisplayName).isEqualTo("You set your display name to $newDisplayName")
|
||||
|
||||
val someoneSetDisplayNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = setContent)
|
||||
val someoneSetDisplayName = formatter.processMessageItem(someoneSetDisplayNameEvent, false)
|
||||
val someoneSetDisplayName = formatter.format(someoneSetDisplayNameEvent, false)
|
||||
Truth.assertThat(someoneSetDisplayName).isEqualTo("$otherName set their display name to $newDisplayName")
|
||||
|
||||
val youRemovedDisplayNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent)
|
||||
val youRemovedDisplayName = formatter.processMessageItem(youRemovedDisplayNameEvent, false)
|
||||
val youRemovedDisplayName = formatter.format(youRemovedDisplayNameEvent, false)
|
||||
Truth.assertThat(youRemovedDisplayName).isEqualTo("You removed your display name (it was $oldDisplayName)")
|
||||
|
||||
val someoneRemovedDisplayNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent)
|
||||
val someoneRemovedDisplayName = formatter.processMessageItem(someoneRemovedDisplayNameEvent, false)
|
||||
val someoneRemovedDisplayName = formatter.format(someoneRemovedDisplayNameEvent, false)
|
||||
Truth.assertThat(someoneRemovedDisplayName).isEqualTo("$otherName removed their display name (it was $oldDisplayName)")
|
||||
|
||||
val unchangedEvent = createRoomEvent(sentByYou = true, senderDisplayName = otherName, content = sameContent)
|
||||
val unchangedResult = formatter.processMessageItem(unchangedEvent, false)
|
||||
val unchangedResult = formatter.format(unchangedEvent, false)
|
||||
Truth.assertThat(unchangedResult).isNull()
|
||||
|
||||
val invalidEvent = createRoomEvent(sentByYou = true, senderDisplayName = otherName, content = invalidContent)
|
||||
val invalidResult = formatter.processMessageItem(invalidEvent, false)
|
||||
val invalidResult = formatter.format(invalidEvent, false)
|
||||
Truth.assertThat(invalidResult).isNull()
|
||||
}
|
||||
|
||||
@@ -735,15 +744,15 @@ class DefaultRoomLastMessageFormatterTests {
|
||||
)
|
||||
|
||||
val youChangedBothEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent)
|
||||
val youChangedBoth = formatter.processMessageItem(youChangedBothEvent, false)
|
||||
val youChangedBoth = formatter.format(youChangedBothEvent, false)
|
||||
Truth.assertThat(youChangedBoth).isEqualTo("You changed your display name from $oldDisplayName to $newDisplayName\n(avatar was changed too)")
|
||||
|
||||
val invalidContentEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = invalidContent)
|
||||
val invalidMessage = formatter.processMessageItem(invalidContentEvent, false)
|
||||
val invalidMessage = formatter.format(invalidContentEvent, false)
|
||||
Truth.assertThat(invalidMessage).isNull()
|
||||
|
||||
val sameContentEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = sameContent)
|
||||
val sameMessage = formatter.processMessageItem(sameContentEvent, false)
|
||||
val sameMessage = formatter.format(sameContentEvent, false)
|
||||
Truth.assertThat(sameMessage).isNull()
|
||||
}
|
||||
|
||||
28
libraries/eventformatter/test/build.gradle.kts
Normal file
28
libraries/eventformatter/test/build.gradle.kts
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2022 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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("io.element.android-library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.libraries.eventformatter.test"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.libraries.eventformatter.api)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
}
|
||||
@@ -14,18 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.features.roomlist.impl
|
||||
package io.element.android.libraries.eventformatter.test
|
||||
|
||||
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||
|
||||
class FakeRoomLastMessageFormatter : RoomLastMessageFormatter {
|
||||
|
||||
private var processMessageItemResult: CharSequence? = null
|
||||
override fun processMessageItem(event: EventTimelineItem, isDmRoom: Boolean): CharSequence? {
|
||||
return processMessageItemResult
|
||||
private var result: CharSequence? = null
|
||||
|
||||
override fun format(event: EventTimelineItem, isDmRoom: Boolean): CharSequence? {
|
||||
return result
|
||||
}
|
||||
|
||||
fun givenRoomSummaryResult(result: CharSequence?) {
|
||||
processMessageItemResult = result
|
||||
fun givenFormatResult(result: CharSequence?) {
|
||||
this.result = result
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,16 @@
|
||||
<string name="notification_ticker_text_dm">"%1$s: %2$s"</string>
|
||||
<string name="notification_ticker_text_group">"%1$s: %2$s %3$s"</string>
|
||||
<string name="notification_unread_notified_messages_and_invitation">"%1$s und %2$s"</string>
|
||||
<string name="notification_unread_notified_messages_in_room">"%1$s in %2$s"</string>
|
||||
<string name="notification_unread_notified_messages_in_room_and_invitation">"%1$s in %2$s und %3$s"</string>
|
||||
<plurals name="notification_compat_summary_line_for_room">
|
||||
<item quantity="one">"%1$s: %2$d Nachricht"</item>
|
||||
<item quantity="other">"%1$s: %2$d Nachrichten"</item>
|
||||
</plurals>
|
||||
<plurals name="notification_compat_summary_title">
|
||||
<item quantity="one">"%d Mitteilung"</item>
|
||||
<item quantity="other">"%d Mitteilungen"</item>
|
||||
</plurals>
|
||||
<plurals name="notification_invitations">
|
||||
<item quantity="one">"%d Einladung"</item>
|
||||
<item quantity="other">"%d Einladungen"</item>
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<string name="action_leave_room">"Raum verlassen"</string>
|
||||
<string name="action_next">"Weiter"</string>
|
||||
<string name="action_no">"Nein"</string>
|
||||
<string name="action_not_now">"Nicht jetzt"</string>
|
||||
<string name="action_ok">"OK"</string>
|
||||
<string name="action_quick_reply">"Schnellantwort"</string>
|
||||
<string name="action_quote">"Zitieren"</string>
|
||||
@@ -41,10 +42,11 @@
|
||||
<string name="action_share">"Teilen"</string>
|
||||
<string name="action_share_link">"Link teilen"</string>
|
||||
<string name="action_skip">"Überspringen"</string>
|
||||
<string name="action_start_chat">"Chat starten"</string>
|
||||
<string name="action_take_photo">"Foto aufnehmen"</string>
|
||||
<string name="action_yes">"Ja"</string>
|
||||
<string name="common_about">"Über"</string>
|
||||
<string name="common_analytics">"Analytik"</string>
|
||||
<string name="common_analytics">"Analyse"</string>
|
||||
<string name="common_audio">"Audio"</string>
|
||||
<string name="common_bubbles">"Blasen"</string>
|
||||
<string name="common_decryption_error">"Entschlüsselungsfehler"</string>
|
||||
@@ -61,8 +63,12 @@
|
||||
<string name="common_offline">"Offline"</string>
|
||||
<string name="common_password">"Passwort"</string>
|
||||
<string name="common_reactions">"Reaktionen"</string>
|
||||
<string name="common_report_a_bug">"Fehler melden"</string>
|
||||
<string name="common_search_results">"Suchergebnisse"</string>
|
||||
<string name="common_security">"Sicherheit"</string>
|
||||
<string name="common_server_not_supported">"Server wird nicht unterstützt"</string>
|
||||
<string name="common_settings">"Einstellungen"</string>
|
||||
<string name="common_starting_chat">"Chat wird gestartet…"</string>
|
||||
<string name="common_sticker">"Sticker"</string>
|
||||
<string name="common_success">"Erfolg"</string>
|
||||
<string name="common_suggestions">"Vorschläge"</string>
|
||||
@@ -84,6 +90,7 @@
|
||||
<string name="emoji_picker_category_places">"Reisen & Orte"</string>
|
||||
<string name="emoji_picker_category_symbols">"Symbole"</string>
|
||||
<string name="error_failed_loading_messages">"Fehler beim Laden der Nachrichten"</string>
|
||||
<string name="error_some_messages_have_not_been_sent">"Einige Nachrichten wurden nicht gesendet"</string>
|
||||
<string name="error_unknown">"Entschuldigung, ein Fehler ist aufgetreten."</string>
|
||||
<string name="login_initial_device_name_android">"%1$s Android"</string>
|
||||
<plurals name="common_member_count">
|
||||
@@ -93,6 +100,9 @@
|
||||
<string name="report_content_hint">"Grund für die Meldung dieses Inhalts"</string>
|
||||
<string name="room_timeline_beginning_of_room">"Dies ist der Anfang von %1$s."</string>
|
||||
<string name="room_timeline_read_marker_title">"Neu"</string>
|
||||
<string name="screen_analytics_prompt_data_usage">"Wir erfassen und analysieren "<b>"keine"</b>" Account-Daten"</string>
|
||||
<string name="screen_analytics_prompt_settings">"Sie können die Analyse jederzeit in den Einstellungen deaktivieren"</string>
|
||||
<string name="screen_analytics_prompt_third_party_sharing">"Wir geben "<b>"keine"</b>" Informationen an Dritte weiter"</string>
|
||||
<string name="screen_analytics_settings_share_data">"Teile Analyse-Daten"</string>
|
||||
<string name="screen_media_picker_error_failed_selection">"Medienauswahl fehlgeschlagen, bitte versuche es erneut."</string>
|
||||
<string name="settings_rageshake_detection_threshold">"Erkennungsschwelle"</string>
|
||||
|
||||
@@ -116,10 +116,6 @@
|
||||
<item quantity="one">"%1$d miembro"</item>
|
||||
<item quantity="other">"%1$d miembros"</item>
|
||||
</plurals>
|
||||
<plurals name="room_timeline_state_changes">
|
||||
<item quantity="one">"%1$d cambio en la sala"</item>
|
||||
<item quantity="other">"%1$d cambios en la sala"</item>
|
||||
</plurals>
|
||||
<string name="preference_rageshake">"Agitar con fuerza para informar de un error"</string>
|
||||
<string name="rageshake_dialog_content">"Parece que sacudes el teléfono con frustración. ¿Quieres abrir la pantalla de informe de errores?"</string>
|
||||
<string name="report_content_explanation">"Este mensaje se notificará al administrador de su homeserver. No podrán leer ningún mensaje cifrado."</string>
|
||||
|
||||
@@ -116,10 +116,6 @@
|
||||
<item quantity="one">"%1$d membro"</item>
|
||||
<item quantity="other">"%1$d membri"</item>
|
||||
</plurals>
|
||||
<plurals name="room_timeline_state_changes">
|
||||
<item quantity="one">"%1$d modifica alla stanza"</item>
|
||||
<item quantity="other">"%1$d modifiche alla stanza"</item>
|
||||
</plurals>
|
||||
<string name="preference_rageshake">"Scuoti per segnalare un problema"</string>
|
||||
<string name="rageshake_dialog_content">"Sembra che tu stia scuotendo il telefono per la frustrazione. Vuoi aprire la schermata di segnalazione dei problemi?"</string>
|
||||
<string name="report_content_explanation">"Questo messaggio verrà segnalato all\'amministratore dell\'homeserver. Questi non sarà in grado di leggere i messaggi criptati."</string>
|
||||
|
||||
@@ -125,11 +125,6 @@
|
||||
<item quantity="few">"%1$d membri"</item>
|
||||
<item quantity="other">"%1$d membri"</item>
|
||||
</plurals>
|
||||
<plurals name="room_timeline_state_changes">
|
||||
<item quantity="one">"%1$d schimbare a camerii"</item>
|
||||
<item quantity="few">"%1$d schimbări ale camerei"</item>
|
||||
<item quantity="other">"%1$d schimbări ale camerei"</item>
|
||||
</plurals>
|
||||
<string name="preference_rageshake">"Rageshake pentru a raporta erori"</string>
|
||||
<string name="rageshake_dialog_content">"Se pare că scuturați telefonul de frustrare. Doriți să deschdeți ecranul de raportare a unei erori?"</string>
|
||||
<string name="report_content_explanation">"Acest mesaj va fi raportat administratorilor homeserver-ului tau. Ei nu vor putea citi niciun mesaj criptat."</string>
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
<string name="error_failed_loading_messages">"Failed loading messages"</string>
|
||||
<string name="error_some_messages_have_not_been_sent">"Some messages have not been sent"</string>
|
||||
<string name="error_unknown">"Sorry, an error occurred"</string>
|
||||
<string name="invite_friends_rich_title">"🔐️ Join me on %1$s"</string>
|
||||
<string name="invite_friends_rich_title">"🔐️ Join me on %1$s"</string>
|
||||
<string name="invite_friends_text">"Hey, talk to me on %1$s: %2$s"</string>
|
||||
<string name="leave_room_alert_empty_subtitle">"Are you sure that you want to leave this room? You are the only person here. If you leave, no one will be able to join in the future, including you."</string>
|
||||
<string name="leave_room_alert_private_subtitle">"Are you sure that you want to leave this room? This room is not public and you will not be able to rejoin without an invite."</string>
|
||||
@@ -132,10 +132,6 @@
|
||||
<item quantity="one">"%1$d member"</item>
|
||||
<item quantity="other">"%1$d members"</item>
|
||||
</plurals>
|
||||
<plurals name="room_timeline_state_changes">
|
||||
<item quantity="one">"%1$d room change"</item>
|
||||
<item quantity="other">"%1$d room changes"</item>
|
||||
</plurals>
|
||||
<string name="preference_rageshake">"Rageshake to report bug"</string>
|
||||
<string name="rageshake_dialog_content">"You seem to be shaking the phone in frustration. Would you like to open the bug report screen?"</string>
|
||||
<string name="report_content_explanation">"This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages."</string>
|
||||
@@ -167,4 +163,4 @@
|
||||
<string name="screen_analytics_settings_read_terms">"You can read all our terms %1$s."</string>
|
||||
<string name="screen_analytics_settings_read_terms_content_link">"here"</string>
|
||||
<string name="screen_report_content_block_user">"Block user"</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -80,6 +80,7 @@ fun DependencyHandlerScope.allLibrariesImpl() {
|
||||
implementation(project(":libraries:matrixui"))
|
||||
implementation(project(":libraries:network"))
|
||||
implementation(project(":libraries:core"))
|
||||
implementation(project(":libraries:eventformatter:impl"))
|
||||
implementation(project(":libraries:permissions:impl"))
|
||||
implementation(project(":libraries:push:impl"))
|
||||
implementation(project(":libraries:push:impl"))
|
||||
|
||||
@@ -55,11 +55,13 @@ dependencies {
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.core)
|
||||
implementation(projects.libraries.dateformatter.impl)
|
||||
implementation(projects.libraries.eventformatter.impl)
|
||||
implementation(projects.features.invitelist.impl)
|
||||
implementation(projects.features.roomlist.impl)
|
||||
implementation(projects.features.leaveroom.impl)
|
||||
implementation(projects.features.login.impl)
|
||||
implementation(projects.features.networkmonitor.impl)
|
||||
implementation(projects.services.toolbox.impl)
|
||||
implementation(libs.coroutines.core)
|
||||
coreLibraryDesugaring(libs.android.desugar)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import io.element.android.features.invitelist.impl.DefaultSeenInvitesStore
|
||||
import io.element.android.features.leaveroom.impl.LeaveRoomPresenterImpl
|
||||
import io.element.android.features.networkmonitor.impl.NetworkMonitorImpl
|
||||
import io.element.android.features.roomlist.impl.DefaultInviteStateDataSource
|
||||
import io.element.android.features.roomlist.impl.DefaultRoomLastMessageFormatter
|
||||
import io.element.android.features.roomlist.impl.RoomListPresenter
|
||||
import io.element.android.features.roomlist.impl.RoomListView
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
@@ -32,9 +31,14 @@ import io.element.android.libraries.dateformatter.impl.DateFormatters
|
||||
import io.element.android.libraries.dateformatter.impl.DefaultLastMessageTimestampFormatter
|
||||
import io.element.android.libraries.dateformatter.impl.LocalDateTimeProvider
|
||||
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
|
||||
import io.element.android.libraries.eventformatter.impl.DefaultRoomLastMessageFormatter
|
||||
import io.element.android.libraries.eventformatter.impl.ProfileChangeContentFormatter
|
||||
import io.element.android.libraries.eventformatter.impl.RoomMembershipContentFormatter
|
||||
import io.element.android.libraries.eventformatter.impl.StateContentFormatter
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.services.toolbox.impl.strings.AndroidStringProvider
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.datetime.Clock
|
||||
@@ -52,10 +56,17 @@ class RoomListScreen(
|
||||
private val dateTimeProvider = LocalDateTimeProvider(clock, timeZone)
|
||||
private val dateFormatters = DateFormatters(locale, clock, timeZone)
|
||||
private val sessionVerificationService = matrixClient.sessionVerificationService()
|
||||
private val stringProvider = AndroidStringProvider(context.resources)
|
||||
private val presenter = RoomListPresenter(
|
||||
client = matrixClient,
|
||||
lastMessageTimestampFormatter = DefaultLastMessageTimestampFormatter(dateTimeProvider, dateFormatters),
|
||||
roomLastMessageFormatter = DefaultRoomLastMessageFormatter(context, matrixClient),
|
||||
roomLastMessageFormatter = DefaultRoomLastMessageFormatter(
|
||||
sp = stringProvider,
|
||||
matrixClient = matrixClient,
|
||||
roomMembershipContentFormatter = RoomMembershipContentFormatter(matrixClient, stringProvider),
|
||||
profileChangeContentFormatter = ProfileChangeContentFormatter(stringProvider),
|
||||
stateContentFormatter = StateContentFormatter(stringProvider),
|
||||
),
|
||||
sessionVerificationService = sessionVerificationService,
|
||||
networkMonitor = NetworkMonitorImpl(context),
|
||||
snackbarDispatcher = SnackbarDispatcher(),
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user