Merge pull request #443 from vector-im/feature/bma/timelineStateEventFormatting
Timeline state event formatting
This commit is contained in:
commit
3f1433f24c
118 changed files with 2266 additions and 853 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
<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_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_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_private_option_title">"Privater Raum (nur auf Einladung)"</string>
|
||||||
<string name="screen_create_room_room_name_label">"Raumname"</string>
|
<string name="screen_create_room_room_name_label">"Raumname"</string>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
<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_change_server_subtitle">"Wie lautet die Adresse deines Servers?"</string>
|
||||||
<string name="screen_login_title">"Willkommen zurück!"</string>
|
<string name="screen_login_title">"Willkommen zurück!"</string>
|
||||||
<string name="screen_login_password_hint">"Passwort"</string>
|
<string name="screen_login_password_hint">"Passwort"</string>
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ dependencies {
|
||||||
implementation(projects.libraries.textcomposer)
|
implementation(projects.libraries.textcomposer)
|
||||||
implementation(projects.libraries.uiStrings)
|
implementation(projects.libraries.uiStrings)
|
||||||
implementation(projects.libraries.dateformatter.api)
|
implementation(projects.libraries.dateformatter.api)
|
||||||
|
implementation(projects.libraries.eventformatter.api)
|
||||||
implementation(projects.libraries.mediapickers.api)
|
implementation(projects.libraries.mediapickers.api)
|
||||||
implementation(projects.libraries.featureflag.api)
|
implementation(projects.libraries.featureflag.api)
|
||||||
implementation(projects.libraries.mediaupload.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.actionlist.anActionListState
|
||||||
import io.element.android.features.messages.impl.textcomposer.AttachmentSourcePicker
|
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.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.aTimelineItemList
|
||||||
import io.element.android.features.messages.impl.timeline.aTimelineState
|
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.core.data.StableCharSequence
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
@ -48,7 +48,7 @@ fun aMessagesState() = MessagesState(
|
||||||
mode = MessageComposerMode.Normal("Hello"),
|
mode = MessageComposerMode.Normal("Hello"),
|
||||||
),
|
),
|
||||||
timelineState = aTimelineState().copy(
|
timelineState = aTimelineState().copy(
|
||||||
timelineItems = aTimelineItemList(aTimelineItemContent()),
|
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
|
||||||
),
|
),
|
||||||
actionListState = anActionListState(),
|
actionListState = anActionListState(),
|
||||||
hasNetworkConnection = true,
|
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.AttachmentSourcePicker
|
||||||
import io.element.android.features.messages.impl.textcomposer.MessageComposerEvents
|
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.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.TimelineView
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView
|
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) {
|
fun onActionSelected(action: TimelineItemAction, event: TimelineItem.Event) {
|
||||||
state.eventSink(MessagesEvents.HandleAction(action, event))
|
state.eventSink(MessagesEvents.HandleAction(action, event))
|
||||||
}
|
}
|
||||||
|
|
@ -189,7 +195,8 @@ fun MessagesView(
|
||||||
.padding(padding)
|
.padding(padding)
|
||||||
.consumeWindowInsets(padding),
|
.consumeWindowInsets(padding),
|
||||||
onMessageClicked = ::onMessageClicked,
|
onMessageClicked = ::onMessageClicked,
|
||||||
onMessageLongClicked = ::onMessageLongClicked
|
onMessageLongClicked = ::onMessageLongClicked,
|
||||||
|
onExpandGroupClick = ::onExpandGroupClick,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
snackbarHost = {
|
snackbarHost = {
|
||||||
|
|
@ -214,6 +221,7 @@ fun MessagesViewContent(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onMessageClicked: (TimelineItem.Event) -> Unit = {},
|
onMessageClicked: (TimelineItem.Event) -> Unit = {},
|
||||||
onMessageLongClicked: (TimelineItem.Event) -> Unit = {},
|
onMessageLongClicked: (TimelineItem.Event) -> Unit = {},
|
||||||
|
onExpandGroupClick: (TimelineItem.GroupedEvents) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
|
@ -227,7 +235,8 @@ fun MessagesViewContent(
|
||||||
state = state.timelineState,
|
state = state.timelineState,
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
onMessageClicked = onMessageClicked,
|
onMessageClicked = onMessageClicked,
|
||||||
onMessageLongClicked = onMessageLongClicked
|
onMessageLongClicked = onMessageLongClicked,
|
||||||
|
onExpandGroupClick = onExpandGroupClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
MessageComposerView(
|
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.actionlist.model.TimelineItemAction
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
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.TimelineItemRedactedContent
|
||||||
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
|
@ -53,12 +54,16 @@ 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)
|
target.value = ActionListState.Target.Loading(timelineItem)
|
||||||
val actions =
|
val actions =
|
||||||
if (timelineItem.content is TimelineItemRedactedContent) {
|
when (timelineItem.content) {
|
||||||
|
is TimelineItemRedactedContent,
|
||||||
|
is TimelineItemStateContent -> {
|
||||||
|
// TODO Add Share action (also) here, and developer options
|
||||||
emptyList()
|
emptyList()
|
||||||
} else {
|
}
|
||||||
|
else -> {
|
||||||
mutableListOf(
|
mutableListOf(
|
||||||
TimelineItemAction.Reply,
|
TimelineItemAction.Reply,
|
||||||
TimelineItemAction.Forward,
|
TimelineItemAction.Forward,
|
||||||
|
|
@ -70,6 +75,7 @@ class ActionListPresenter @Inject constructor() : Presenter<ActionListState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
target.value = ActionListState.Target.Success(timelineItem, actions.toImmutableList())
|
target.value = ActionListState.Target.Success(timelineItem, actions.toImmutableList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,11 @@
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.timeline
|
package io.element.android.features.messages.impl.timeline
|
||||||
|
|
||||||
|
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
|
|
||||||
sealed interface TimelineEvents {
|
sealed interface TimelineEvents {
|
||||||
object LoadMore : TimelineEvents
|
object LoadMore : TimelineEvents
|
||||||
data class SetHighlightedEvent(val eventId: EventId?) : 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
|
package io.element.android.features.messages.impl.timeline
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.mutableStateMapOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory
|
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.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.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
import io.element.android.libraries.matrix.api.timeline.MatrixTimeline
|
||||||
|
|
@ -42,6 +45,7 @@ private const val backPaginationPageSize = 50
|
||||||
|
|
||||||
class TimelinePresenter @Inject constructor(
|
class TimelinePresenter @Inject constructor(
|
||||||
private val timelineItemsFactory: TimelineItemsFactory,
|
private val timelineItemsFactory: TimelineItemsFactory,
|
||||||
|
private val timelineItemGrouper: TimelineItemGrouper,
|
||||||
room: MatrixRoom,
|
room: MatrixRoom,
|
||||||
) : Presenter<TimelineState> {
|
) : Presenter<TimelineState> {
|
||||||
|
|
||||||
|
|
@ -53,6 +57,8 @@ class TimelinePresenter @Inject constructor(
|
||||||
val highlightedEventId: MutableState<EventId?> = rememberSaveable {
|
val highlightedEventId: MutableState<EventId?> = rememberSaveable {
|
||||||
mutableStateOf(null)
|
mutableStateOf(null)
|
||||||
}
|
}
|
||||||
|
val expandedGroups = remember { mutableStateMapOf<String, Boolean>() }
|
||||||
|
|
||||||
val timelineItems = timelineItemsFactory
|
val timelineItems = timelineItemsFactory
|
||||||
.flow()
|
.flow()
|
||||||
.collectAsState()
|
.collectAsState()
|
||||||
|
|
@ -65,6 +71,9 @@ class TimelinePresenter @Inject constructor(
|
||||||
when (event) {
|
when (event) {
|
||||||
TimelineEvents.LoadMore -> localCoroutineScope.loadMore(paginationState.value)
|
TimelineEvents.LoadMore -> localCoroutineScope.loadMore(paginationState.value)
|
||||||
is TimelineEvents.SetHighlightedEvent -> highlightedEventId.value = event.eventId
|
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(
|
return TimelineState(
|
||||||
highlightedEventId = highlightedEventId.value,
|
highlightedEventId = highlightedEventId.value,
|
||||||
paginationState = paginationState.value,
|
paginationState = paginationState.value,
|
||||||
timelineItems = timelineItems.value.toImmutableList(),
|
timelineItems = timelineItemGrouper.group(timelineItems.value, expandedGroups).toImmutableList(),
|
||||||
eventSink = ::handleEvents
|
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.TimelineItemGroupPosition
|
||||||
import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions
|
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.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.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
|
@ -55,6 +56,12 @@ internal fun aTimelineItemList(content: TimelineItemEventContent): ImmutableList
|
||||||
content = content,
|
content = content,
|
||||||
groupPosition = TimelineItemGroupPosition.First
|
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
|
// 3 items (First Middle Last) with isMine = true
|
||||||
aTimelineItemEvent(
|
aTimelineItemEvent(
|
||||||
isMine = true,
|
isMine = true,
|
||||||
|
|
@ -71,12 +78,18 @@ internal fun aTimelineItemList(content: TimelineItemEventContent): ImmutableList
|
||||||
content = content,
|
content = content,
|
||||||
groupPosition = TimelineItemGroupPosition.First
|
groupPosition = TimelineItemGroupPosition.First
|
||||||
),
|
),
|
||||||
|
// A state event on top of it
|
||||||
|
aTimelineItemEvent(
|
||||||
|
isMine = true,
|
||||||
|
content = aTimelineItemStateEventContent(),
|
||||||
|
groupPosition = TimelineItemGroupPosition.None
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun aTimelineItemEvent(
|
internal fun aTimelineItemEvent(
|
||||||
isMine: Boolean = false,
|
isMine: Boolean = false,
|
||||||
content: TimelineItemEventContent = aTimelineItemContent(),
|
content: TimelineItemEventContent = aTimelineItemTextContent(),
|
||||||
groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.First
|
groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.First
|
||||||
): TimelineItem.Event {
|
): TimelineItem.Event {
|
||||||
val randomId = "\$" + Random.nextInt().toString()
|
val randomId = "\$" + Random.nextInt().toString()
|
||||||
|
|
@ -96,10 +109,3 @@ internal fun aTimelineItemEvent(
|
||||||
groupPosition = groupPosition,
|
groupPosition = groupPosition,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun aTimelineItemContent(): TimelineItemEventContent {
|
|
||||||
return TimelineItemTextContent(
|
|
||||||
body = "Text",
|
|
||||||
htmlDocument = null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.timeline
|
package io.element.android.features.messages.impl.timeline
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxScope
|
import androidx.compose.foundation.layout.BoxScope
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
|
@ -50,19 +50,24 @@ import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.layout.LastBaseline
|
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.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.zIndex
|
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.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.TimelineItemReactionsView
|
||||||
import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView
|
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.TimelineItemDaySeparatorView
|
||||||
import io.element.android.features.messages.impl.timeline.components.virtual.TimelineLoadingMoreIndicator
|
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.TimelineItem
|
||||||
import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState
|
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.TimelineItemEventContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentProvider
|
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.TimelineItemDaySeparatorModel
|
||||||
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemLoadingModel
|
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemLoadingModel
|
||||||
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
import io.element.android.libraries.designsystem.components.avatar.Avatar
|
||||||
|
|
@ -82,6 +87,7 @@ fun TimelineView(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onMessageClicked: (TimelineItem.Event) -> Unit = {},
|
onMessageClicked: (TimelineItem.Event) -> Unit = {},
|
||||||
onMessageLongClicked: (TimelineItem.Event) -> Unit = {},
|
onMessageLongClicked: (TimelineItem.Event) -> Unit = {},
|
||||||
|
onExpandGroupClick: (TimelineItem.GroupedEvents) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun onReachedLoadMore() {
|
fun onReachedLoadMore() {
|
||||||
|
|
@ -93,8 +99,6 @@ fun TimelineView(
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
state = lazyListState,
|
state = lazyListState,
|
||||||
horizontalAlignment = Alignment.Start,
|
|
||||||
verticalArrangement = Arrangement.Bottom,
|
|
||||||
reverseLayout = true
|
reverseLayout = true
|
||||||
) {
|
) {
|
||||||
itemsIndexed(
|
itemsIndexed(
|
||||||
|
|
@ -104,9 +108,10 @@ fun TimelineView(
|
||||||
) { index, timelineItem ->
|
) { index, timelineItem ->
|
||||||
TimelineItemRow(
|
TimelineItemRow(
|
||||||
timelineItem = timelineItem,
|
timelineItem = timelineItem,
|
||||||
isHighlighted = timelineItem.identifier() == state.highlightedEventId?.value,
|
highlightedItem = state.highlightedEventId?.value,
|
||||||
onClick = onMessageClicked,
|
onClick = onMessageClicked,
|
||||||
onLongClick = onMessageLongClicked
|
onLongClick = onMessageLongClicked,
|
||||||
|
onExpandGroupClick = onExpandGroupClick,
|
||||||
)
|
)
|
||||||
if (index == state.timelineItems.lastIndex) {
|
if (index == state.timelineItems.lastIndex) {
|
||||||
onReachedLoadMore()
|
onReachedLoadMore()
|
||||||
|
|
@ -125,16 +130,20 @@ fun TimelineView(
|
||||||
@Composable
|
@Composable
|
||||||
fun TimelineItemRow(
|
fun TimelineItemRow(
|
||||||
timelineItem: TimelineItem,
|
timelineItem: TimelineItem,
|
||||||
isHighlighted: Boolean,
|
highlightedItem: String?,
|
||||||
onClick: (TimelineItem.Event) -> Unit,
|
onClick: (TimelineItem.Event) -> Unit,
|
||||||
onLongClick: (TimelineItem.Event) -> Unit,
|
onLongClick: (TimelineItem.Event) -> Unit,
|
||||||
|
onExpandGroupClick: (TimelineItem.GroupedEvents) -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
when (timelineItem) {
|
when (timelineItem) {
|
||||||
is TimelineItem.Virtual -> TimelineItemVirtualRow(
|
is TimelineItem.Virtual -> {
|
||||||
virtual = timelineItem
|
TimelineItemVirtualRow(
|
||||||
|
virtual = timelineItem,
|
||||||
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
is TimelineItem.Event -> {
|
is TimelineItem.Event -> {
|
||||||
|
|
||||||
fun onClick() {
|
fun onClick() {
|
||||||
onClick(timelineItem)
|
onClick(timelineItem)
|
||||||
}
|
}
|
||||||
|
|
@ -143,14 +152,56 @@ fun TimelineItemRow(
|
||||||
onLongClick(timelineItem)
|
onLongClick(timelineItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (timelineItem.content is TimelineItemStateContent) {
|
||||||
|
TimelineItemStateEventRow(
|
||||||
|
event = timelineItem,
|
||||||
|
isHighlighted = highlightedItem == timelineItem.identifier(),
|
||||||
|
onClick = ::onClick,
|
||||||
|
onLongClick = ::onLongClick,
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
TimelineItemEventRow(
|
TimelineItemEventRow(
|
||||||
event = timelineItem,
|
event = timelineItem,
|
||||||
isHighlighted = isHighlighted,
|
isHighlighted = highlightedItem == timelineItem.identifier(),
|
||||||
onClick = ::onClick,
|
onClick = ::onClick,
|
||||||
onLongClick = ::onLongClick
|
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 = {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -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
|
@Composable
|
||||||
private fun MessageSenderInformation(
|
private fun MessageSenderInformation(
|
||||||
sender: String,
|
sender: String,
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ fun MessageEventBubble(
|
||||||
|
|
||||||
fun Modifier.offsetForItem(): Modifier {
|
fun Modifier.offsetForItem(): Modifier {
|
||||||
return if (state.isMine) {
|
return if (state.isMine) {
|
||||||
|
// FIXME setting y offset to -12.dp can overlap a state event displayed above.
|
||||||
offset(y = -(12.dp))
|
offset(y = -(12.dp))
|
||||||
} else {
|
} else {
|
||||||
offset(x = 20.dp, y = -(12.dp))
|
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.TimelineItemEventContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
|
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.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.TimelineItemTextBasedContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
|
||||||
|
|
||||||
|
|
@ -58,5 +59,9 @@ fun TimelineItemEventContentView(
|
||||||
content = content,
|
content = content,
|
||||||
modifier = modifier
|
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.TimelineItemEventContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
|
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.FailedToParseMessageLikeContent
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
|
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.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.RoomMembershipContent
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
|
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.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.UnableToDecryptContent
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
|
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -43,15 +43,15 @@ class TimelineItemContentFactory @Inject constructor(
|
||||||
private val failedToParseStateFactory: TimelineItemContentFailedToParseStateFactory
|
private val failedToParseStateFactory: TimelineItemContentFailedToParseStateFactory
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun create(itemContent: EventContent): TimelineItemEventContent {
|
fun create(eventTimelineItem: EventTimelineItem): TimelineItemEventContent {
|
||||||
return when (itemContent) {
|
return when (val itemContent = eventTimelineItem.content) {
|
||||||
is FailedToParseMessageLikeContent -> failedToParseMessageFactory.create(itemContent)
|
is FailedToParseMessageLikeContent -> failedToParseMessageFactory.create(itemContent)
|
||||||
is FailedToParseStateContent -> failedToParseStateFactory.create(itemContent)
|
is FailedToParseStateContent -> failedToParseStateFactory.create(itemContent)
|
||||||
is MessageContent -> messageFactory.create(itemContent)
|
is MessageContent -> messageFactory.create(itemContent)
|
||||||
is ProfileChangeContent -> profileChangeFactory.create(itemContent)
|
is ProfileChangeContent -> profileChangeFactory.create(eventTimelineItem)
|
||||||
is RedactedContent -> redactedMessageFactory.create(itemContent)
|
is RedactedContent -> redactedMessageFactory.create(itemContent)
|
||||||
is RoomMembershipContent -> roomMembershipFactory.create(itemContent)
|
is RoomMembershipContent -> roomMembershipFactory.create(eventTimelineItem)
|
||||||
is StateContent -> stateFactory.create(itemContent)
|
is StateContent -> stateFactory.create(eventTimelineItem)
|
||||||
is StickerContent -> stickerFactory.create(itemContent)
|
is StickerContent -> stickerFactory.create(itemContent)
|
||||||
is UnableToDecryptContent -> utdFactory.create(itemContent)
|
is UnableToDecryptContent -> utdFactory.create(itemContent)
|
||||||
is UnknownContent -> TimelineItemUnknownContent
|
is UnknownContent -> TimelineItemUnknownContent
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,18 @@
|
||||||
package io.element.android.features.messages.impl.timeline.factories.event
|
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.TimelineItemEventContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemProfileChangeContent
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
|
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
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TimelineItemContentProfileChangeFactory @Inject constructor() {
|
class TimelineItemContentProfileChangeFactory @Inject constructor(
|
||||||
|
private val timelineEventFormatter: TimelineEventFormatter,
|
||||||
|
) {
|
||||||
|
|
||||||
fun create(content: ProfileChangeContent): TimelineItemEventContent {
|
fun create(eventTimelineItem: EventTimelineItem): TimelineItemEventContent {
|
||||||
return TimelineItemUnknownContent
|
val text = timelineEventFormatter.format(eventTimelineItem)
|
||||||
|
return TimelineItemProfileChangeContent(text.orEmpty().toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,18 @@
|
||||||
package io.element.android.features.messages.impl.timeline.factories.event
|
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.TimelineItemEventContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRoomMembershipContent
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
|
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
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TimelineItemContentRoomMembershipFactory @Inject constructor() {
|
class TimelineItemContentRoomMembershipFactory @Inject constructor(
|
||||||
|
private val timelineEventFormatter: TimelineEventFormatter,
|
||||||
|
) {
|
||||||
|
|
||||||
fun create(content: RoomMembershipContent): TimelineItemEventContent {
|
fun create(eventTimelineItem: EventTimelineItem): TimelineItemEventContent {
|
||||||
return TimelineItemUnknownContent
|
val text = timelineEventFormatter.format(eventTimelineItem)
|
||||||
|
return TimelineItemRoomMembershipContent(text.orEmpty().toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,18 @@
|
||||||
package io.element.android.features.messages.impl.timeline.factories.event
|
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.TimelineItemEventContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateEventContent
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
|
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
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TimelineItemContentStateFactory @Inject constructor() {
|
class TimelineItemContentStateFactory @Inject constructor(
|
||||||
|
private val timelineEventFormatter: TimelineEventFormatter,
|
||||||
|
) {
|
||||||
|
|
||||||
fun create(content: StateContent): TimelineItemEventContent {
|
fun create(eventTimelineItem: EventTimelineItem): TimelineItemEventContent {
|
||||||
return TimelineItemUnknownContent
|
val text = timelineEventFormatter.format(eventTimelineItem)
|
||||||
|
return TimelineItemStateEventContent(text.orEmpty().toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ class TimelineItemEventFactory @Inject constructor(
|
||||||
senderId = currentSender,
|
senderId = currentSender,
|
||||||
senderDisplayName = senderDisplayName,
|
senderDisplayName = senderDisplayName,
|
||||||
senderAvatar = senderAvatarData,
|
senderAvatar = senderAvatarData,
|
||||||
content = contentFactory.create(currentTimelineItem.event.content),
|
content = contentFactory.create(currentTimelineItem.event),
|
||||||
isMine = currentTimelineItem.event.isOwn,
|
isMine = currentTimelineItem.event.isOwn,
|
||||||
groupPosition = groupPosition,
|
groupPosition = groupPosition,
|
||||||
reactionsState = currentTimelineItem.computeReactionsState()
|
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.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
sealed interface TimelineItem {
|
sealed interface TimelineItem {
|
||||||
|
|
@ -29,11 +30,13 @@ sealed interface TimelineItem {
|
||||||
fun identifier(): String = when (this) {
|
fun identifier(): String = when (this) {
|
||||||
is Event -> id
|
is Event -> id
|
||||||
is Virtual -> id
|
is Virtual -> id
|
||||||
|
is GroupedEvents -> id
|
||||||
}
|
}
|
||||||
|
|
||||||
fun contentType(): String = when (this) {
|
fun contentType(): String = when (this) {
|
||||||
is Event -> content.type
|
is Event -> content.type
|
||||||
is Virtual -> model.type
|
is Virtual -> model.type
|
||||||
|
is GroupedEvents -> "groupedEvent"
|
||||||
}
|
}
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
|
|
@ -60,4 +63,13 @@ sealed interface TimelineItem {
|
||||||
|
|
||||||
val safeSenderName: String = senderDisplayName ?: senderId.value
|
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 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"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
<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">"Camera"</string>
|
||||||
<string name="screen_room_attachment_source_camera_photo">"Take photo"</string>
|
<string name="screen_room_attachment_source_camera_photo">"Take photo"</string>
|
||||||
<string name="screen_room_attachment_source_camera_video">"Record a video"</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.actionlist.model.TimelineItemAction
|
||||||
import io.element.android.features.messages.impl.textcomposer.MessageComposerPresenter
|
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.TimelinePresenter
|
||||||
|
import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper
|
||||||
import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
|
import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
|
||||||
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
|
||||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||||
|
|
@ -138,6 +139,7 @@ class MessagesPresenterTest {
|
||||||
)
|
)
|
||||||
val timelinePresenter = TimelinePresenter(
|
val timelinePresenter = TimelinePresenter(
|
||||||
timelineItemsFactory = aTimelineItemsFactory(),
|
timelineItemsFactory = aTimelineItemsFactory(),
|
||||||
|
timelineItemGrouper = TimelineItemGrouper(),
|
||||||
room = matrixRoom,
|
room = matrixRoom,
|
||||||
)
|
)
|
||||||
val actionListPresenter = ActionListPresenter()
|
val actionListPresenter = ActionListPresenter()
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,13 @@ 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.TimelineItemDaySeparatorFactory
|
||||||
import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory
|
import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory
|
||||||
import io.element.android.libraries.dateformatter.test.FakeDaySeparatorFormatter
|
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
|
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||||
|
|
||||||
internal fun aTimelineItemsFactory() = TimelineItemsFactory(
|
internal fun aTimelineItemsFactory(): TimelineItemsFactory {
|
||||||
|
val timelineEventFormatter = aTimelineEventFormatter()
|
||||||
|
return TimelineItemsFactory(
|
||||||
dispatchers = testCoroutineDispatchers(),
|
dispatchers = testCoroutineDispatchers(),
|
||||||
eventItemFactory = TimelineItemEventFactory(
|
eventItemFactory = TimelineItemEventFactory(
|
||||||
TimelineItemContentFactory(
|
TimelineItemContentFactory(
|
||||||
|
|
@ -41,9 +45,9 @@ internal fun aTimelineItemsFactory() = TimelineItemsFactory(
|
||||||
redactedMessageFactory = TimelineItemContentRedactedFactory(),
|
redactedMessageFactory = TimelineItemContentRedactedFactory(),
|
||||||
stickerFactory = TimelineItemContentStickerFactory(),
|
stickerFactory = TimelineItemContentStickerFactory(),
|
||||||
utdFactory = TimelineItemContentUTDFactory(),
|
utdFactory = TimelineItemContentUTDFactory(),
|
||||||
roomMembershipFactory = TimelineItemContentRoomMembershipFactory(),
|
roomMembershipFactory = TimelineItemContentRoomMembershipFactory(timelineEventFormatter),
|
||||||
profileChangeFactory = TimelineItemContentProfileChangeFactory(),
|
profileChangeFactory = TimelineItemContentProfileChangeFactory(timelineEventFormatter),
|
||||||
stateFactory = TimelineItemContentStateFactory(),
|
stateFactory = TimelineItemContentStateFactory(timelineEventFormatter),
|
||||||
failedToParseMessageFactory = TimelineItemContentFailedToParseMessageFactory(),
|
failedToParseMessageFactory = TimelineItemContentFailedToParseMessageFactory(),
|
||||||
failedToParseStateFactory = TimelineItemContentFailedToParseStateFactory()
|
failedToParseStateFactory = TimelineItemContentFailedToParseStateFactory()
|
||||||
)
|
)
|
||||||
|
|
@ -54,3 +58,12 @@ internal fun aTimelineItemsFactory() = TimelineItemsFactory(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun aTimelineEventFormatter(): TimelineEventFormatter {
|
||||||
|
return object : TimelineEventFormatter {
|
||||||
|
override fun format(event: EventTimelineItem): CharSequence {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
|
||||||
package io.element.android.features.messages.textcomposer
|
package io.element.android.features.messages.textcomposer
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionClock
|
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.element.android.libraries.textcomposer.MessageComposerMode
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.test.runCurrent
|
import kotlinx.coroutines.test.runCurrent
|
||||||
import kotlinx.coroutines.test.runTest
|
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.fixtures.aTimelineItemsFactory
|
||||||
import io.element.android.features.messages.impl.timeline.TimelineEvents
|
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.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.AN_EVENT_ID
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
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 kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
|
|
@ -33,6 +38,7 @@ class TimelinePresenterTest {
|
||||||
fun `present - initial state`() = runTest {
|
fun `present - initial state`() = runTest {
|
||||||
val presenter = TimelinePresenter(
|
val presenter = TimelinePresenter(
|
||||||
timelineItemsFactory = aTimelineItemsFactory(),
|
timelineItemsFactory = aTimelineItemsFactory(),
|
||||||
|
timelineItemGrouper = TimelineItemGrouper(),
|
||||||
room = FakeMatrixRoom(),
|
room = FakeMatrixRoom(),
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionClock.Immediate) {
|
moleculeFlow(RecompositionClock.Immediate) {
|
||||||
|
|
@ -49,6 +55,7 @@ class TimelinePresenterTest {
|
||||||
fun `present - load more`() = runTest {
|
fun `present - load more`() = runTest {
|
||||||
val presenter = TimelinePresenter(
|
val presenter = TimelinePresenter(
|
||||||
timelineItemsFactory = aTimelineItemsFactory(),
|
timelineItemsFactory = aTimelineItemsFactory(),
|
||||||
|
timelineItemGrouper = TimelineItemGrouper(),
|
||||||
room = FakeMatrixRoom(),
|
room = FakeMatrixRoom(),
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionClock.Immediate) {
|
moleculeFlow(RecompositionClock.Immediate) {
|
||||||
|
|
@ -71,6 +78,7 @@ class TimelinePresenterTest {
|
||||||
fun `present - set highlighted event`() = runTest {
|
fun `present - set highlighted event`() = runTest {
|
||||||
val presenter = TimelinePresenter(
|
val presenter = TimelinePresenter(
|
||||||
timelineItemsFactory = aTimelineItemsFactory(),
|
timelineItemsFactory = aTimelineItemsFactory(),
|
||||||
|
timelineItemGrouper = TimelineItemGrouper(),
|
||||||
room = FakeMatrixRoom(),
|
room = FakeMatrixRoom(),
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionClock.Immediate) {
|
moleculeFlow(RecompositionClock.Immediate) {
|
||||||
|
|
@ -87,4 +95,37 @@ class TimelinePresenterTest {
|
||||||
assertThat(withoutHighlightedState.highlightedEventId).isNull()
|
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="one">"1 Person"</item>
|
||||||
<item quantity="other">"%1$d Personen"</item>
|
<item quantity="other">"%1$d Personen"</item>
|
||||||
</plurals>
|
</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_room_details_share_room_title">"Raum teilen"</string>
|
||||||
<string name="screen_dm_details_block_alert_action">"Blockieren"</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_block_user">"Nutzer blockieren"</string>
|
||||||
<string name="screen_dm_details_unblock_alert_action">"Blockierung aufheben"</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_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_leave_room_title">"Raum verlassen"</string>
|
||||||
<string name="screen_room_details_security_title">"Sicherheit"</string>
|
<string name="screen_room_details_security_title">"Sicherheit"</string>
|
||||||
<string name="screen_room_details_topic_title">"Thema"</string>
|
<string name="screen_room_details_topic_title">"Thema"</string>
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ dependencies {
|
||||||
implementation(projects.libraries.testtags)
|
implementation(projects.libraries.testtags)
|
||||||
implementation(projects.libraries.uiStrings)
|
implementation(projects.libraries.uiStrings)
|
||||||
implementation(projects.libraries.dateformatter.api)
|
implementation(projects.libraries.dateformatter.api)
|
||||||
|
implementation(projects.libraries.eventformatter.api)
|
||||||
implementation(projects.features.invitelist.api)
|
implementation(projects.features.invitelist.api)
|
||||||
implementation(projects.features.networkmonitor.api)
|
implementation(projects.features.networkmonitor.api)
|
||||||
implementation(projects.features.leaveroom.api)
|
implementation(projects.features.leaveroom.api)
|
||||||
|
|
@ -63,6 +64,7 @@ dependencies {
|
||||||
testImplementation(libs.test.robolectric)
|
testImplementation(libs.test.robolectric)
|
||||||
testImplementation(projects.libraries.matrix.test)
|
testImplementation(projects.libraries.matrix.test)
|
||||||
testImplementation(projects.libraries.dateformatter.test)
|
testImplementation(projects.libraries.dateformatter.test)
|
||||||
|
testImplementation(projects.libraries.eventformatter.test)
|
||||||
testImplementation(projects.libraries.permissions.noop)
|
testImplementation(projects.libraries.permissions.noop)
|
||||||
testImplementation(projects.features.invitelist.test)
|
testImplementation(projects.features.invitelist.test)
|
||||||
testImplementation(projects.features.networkmonitor.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.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
|
||||||
import io.element.android.libraries.designsystem.utils.handleSnackbarMessage
|
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.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
|
@ -199,7 +200,7 @@ class RoomListPresenter @Inject constructor(
|
||||||
hasUnread = roomSummary.details.unreadNotificationCount > 0,
|
hasUnread = roomSummary.details.unreadNotificationCount > 0,
|
||||||
timestamp = lastMessageTimestampFormatter.format(roomSummary.details.lastMessageTimestamp),
|
timestamp = lastMessageTimestampFormatter.format(roomSummary.details.lastMessageTimestamp),
|
||||||
lastMessage = roomSummary.details.lastMessage?.let { message ->
|
lastMessage = roomSummary.details.lastMessage?.let { message ->
|
||||||
roomLastMessageFormatter.processMessageItem(message.event, roomSummary.details.isDirect)
|
roomLastMessageFormatter.format(message.event, roomSummary.details.isDirect)
|
||||||
}.orEmpty(),
|
}.orEmpty(),
|
||||||
avatarData = avatarData,
|
avatarData = avatarData,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
<string name="screen_roomlist_main_space_title">"Alle Chats"</string>
|
<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>
|
</resources>
|
||||||
|
|
@ -4,58 +4,4 @@
|
||||||
<string name="screen_roomlist_main_space_title">"Todos los chats"</string>
|
<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_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="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>
|
</resources>
|
||||||
|
|
@ -4,58 +4,4 @@
|
||||||
<string name="screen_roomlist_main_space_title">"Tutte le conversazioni"</string>
|
<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_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="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>
|
</resources>
|
||||||
|
|
@ -4,58 +4,4 @@
|
||||||
<string name="screen_roomlist_main_space_title">"Toate conversatiile"</string>
|
<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_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="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>
|
</resources>
|
||||||
|
|
@ -4,58 +4,4 @@
|
||||||
<string name="screen_roomlist_main_space_title">"All Chats"</string>
|
<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_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="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>
|
</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.dateformatter.test.FakeLastMessageTimestampFormatter
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||||
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
|
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.api.verification.SessionVerifiedStatus
|
||||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
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.
|
* 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
|
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||||
|
|
||||||
interface RoomLastMessageFormatter {
|
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.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.element.android.features.roomlist.impl
|
package io.element.android.libraries.eventformatter.impl
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
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.FakeMatrixClient
|
||||||
import io.element.android.libraries.matrix.test.room.aProfileChangeMessageContent
|
import io.element.android.libraries.matrix.test.room.aProfileChangeMessageContent
|
||||||
import io.element.android.libraries.matrix.test.room.anEventTimelineItem
|
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.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
@ -66,7 +67,14 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
fun setup() {
|
fun setup() {
|
||||||
context = RuntimeEnvironment.getApplication() as Context
|
context = RuntimeEnvironment.getApplication() as Context
|
||||||
fakeMatrixClient = FakeMatrixClient()
|
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
|
@Test
|
||||||
|
|
@ -76,7 +84,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
val senderName = "Someone"
|
val senderName = "Someone"
|
||||||
sequenceOf(false, true).forEach { isDm ->
|
sequenceOf(false, true).forEach { isDm ->
|
||||||
val message = createRoomEvent(false, senderName, RedactedContent)
|
val message = createRoomEvent(false, senderName, RedactedContent)
|
||||||
val result = formatter.processMessageItem(message, isDm)
|
val result = formatter.format(message, isDm)
|
||||||
if (isDm) {
|
if (isDm) {
|
||||||
Truth.assertThat(result).isEqualTo(expected)
|
Truth.assertThat(result).isEqualTo(expected)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -92,7 +100,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
val body = "body"
|
val body = "body"
|
||||||
val info = ImageInfo(null, null, null, null, null, null, null)
|
val info = ImageInfo(null, null, null, null, null, null, null)
|
||||||
val message = createRoomEvent(false, null, StickerContent(body, info, "url"))
|
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)
|
Truth.assertThat(result).isEqualTo(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,7 +111,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
val senderName = "Someone"
|
val senderName = "Someone"
|
||||||
sequenceOf(false, true).forEach { isDm ->
|
sequenceOf(false, true).forEach { isDm ->
|
||||||
val message = createRoomEvent(false, senderName, UnableToDecryptContent(UnableToDecryptContent.Data.Unknown))
|
val message = createRoomEvent(false, senderName, UnableToDecryptContent(UnableToDecryptContent.Data.Unknown))
|
||||||
val result = formatter.processMessageItem(message, isDm)
|
val result = formatter.format(message, isDm)
|
||||||
if (isDm) {
|
if (isDm) {
|
||||||
Truth.assertThat(result).isEqualTo(expected)
|
Truth.assertThat(result).isEqualTo(expected)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -125,7 +133,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
UnknownContent,
|
UnknownContent,
|
||||||
).forEach { type ->
|
).forEach { type ->
|
||||||
val message = createRoomEvent(false, senderName, type)
|
val message = createRoomEvent(false, senderName, type)
|
||||||
val result = formatter.processMessageItem(message, isDm)
|
val result = formatter.format(message, isDm)
|
||||||
if (isDm) {
|
if (isDm) {
|
||||||
Truth.assertWithMessage("$type was not properly handled").that(result).isEqualTo(expected)
|
Truth.assertWithMessage("$type was not properly handled").that(result).isEqualTo(expected)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -145,6 +153,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
fun createMessageContent(type: MessageType): MessageContent {
|
fun createMessageContent(type: MessageType): MessageContent {
|
||||||
return MessageContent(body, null, false, type)
|
return MessageContent(body, null, false, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
val sharedContentMessagesTypes = arrayOf(
|
val sharedContentMessagesTypes = arrayOf(
|
||||||
TextMessageType(body, null),
|
TextMessageType(body, null),
|
||||||
VideoMessageType(body, "url", null),
|
VideoMessageType(body, "url", null),
|
||||||
|
|
@ -163,7 +172,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
sharedContentMessagesTypes.forEach { type ->
|
sharedContentMessagesTypes.forEach { type ->
|
||||||
val content = createMessageContent(type)
|
val content = createMessageContent(type)
|
||||||
val message = createRoomEvent(sentByYou = false, senderDisplayName = "Someone", content = content)
|
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) {
|
if (isDm) {
|
||||||
resultsInDm.add(type to result)
|
resultsInDm.add(type to result)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -171,7 +180,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val unknownMessage = createRoomEvent(sentByYou = false, senderDisplayName = "Someone", content = createMessageContent(UnknownMessageType))
|
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) {
|
if (isDm) {
|
||||||
resultsInDm.add(result)
|
resultsInDm.add(result)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -235,11 +244,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.JOINED)
|
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.JOINED)
|
||||||
|
|
||||||
val youJoinedRoomEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
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")
|
Truth.assertThat(youJoinedRoom).isEqualTo("You joined the room")
|
||||||
|
|
||||||
val someoneJoinedRoomEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
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")
|
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 someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.LEFT)
|
||||||
|
|
||||||
val youLeftRoomEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
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")
|
Truth.assertThat(youLeftRoom).isEqualTo("You left the room")
|
||||||
|
|
||||||
val someoneLeftRoomEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
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")
|
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 someoneKickedContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.KICKED_AND_BANNED)
|
||||||
|
|
||||||
val youBannedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
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}")
|
Truth.assertThat(youBanned).isEqualTo("You banned ${youContent.userId}")
|
||||||
|
|
||||||
val youKickBannedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youKickedContent)
|
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}")
|
Truth.assertThat(youKickedBanned).isEqualTo("You banned ${youContent.userId}")
|
||||||
|
|
||||||
val someoneBannedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
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}")
|
Truth.assertThat(someoneBanned).isEqualTo("$otherName banned ${someoneContent.userId}")
|
||||||
|
|
||||||
val someoneKickBannedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneKickedContent)
|
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}")
|
Truth.assertThat(someoneKickBanned).isEqualTo("$otherName banned ${someoneContent.userId}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -293,11 +302,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.UNBANNED)
|
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.UNBANNED)
|
||||||
|
|
||||||
val youUnbannedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
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}")
|
Truth.assertThat(youUnbanned).isEqualTo("You unbanned ${youContent.userId}")
|
||||||
|
|
||||||
val someoneUnbannedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
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}")
|
Truth.assertThat(someoneUnbanned).isEqualTo("$otherName unbanned ${someoneContent.userId}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -309,11 +318,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.KICKED)
|
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.KICKED)
|
||||||
|
|
||||||
val youKickedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
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}")
|
Truth.assertThat(youKicked).isEqualTo("You removed ${youContent.userId}")
|
||||||
|
|
||||||
val someoneKickedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
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}")
|
Truth.assertThat(someoneKicked).isEqualTo("$otherName removed ${someoneContent.userId}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -325,15 +334,15 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.INVITED)
|
val someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.INVITED)
|
||||||
|
|
||||||
val youWereInvitedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = youContent)
|
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")
|
Truth.assertThat(youWereInvited).isEqualTo("$otherName invited you")
|
||||||
|
|
||||||
val youInvitedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = someoneContent)
|
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}")
|
Truth.assertThat(youInvited).isEqualTo("You invited ${someoneContent.userId}")
|
||||||
|
|
||||||
val someoneInvitedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
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}")
|
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 someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.INVITATION_ACCEPTED)
|
||||||
|
|
||||||
val youAcceptedInviteEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
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")
|
Truth.assertThat(youAcceptedInvite).isEqualTo("You accepted the invite")
|
||||||
|
|
||||||
val someoneAcceptedInviteEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
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")
|
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 someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.INVITATION_REJECTED)
|
||||||
|
|
||||||
val youRejectedInviteEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
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")
|
Truth.assertThat(youRejectedInvite).isEqualTo("You rejected the invitation")
|
||||||
|
|
||||||
val someoneRejectedInviteEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
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")
|
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 someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.INVITATION_REVOKED)
|
||||||
|
|
||||||
val youRevokedInviteEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = someoneContent)
|
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")
|
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 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")
|
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 someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.KNOCKED)
|
||||||
|
|
||||||
val youKnockedEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
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")
|
Truth.assertThat(youKnocked).isEqualTo("You requested to join")
|
||||||
|
|
||||||
val someoneKnockedEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
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")
|
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 someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.KNOCK_ACCEPTED)
|
||||||
|
|
||||||
val youAcceptedKnockEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = someoneContent)
|
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")
|
Truth.assertThat(youAcceptedKnock).isEqualTo("${someoneContent.userId} allowed you to join")
|
||||||
|
|
||||||
val someoneAcceptedKnockEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
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")
|
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 someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.KNOCK_RETRACTED)
|
||||||
|
|
||||||
val youRetractedKnockEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = youContent)
|
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")
|
Truth.assertThat(youRetractedKnock).isEqualTo("You cancelled your request to join")
|
||||||
|
|
||||||
val someoneRetractedKnockEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
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")
|
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 someoneContent = RoomMembershipContent(UserId("@someone_else:domain"), MembershipChange.KNOCK_DENIED)
|
||||||
|
|
||||||
val youDeniedKnockEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = someoneContent)
|
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")
|
Truth.assertThat(youDeniedKnock).isEqualTo("You rejected ${someoneContent.userId}'s request to join")
|
||||||
|
|
||||||
val someoneDeniedKnockEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = someoneContent)
|
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")
|
Truth.assertThat(someoneDeniedKnock).isEqualTo("$otherName rejected ${someoneContent.userId}'s request to join")
|
||||||
|
|
||||||
val someoneDeniedYourKnockEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = youContent)
|
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")
|
Truth.assertThat(someoneDeniedYourKnock).isEqualTo("$otherName rejected your request to join")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -459,7 +468,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
val results = otherChanges.map { change ->
|
val results = otherChanges.map { change ->
|
||||||
val content = RoomMembershipContent(A_USER_ID, change)
|
val content = RoomMembershipContent(A_USER_ID, change)
|
||||||
val event = createRoomEvent(sentByYou = false, senderDisplayName = "Someone", content = content)
|
val event = createRoomEvent(sentByYou = false, senderDisplayName = "Someone", content = content)
|
||||||
val result = formatter.processMessageItem(event, false)
|
val result = formatter.format(event, false)
|
||||||
change to result
|
change to result
|
||||||
}
|
}
|
||||||
val expected = otherChanges.map { it to null }
|
val expected = otherChanges.map { it to null }
|
||||||
|
|
@ -478,19 +487,19 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
val removedContent = StateContent("", OtherState.RoomAvatar(null))
|
val removedContent = StateContent("", OtherState.RoomAvatar(null))
|
||||||
|
|
||||||
val youChangedRoomAvatarEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent)
|
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")
|
Truth.assertThat(youChangedRoomAvatar).isEqualTo("You changed the room avatar")
|
||||||
|
|
||||||
val someoneChangedRoomAvatarEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent)
|
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")
|
Truth.assertThat(someoneChangedRoomAvatar).isEqualTo("$otherName changed the room avatar")
|
||||||
|
|
||||||
val youRemovedRoomAvatarEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent)
|
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")
|
Truth.assertThat(youRemovedRoomAvatar).isEqualTo("You removed the room avatar")
|
||||||
|
|
||||||
val someoneRemovedRoomAvatarEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent)
|
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")
|
Truth.assertThat(someoneRemovedRoomAvatar).isEqualTo("$otherName removed the room avatar")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -501,11 +510,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
val content = StateContent("", OtherState.RoomCreate)
|
val content = StateContent("", OtherState.RoomCreate)
|
||||||
|
|
||||||
val youCreatedRoomMessage = createRoomEvent(sentByYou = true, senderDisplayName = null, content = content)
|
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")
|
Truth.assertThat(youCreatedRoom).isEqualTo("You created the room")
|
||||||
|
|
||||||
val someoneCreatedRoomEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = content)
|
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")
|
Truth.assertThat(someoneCreatedRoom).isEqualTo("$otherName created the room")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -516,11 +525,11 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
val content = StateContent("", OtherState.RoomEncryption)
|
val content = StateContent("", OtherState.RoomEncryption)
|
||||||
|
|
||||||
val youCreatedRoomMessage = createRoomEvent(sentByYou = true, senderDisplayName = null, content = content)
|
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")
|
Truth.assertThat(youCreatedRoom).isEqualTo("Encryption enabled")
|
||||||
|
|
||||||
val someoneCreatedRoomEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = content)
|
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")
|
Truth.assertThat(someoneCreatedRoom).isEqualTo("Encryption enabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -533,19 +542,19 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
val removedContent = StateContent("", OtherState.RoomName(null))
|
val removedContent = StateContent("", OtherState.RoomName(null))
|
||||||
|
|
||||||
val youChangedRoomNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent)
|
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")
|
Truth.assertThat(youChangedRoomName).isEqualTo("You changed the room name to: $newName")
|
||||||
|
|
||||||
val someoneChangedRoomNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent)
|
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")
|
Truth.assertThat(someoneChangedRoomName).isEqualTo("$otherName changed the room name to: $newName")
|
||||||
|
|
||||||
val youRemovedRoomNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent)
|
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")
|
Truth.assertThat(youRemovedRoomName).isEqualTo("You removed the room name")
|
||||||
|
|
||||||
val someoneRemovedRoomNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent)
|
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")
|
Truth.assertThat(someoneRemovedRoomName).isEqualTo("$otherName removed the room name")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -558,19 +567,19 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
val removedContent = StateContent("", OtherState.RoomThirdPartyInvite(null))
|
val removedContent = StateContent("", OtherState.RoomThirdPartyInvite(null))
|
||||||
|
|
||||||
val youInvitedSomeoneEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent)
|
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")
|
Truth.assertThat(youInvitedSomeone).isEqualTo("You sent an invitation to $inviteeName to join the room")
|
||||||
|
|
||||||
val someoneInvitedSomeoneEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent)
|
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")
|
Truth.assertThat(someoneInvitedSomeone).isEqualTo("$otherName sent an invitation to $inviteeName to join the room")
|
||||||
|
|
||||||
val youInvitedNoOneEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent)
|
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()
|
Truth.assertThat(youInvitedNoOne).isNull()
|
||||||
|
|
||||||
val someoneInvitedNoOneEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent)
|
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()
|
Truth.assertThat(someoneInvitedNoOne).isNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -583,19 +592,19 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
val removedContent = StateContent("", OtherState.RoomTopic(null))
|
val removedContent = StateContent("", OtherState.RoomTopic(null))
|
||||||
|
|
||||||
val youChangedRoomTopicEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent)
|
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")
|
Truth.assertThat(youChangedRoomTopic).isEqualTo("You changed the topic to: $roomTopic")
|
||||||
|
|
||||||
val someoneChangedRoomTopicEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent)
|
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")
|
Truth.assertThat(someoneChangedRoomTopic).isEqualTo("$otherName changed the topic to: $roomTopic")
|
||||||
|
|
||||||
val youRemovedRoomTopicEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent)
|
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")
|
Truth.assertThat(youRemovedRoomTopic).isEqualTo("You removed the room topic")
|
||||||
|
|
||||||
val someoneRemovedRoomTopicEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent)
|
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")
|
Truth.assertThat(someoneRemovedRoomTopic).isEqualTo("$otherName removed the room topic")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -611,7 +620,7 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
val results = otherStates.map { state ->
|
val results = otherStates.map { state ->
|
||||||
val content = StateContent("", state)
|
val content = StateContent("", state)
|
||||||
val event = createRoomEvent(sentByYou = false, senderDisplayName = "Someone", content = content)
|
val event = createRoomEvent(sentByYou = false, senderDisplayName = "Someone", content = content)
|
||||||
val result = formatter.processMessageItem(event, false)
|
val result = formatter.format(event, false)
|
||||||
state to result
|
state to result
|
||||||
}
|
}
|
||||||
val expected = otherStates.map { it to null }
|
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 sameContent = aProfileChangeMessageContent(avatarUrl = "same_avatar_url", prevAvatarUrl = "same_avatar_url")
|
||||||
|
|
||||||
val youChangedAvatarEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent)
|
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")
|
Truth.assertThat(youChangedAvatar).isEqualTo("You changed your avatar")
|
||||||
|
|
||||||
val someoneChangeAvatarEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent)
|
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")
|
Truth.assertThat(someoneChangeAvatar).isEqualTo("$otherName changed their avatar")
|
||||||
|
|
||||||
val youSetAvatarEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = setContent)
|
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")
|
Truth.assertThat(youSetAvatar).isEqualTo("You changed your avatar")
|
||||||
|
|
||||||
val someoneSetAvatarEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = setContent)
|
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")
|
Truth.assertThat(someoneSetAvatar).isEqualTo("$otherName changed their avatar")
|
||||||
|
|
||||||
val youRemovedAvatarEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent)
|
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")
|
Truth.assertThat(youRemovedAvatar).isEqualTo("You changed your avatar")
|
||||||
|
|
||||||
val someoneRemovedAvatarEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent)
|
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")
|
Truth.assertThat(someoneRemovedAvatar).isEqualTo("$otherName changed their avatar")
|
||||||
|
|
||||||
val unchangedEvent = createRoomEvent(sentByYou = true, senderDisplayName = otherName, content = sameContent)
|
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()
|
Truth.assertThat(unchangedResult).isNull()
|
||||||
|
|
||||||
val invalidEvent = createRoomEvent(sentByYou = true, senderDisplayName = otherName, content = invalidContent)
|
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()
|
Truth.assertThat(invalidResult).isNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -678,35 +687,35 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
val invalidContent = aProfileChangeMessageContent(displayName = null, prevDisplayName = null)
|
val invalidContent = aProfileChangeMessageContent(displayName = null, prevDisplayName = null)
|
||||||
|
|
||||||
val youChangedDisplayNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent)
|
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")
|
Truth.assertThat(youChangedDisplayName).isEqualTo("You changed your display name from $oldDisplayName to $newDisplayName")
|
||||||
|
|
||||||
val someoneChangedDisplayNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = changedContent)
|
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")
|
Truth.assertThat(someoneChangedDisplayName).isEqualTo("$otherName changed their display name from $oldDisplayName to $newDisplayName")
|
||||||
|
|
||||||
val youSetDisplayNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = setContent)
|
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")
|
Truth.assertThat(youSetDisplayName).isEqualTo("You set your display name to $newDisplayName")
|
||||||
|
|
||||||
val someoneSetDisplayNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = setContent)
|
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")
|
Truth.assertThat(someoneSetDisplayName).isEqualTo("$otherName set their display name to $newDisplayName")
|
||||||
|
|
||||||
val youRemovedDisplayNameEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = removedContent)
|
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)")
|
Truth.assertThat(youRemovedDisplayName).isEqualTo("You removed your display name (it was $oldDisplayName)")
|
||||||
|
|
||||||
val someoneRemovedDisplayNameEvent = createRoomEvent(sentByYou = false, senderDisplayName = otherName, content = removedContent)
|
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)")
|
Truth.assertThat(someoneRemovedDisplayName).isEqualTo("$otherName removed their display name (it was $oldDisplayName)")
|
||||||
|
|
||||||
val unchangedEvent = createRoomEvent(sentByYou = true, senderDisplayName = otherName, content = sameContent)
|
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()
|
Truth.assertThat(unchangedResult).isNull()
|
||||||
|
|
||||||
val invalidEvent = createRoomEvent(sentByYou = true, senderDisplayName = otherName, content = invalidContent)
|
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()
|
Truth.assertThat(invalidResult).isNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -735,15 +744,15 @@ class DefaultRoomLastMessageFormatterTests {
|
||||||
)
|
)
|
||||||
|
|
||||||
val youChangedBothEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = changedContent)
|
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)")
|
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 invalidContentEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = invalidContent)
|
||||||
val invalidMessage = formatter.processMessageItem(invalidContentEvent, false)
|
val invalidMessage = formatter.format(invalidContentEvent, false)
|
||||||
Truth.assertThat(invalidMessage).isNull()
|
Truth.assertThat(invalidMessage).isNull()
|
||||||
|
|
||||||
val sameContentEvent = createRoomEvent(sentByYou = true, senderDisplayName = null, content = sameContent)
|
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()
|
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.
|
* 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
|
import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem
|
||||||
|
|
||||||
class FakeRoomLastMessageFormatter : RoomLastMessageFormatter {
|
class FakeRoomLastMessageFormatter : RoomLastMessageFormatter {
|
||||||
|
|
||||||
private var processMessageItemResult: CharSequence? = null
|
private var result: CharSequence? = null
|
||||||
override fun processMessageItem(event: EventTimelineItem, isDmRoom: Boolean): CharSequence? {
|
|
||||||
return processMessageItemResult
|
override fun format(event: EventTimelineItem, isDmRoom: Boolean): CharSequence? {
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun givenRoomSummaryResult(result: CharSequence?) {
|
fun givenFormatResult(result: CharSequence?) {
|
||||||
processMessageItemResult = result
|
this.result = result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,11 +8,16 @@
|
||||||
<string name="notification_ticker_text_dm">"%1$s: %2$s"</string>
|
<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_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_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>
|
<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">
|
<plurals name="notification_compat_summary_line_for_room">
|
||||||
<item quantity="one">"%1$s: %2$d Nachricht"</item>
|
<item quantity="one">"%1$s: %2$d Nachricht"</item>
|
||||||
<item quantity="other">"%1$s: %2$d Nachrichten"</item>
|
<item quantity="other">"%1$s: %2$d Nachrichten"</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
<plurals name="notification_compat_summary_title">
|
||||||
|
<item quantity="one">"%d Mitteilung"</item>
|
||||||
|
<item quantity="other">"%d Mitteilungen"</item>
|
||||||
|
</plurals>
|
||||||
<plurals name="notification_invitations">
|
<plurals name="notification_invitations">
|
||||||
<item quantity="one">"%d Einladung"</item>
|
<item quantity="one">"%d Einladung"</item>
|
||||||
<item quantity="other">"%d Einladungen"</item>
|
<item quantity="other">"%d Einladungen"</item>
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
<string name="action_leave_room">"Raum verlassen"</string>
|
<string name="action_leave_room">"Raum verlassen"</string>
|
||||||
<string name="action_next">"Weiter"</string>
|
<string name="action_next">"Weiter"</string>
|
||||||
<string name="action_no">"Nein"</string>
|
<string name="action_no">"Nein"</string>
|
||||||
|
<string name="action_not_now">"Nicht jetzt"</string>
|
||||||
<string name="action_ok">"OK"</string>
|
<string name="action_ok">"OK"</string>
|
||||||
<string name="action_quick_reply">"Schnellantwort"</string>
|
<string name="action_quick_reply">"Schnellantwort"</string>
|
||||||
<string name="action_quote">"Zitieren"</string>
|
<string name="action_quote">"Zitieren"</string>
|
||||||
|
|
@ -41,10 +42,11 @@
|
||||||
<string name="action_share">"Teilen"</string>
|
<string name="action_share">"Teilen"</string>
|
||||||
<string name="action_share_link">"Link teilen"</string>
|
<string name="action_share_link">"Link teilen"</string>
|
||||||
<string name="action_skip">"Überspringen"</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_take_photo">"Foto aufnehmen"</string>
|
||||||
<string name="action_yes">"Ja"</string>
|
<string name="action_yes">"Ja"</string>
|
||||||
<string name="common_about">"Über"</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_audio">"Audio"</string>
|
||||||
<string name="common_bubbles">"Blasen"</string>
|
<string name="common_bubbles">"Blasen"</string>
|
||||||
<string name="common_decryption_error">"Entschlüsselungsfehler"</string>
|
<string name="common_decryption_error">"Entschlüsselungsfehler"</string>
|
||||||
|
|
@ -61,8 +63,12 @@
|
||||||
<string name="common_offline">"Offline"</string>
|
<string name="common_offline">"Offline"</string>
|
||||||
<string name="common_password">"Passwort"</string>
|
<string name="common_password">"Passwort"</string>
|
||||||
<string name="common_reactions">"Reaktionen"</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_security">"Sicherheit"</string>
|
||||||
|
<string name="common_server_not_supported">"Server wird nicht unterstützt"</string>
|
||||||
<string name="common_settings">"Einstellungen"</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_sticker">"Sticker"</string>
|
||||||
<string name="common_success">"Erfolg"</string>
|
<string name="common_success">"Erfolg"</string>
|
||||||
<string name="common_suggestions">"Vorschläge"</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_places">"Reisen & Orte"</string>
|
||||||
<string name="emoji_picker_category_symbols">"Symbole"</string>
|
<string name="emoji_picker_category_symbols">"Symbole"</string>
|
||||||
<string name="error_failed_loading_messages">"Fehler beim Laden der Nachrichten"</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="error_unknown">"Entschuldigung, ein Fehler ist aufgetreten."</string>
|
||||||
<string name="login_initial_device_name_android">"%1$s Android"</string>
|
<string name="login_initial_device_name_android">"%1$s Android"</string>
|
||||||
<plurals name="common_member_count">
|
<plurals name="common_member_count">
|
||||||
|
|
@ -93,6 +100,9 @@
|
||||||
<string name="report_content_hint">"Grund für die Meldung dieses Inhalts"</string>
|
<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_beginning_of_room">"Dies ist der Anfang von %1$s."</string>
|
||||||
<string name="room_timeline_read_marker_title">"Neu"</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_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="screen_media_picker_error_failed_selection">"Medienauswahl fehlgeschlagen, bitte versuche es erneut."</string>
|
||||||
<string name="settings_rageshake_detection_threshold">"Erkennungsschwelle"</string>
|
<string name="settings_rageshake_detection_threshold">"Erkennungsschwelle"</string>
|
||||||
|
|
|
||||||
|
|
@ -116,10 +116,6 @@
|
||||||
<item quantity="one">"%1$d miembro"</item>
|
<item quantity="one">"%1$d miembro"</item>
|
||||||
<item quantity="other">"%1$d miembros"</item>
|
<item quantity="other">"%1$d miembros"</item>
|
||||||
</plurals>
|
</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="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="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>
|
<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="one">"%1$d membro"</item>
|
||||||
<item quantity="other">"%1$d membri"</item>
|
<item quantity="other">"%1$d membri"</item>
|
||||||
</plurals>
|
</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="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="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>
|
<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="few">"%1$d membri"</item>
|
||||||
<item quantity="other">"%1$d membri"</item>
|
<item quantity="other">"%1$d membri"</item>
|
||||||
</plurals>
|
</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="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="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>
|
<string name="report_content_explanation">"Acest mesaj va fi raportat administratorilor homeserver-ului tau. Ei nu vor putea citi niciun mesaj criptat."</string>
|
||||||
|
|
|
||||||
|
|
@ -132,10 +132,6 @@
|
||||||
<item quantity="one">"%1$d member"</item>
|
<item quantity="one">"%1$d member"</item>
|
||||||
<item quantity="other">"%1$d members"</item>
|
<item quantity="other">"%1$d members"</item>
|
||||||
</plurals>
|
</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="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="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>
|
<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>
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ fun DependencyHandlerScope.allLibrariesImpl() {
|
||||||
implementation(project(":libraries:matrixui"))
|
implementation(project(":libraries:matrixui"))
|
||||||
implementation(project(":libraries:network"))
|
implementation(project(":libraries:network"))
|
||||||
implementation(project(":libraries:core"))
|
implementation(project(":libraries:core"))
|
||||||
|
implementation(project(":libraries:eventformatter:impl"))
|
||||||
implementation(project(":libraries:permissions:impl"))
|
implementation(project(":libraries:permissions:impl"))
|
||||||
implementation(project(":libraries:push:impl"))
|
implementation(project(":libraries:push:impl"))
|
||||||
implementation(project(":libraries:push:impl"))
|
implementation(project(":libraries:push:impl"))
|
||||||
|
|
|
||||||
|
|
@ -55,11 +55,13 @@ dependencies {
|
||||||
implementation(projects.libraries.architecture)
|
implementation(projects.libraries.architecture)
|
||||||
implementation(projects.libraries.core)
|
implementation(projects.libraries.core)
|
||||||
implementation(projects.libraries.dateformatter.impl)
|
implementation(projects.libraries.dateformatter.impl)
|
||||||
|
implementation(projects.libraries.eventformatter.impl)
|
||||||
implementation(projects.features.invitelist.impl)
|
implementation(projects.features.invitelist.impl)
|
||||||
implementation(projects.features.roomlist.impl)
|
implementation(projects.features.roomlist.impl)
|
||||||
implementation(projects.features.leaveroom.impl)
|
implementation(projects.features.leaveroom.impl)
|
||||||
implementation(projects.features.login.impl)
|
implementation(projects.features.login.impl)
|
||||||
implementation(projects.features.networkmonitor.impl)
|
implementation(projects.features.networkmonitor.impl)
|
||||||
|
implementation(projects.services.toolbox.impl)
|
||||||
implementation(libs.coroutines.core)
|
implementation(libs.coroutines.core)
|
||||||
coreLibraryDesugaring(libs.android.desugar)
|
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.leaveroom.impl.LeaveRoomPresenterImpl
|
||||||
import io.element.android.features.networkmonitor.impl.NetworkMonitorImpl
|
import io.element.android.features.networkmonitor.impl.NetworkMonitorImpl
|
||||||
import io.element.android.features.roomlist.impl.DefaultInviteStateDataSource
|
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.RoomListPresenter
|
||||||
import io.element.android.features.roomlist.impl.RoomListView
|
import io.element.android.features.roomlist.impl.RoomListView
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
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.DefaultLastMessageTimestampFormatter
|
||||||
import io.element.android.libraries.dateformatter.impl.LocalDateTimeProvider
|
import io.element.android.libraries.dateformatter.impl.LocalDateTimeProvider
|
||||||
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
|
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.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
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.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.datetime.Clock
|
import kotlinx.datetime.Clock
|
||||||
|
|
@ -52,10 +56,17 @@ class RoomListScreen(
|
||||||
private val dateTimeProvider = LocalDateTimeProvider(clock, timeZone)
|
private val dateTimeProvider = LocalDateTimeProvider(clock, timeZone)
|
||||||
private val dateFormatters = DateFormatters(locale, clock, timeZone)
|
private val dateFormatters = DateFormatters(locale, clock, timeZone)
|
||||||
private val sessionVerificationService = matrixClient.sessionVerificationService()
|
private val sessionVerificationService = matrixClient.sessionVerificationService()
|
||||||
|
private val stringProvider = AndroidStringProvider(context.resources)
|
||||||
private val presenter = RoomListPresenter(
|
private val presenter = RoomListPresenter(
|
||||||
client = matrixClient,
|
client = matrixClient,
|
||||||
lastMessageTimestampFormatter = DefaultLastMessageTimestampFormatter(dateTimeProvider, dateFormatters),
|
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,
|
sessionVerificationService = sessionVerificationService,
|
||||||
networkMonitor = NetworkMonitorImpl(context),
|
networkMonitor = NetworkMonitorImpl(context),
|
||||||
snackbarDispatcher = SnackbarDispatcher(),
|
snackbarDispatcher = SnackbarDispatcher(),
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:b7787897d8dda469f17da7151260f26b6dcc5500f93ce44fb317af42ae8d457c
|
oid sha256:cbfbfa08085539bbfa4f20bc5d149bf0db0036169a32747c96f956d0d0f4b42d
|
||||||
size 93739
|
size 93741
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:6691512398d2c7e319d15397120054f1c2aae452ffed0be47df72ecb906ac4fa
|
oid sha256:9e0ba60e784a0dc582826a3f5679a6015531dc9aca22617ec1e0dede38b97f30
|
||||||
size 93675
|
size 93678
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:44313efc4d87857b9eb0bb05c8d24330b17353fd95344bcce06c1ad1bba8df81
|
oid sha256:766b2d44b51a36d1b15d8a39434ce5237f8427d1150f9b4c1255043fc1e4bb79
|
||||||
size 492167
|
size 492165
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:44313efc4d87857b9eb0bb05c8d24330b17353fd95344bcce06c1ad1bba8df81
|
oid sha256:766b2d44b51a36d1b15d8a39434ce5237f8427d1150f9b4c1255043fc1e4bb79
|
||||||
size 492167
|
size 492165
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:8a94a376c6b7f7db5e901e23cd9ba4858bdd47fa330161b4d30daa977efda048
|
||||||
|
size 3078
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:977ad7144c5d2cb5d6eec5bcfbedf81bc39a83e9fcd0243aab06252d9090640b
|
||||||
|
size 3094
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:a1846b6a8267752a502ded5cfba052569c929fbb7b53748885b6aba43638b745
|
||||||
|
size 54262
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:6c90a40ad84d4ee108ddbd94fb7dfb9de0203f792c51409565b146f26fcf3763
|
||||||
|
size 53225
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:0d7d5bc725daafd716308b8f5415a8357608941992c1f1133300aaccb81969ac
|
||||||
|
size 1582
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:dae0c56765a6004b43f904e652b1605294eb688c7b3d72af4385f2c58a8234eb
|
||||||
|
size 1651
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:523d6607d70a331d2ec32aeabf033d59c4dc60e0e7373982893df0245ae924ce
|
oid sha256:a3443f60b8ceec7eccfee92c1bca8cad151e25cff8e7845459a5f4c3d50e5e08
|
||||||
size 3332
|
size 3330
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:266490a1bdce29ac65d875543deda072d7f2a9aa2e216efb0d54b93c2a11aa5f
|
oid sha256:d0b0cdb7e39d5ca5ce874477da4397ebed00ecf9f5d8980f9f3b03ab72259d9f
|
||||||
size 4001
|
size 4000
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:523d6607d70a331d2ec32aeabf033d59c4dc60e0e7373982893df0245ae924ce
|
oid sha256:a3443f60b8ceec7eccfee92c1bca8cad151e25cff8e7845459a5f4c3d50e5e08
|
||||||
size 3332
|
size 3330
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:266490a1bdce29ac65d875543deda072d7f2a9aa2e216efb0d54b93c2a11aa5f
|
oid sha256:d0b0cdb7e39d5ca5ce874477da4397ebed00ecf9f5d8980f9f3b03ab72259d9f
|
||||||
size 4001
|
size 4000
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e94515b43bc4b8427edc60456ec657ec38b33539a034a2c45c92624a6b75d4e8
|
oid sha256:d35eb1aac0d413c216dc77f45f655be97ae9d36c3cade57ee8f70e25c44fafd1
|
||||||
size 3138
|
size 3142
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a4dc928a04bfc6331673be2fa0bf7f12e384dfd8ff91b07dc57b220c8dbbdd71
|
oid sha256:782a439312e4b616a6dd3020d8c99d04042208b2ebc416fab73c95344adeba67
|
||||||
size 3829
|
size 3834
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e94515b43bc4b8427edc60456ec657ec38b33539a034a2c45c92624a6b75d4e8
|
oid sha256:d35eb1aac0d413c216dc77f45f655be97ae9d36c3cade57ee8f70e25c44fafd1
|
||||||
size 3138
|
size 3142
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a4dc928a04bfc6331673be2fa0bf7f12e384dfd8ff91b07dc57b220c8dbbdd71
|
oid sha256:782a439312e4b616a6dd3020d8c99d04042208b2ebc416fab73c95344adeba67
|
||||||
size 3829
|
size 3834
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:25d0334932490997d432115c550c42e74e7226399ef82031430bf86ba33972bd
|
oid sha256:6cab21ee595c96700e4577554f9c98fdc56a59bf5093aa9d81a54d10381ba122
|
||||||
size 6066
|
size 6065
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a2d15387248308e5234c84b9b2b282e736f05f0495c80807bacfb0acff31eb2b
|
oid sha256:b9235621126fe90a51d557df616878d53120dada814830fba006b54f1556e69e
|
||||||
size 5890
|
size 5895
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:5315b975f8b9def0b0d7575160cb34300511f1b83845ee2d1762188481cf6fe9
|
oid sha256:b625f841a45111d58d76fd290cfad376a58e16265cc0b0d8e00318ffa1e3f48e
|
||||||
size 30927
|
size 35204
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:099401d03621aca301d3c4590d90ba4e4642d400d7603a8e15140a1bfef0c0ec
|
oid sha256:a5391a08d36198d28cc2c05c83757d30dc207dd56ada9684cd3a3ea7f9812b0a
|
||||||
size 42758
|
size 46977
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ca5dbc3aa00d6116de38fc0217b20ab0db4096b1f76bcc18b43ab9fd343f8110
|
oid sha256:6f951f69dd21d36338ddbfb303d50c554ef915c40e57919f699d83afce507d75
|
||||||
size 32722
|
size 36906
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:6211ec07d10d48e4713206e4c1fe24eae890fe2310d5bcc1f4ca54cc57f84ec9
|
oid sha256:4dec759835366e66beae953d11e73a52717500b30d5c58df6e3912a3cc60f916
|
||||||
size 44564
|
size 48704
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:8ac7985e70782a84fc351cb15536bfd700ca8d6d48d5da0ee9dcaf5c78226479
|
oid sha256:3bebe664eb8cd7a0578977ad998ddfe24283a309a6830cc420416c99b00f1e60
|
||||||
size 28909
|
size 33114
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c99138940ed1a8beac7da3c673600016a03b87195e07f205d5d389ef05be0fb0
|
oid sha256:9ed02fce151bfb9eec7b380e9469b67c8ce9757eb7a0b7ebbcf0a5932c55087e
|
||||||
size 45978
|
size 50072
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:2f24112ad89e5ef93917d45e3232043168d5092ce6d62a7d414773dd0afda963
|
oid sha256:73de3dd7281ea437f3cb364cca175cec5379046da19fd8f1152f3a52f43b6bfa
|
||||||
size 30326
|
size 34442
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue