Skip to content

Commit 7d985d4

Browse files
authored
Merge pull request #1549 from vector-im/feature/bma/unknownMsgtype
Render unknown msgtype
2 parents 519f5b2 + 71b836a commit 7d985d4

File tree

11 files changed

+260
-148
lines changed

11 files changed

+260
-148
lines changed

changelog.d/1539.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Render body of unknown msgtype in the timeline and in the room list

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
2525
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent
2626
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemNoticeContent
2727
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent
28-
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent
2928
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
3029
import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractor
3130
import io.element.android.features.messages.impl.timeline.util.toHtmlDocument
@@ -49,7 +48,7 @@ class TimelineItemContentMessageFactory @Inject constructor(
4948
) {
5049

5150
fun create(content: MessageContent, senderDisplayName: String): TimelineItemEventContent {
52-
return when (val messageType = content.type ?: UnknownMessageType) {
51+
return when (val messageType = content.type) {
5352
is EmoteMessageType -> TimelineItemEmoteContent(
5453
body = "* $senderDisplayName ${messageType.body}",
5554
htmlDocument = messageType.formatted?.toHtmlDocument(prefix = "* senderDisplayName"),
@@ -131,7 +130,12 @@ class TimelineItemContentMessageFactory @Inject constructor(
131130
htmlDocument = messageType.formatted?.toHtmlDocument(),
132131
isEdited = content.isEdited,
133132
)
134-
UnknownMessageType -> TimelineItemUnknownContent
133+
UnknownMessageType -> TimelineItemTextContent(
134+
// Display the body as a fallback
135+
body = content.body,
136+
htmlDocument = null,
137+
isEdited = content.isEdited,
138+
)
135139
}
136140
}
137141

libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
106106
}
107107

108108
private fun processMessageContents(messageContent: MessageContent, senderDisplayName: String, isDmRoom: Boolean): CharSequence? {
109-
val messageType: MessageType = messageContent.type ?: return null
110-
111-
val internalMessage = when (messageType) {
109+
val internalMessage = when (val messageType: MessageType = messageContent.type) {
112110
// Doesn't need a prefix
113111
is EmoteMessageType -> {
114112
return "* $senderDisplayName ${messageType.body}"
@@ -132,7 +130,8 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
132130
sp.getString(CommonStrings.common_audio)
133131
}
134132
UnknownMessageType -> {
135-
sp.getString(CommonStrings.common_unsupported_event)
133+
// Display the body as a fallback
134+
messageContent.body
136135
}
137136
is NoticeMessageType -> {
138137
messageType.body

libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTests.kt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,11 @@ class DefaultRoomLastMessageFormatterTests {
202202
is FileMessageType -> "File"
203203
is LocationMessageType -> "Shared location"
204204
is EmoteMessageType -> "* $senderName ${type.body}"
205-
is TextMessageType, is NoticeMessageType -> body
206-
UnknownMessageType -> "Unsupported event"
205+
is TextMessageType,
206+
is NoticeMessageType,
207+
UnknownMessageType -> body
207208
}
208-
Truth.assertWithMessage("$type was not properly handled").that(result).isEqualTo(expectedResult)
209+
Truth.assertWithMessage("$type was not properly handled for DM").that(result).isEqualTo(expectedResult)
209210
}
210211

211212
// Verify results of Room mode
@@ -217,9 +218,10 @@ class DefaultRoomLastMessageFormatterTests {
217218
is ImageMessageType -> "$senderName: Image"
218219
is FileMessageType -> "$senderName: File"
219220
is LocationMessageType -> "$senderName: Shared location"
221+
is TextMessageType,
222+
is NoticeMessageType,
223+
UnknownMessageType -> "$senderName: $body"
220224
is EmoteMessageType -> "* $senderName ${type.body}"
221-
is TextMessageType, is NoticeMessageType -> "$senderName: $body"
222-
UnknownMessageType -> "$senderName: Unsupported event"
223225
}
224226
val shouldCreateAnnotatedString = when (type) {
225227
is VideoMessageType -> true
@@ -236,7 +238,7 @@ class DefaultRoomLastMessageFormatterTests {
236238
.that(result)
237239
.isInstanceOf(AnnotatedString::class.java)
238240
}
239-
Truth.assertWithMessage("$type was not properly handled").that(string).isEqualTo(expectedResult)
241+
Truth.assertWithMessage("$type was not properly handled for room").that(string).isEqualTo(expectedResult)
240242
}
241243
}
242244

libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt

Lines changed: 1 addition & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,8 @@
1616

1717
package io.element.android.libraries.matrix.api.timeline.item.event
1818

19-
import io.element.android.libraries.matrix.api.core.EventId
2019
import io.element.android.libraries.matrix.api.core.UserId
21-
import io.element.android.libraries.matrix.api.media.AudioInfo
22-
import io.element.android.libraries.matrix.api.media.FileInfo
2320
import io.element.android.libraries.matrix.api.media.ImageInfo
24-
import io.element.android.libraries.matrix.api.media.MediaSource
25-
import io.element.android.libraries.matrix.api.media.VideoInfo
2621
import io.element.android.libraries.matrix.api.poll.PollAnswer
2722
import io.element.android.libraries.matrix.api.poll.PollKind
2823

@@ -33,36 +28,9 @@ data class MessageContent(
3328
val inReplyTo: InReplyTo?,
3429
val isEdited: Boolean,
3530
val isThreaded: Boolean,
36-
val type: MessageType?
31+
val type: MessageType
3732
) : EventContent
3833

39-
sealed interface InReplyTo {
40-
/** The event details are not loaded yet. We can fetch them. */
41-
data class NotLoaded(val eventId: EventId) : InReplyTo
42-
43-
/** The event details are pending to be fetched. We should **not** fetch them again. */
44-
data object Pending : InReplyTo
45-
46-
/** The event details are available. */
47-
data class Ready(
48-
val eventId: EventId,
49-
val content: EventContent,
50-
val senderId: UserId,
51-
val senderDisplayName: String?,
52-
val senderAvatarUrl: String?,
53-
) : InReplyTo
54-
55-
/**
56-
* Fetching the event details failed.
57-
*
58-
* We can try to fetch them again **with a proper retry strategy**, but not blindly:
59-
*
60-
* If the reason for the failure is consistent on the server, we'd enter a loop
61-
* where we keep trying to fetch the same event.
62-
* */
63-
data object Error : InReplyTo
64-
}
65-
6634
data object RedactedContent : EventContent
6735

6836
data class StickerContent(
@@ -125,105 +93,3 @@ data class FailedToParseStateContent(
12593
) : EventContent
12694

12795
data object UnknownContent : EventContent
128-
129-
sealed interface MessageType
130-
131-
data object UnknownMessageType : MessageType
132-
133-
enum class MessageFormat {
134-
HTML, UNKNOWN
135-
}
136-
137-
data class FormattedBody(
138-
val format: MessageFormat,
139-
val body: String
140-
)
141-
142-
data class EmoteMessageType(
143-
val body: String,
144-
val formatted: FormattedBody?
145-
) : MessageType
146-
147-
data class ImageMessageType(
148-
val body: String,
149-
val source: MediaSource,
150-
val info: ImageInfo?
151-
) : MessageType
152-
153-
data class LocationMessageType(
154-
val body: String,
155-
val geoUri: String,
156-
val description: String?,
157-
) : MessageType
158-
159-
data class AudioMessageType(
160-
val body: String,
161-
val source: MediaSource,
162-
val info: AudioInfo?
163-
) : MessageType
164-
165-
data class VideoMessageType(
166-
val body: String,
167-
val source: MediaSource,
168-
val info: VideoInfo?
169-
) : MessageType
170-
171-
data class FileMessageType(
172-
val body: String,
173-
val source: MediaSource,
174-
val info: FileInfo?
175-
) : MessageType
176-
177-
data class NoticeMessageType(
178-
val body: String,
179-
val formatted: FormattedBody?
180-
) : MessageType
181-
182-
data class TextMessageType(
183-
val body: String,
184-
val formatted: FormattedBody?
185-
) : MessageType
186-
187-
enum class MembershipChange {
188-
NONE,
189-
ERROR,
190-
JOINED,
191-
LEFT,
192-
BANNED,
193-
UNBANNED,
194-
KICKED,
195-
INVITED,
196-
KICKED_AND_BANNED,
197-
INVITATION_ACCEPTED,
198-
INVITATION_REJECTED,
199-
INVITATION_REVOKED,
200-
KNOCKED,
201-
KNOCK_ACCEPTED,
202-
KNOCK_RETRACTED,
203-
KNOCK_DENIED,
204-
NOT_IMPLEMENTED;
205-
}
206-
207-
sealed interface OtherState {
208-
data object PolicyRuleRoom : OtherState
209-
data object PolicyRuleServer : OtherState
210-
data object PolicyRuleUser : OtherState
211-
data object RoomAliases : OtherState
212-
data class RoomAvatar(val url: String?) : OtherState
213-
data object RoomCanonicalAlias : OtherState
214-
data object RoomCreate : OtherState
215-
data object RoomEncryption : OtherState
216-
data object RoomGuestAccess : OtherState
217-
data object RoomHistoryVisibility : OtherState
218-
data object RoomJoinRules : OtherState
219-
data class RoomName(val name: String?) : OtherState
220-
data object RoomPinnedEvents : OtherState
221-
data object RoomPowerLevels : OtherState
222-
data object RoomServerAcl : OtherState
223-
data class RoomThirdPartyInvite(val displayName: String?) : OtherState
224-
data object RoomTombstone : OtherState
225-
data class RoomTopic(val topic: String?) : OtherState
226-
data object SpaceChild : OtherState
227-
data object SpaceParent : OtherState
228-
data class Custom(val eventType: String) : OtherState
229-
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright (c) 2023 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.libraries.matrix.api.timeline.item.event
18+
19+
data class FormattedBody(
20+
val format: MessageFormat,
21+
val body: String
22+
)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (c) 2023 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.libraries.matrix.api.timeline.item.event
18+
19+
import io.element.android.libraries.matrix.api.core.EventId
20+
import io.element.android.libraries.matrix.api.core.UserId
21+
22+
sealed interface InReplyTo {
23+
/** The event details are not loaded yet. We can fetch them. */
24+
data class NotLoaded(val eventId: EventId) : InReplyTo
25+
26+
/** The event details are pending to be fetched. We should **not** fetch them again. */
27+
data object Pending : InReplyTo
28+
29+
/** The event details are available. */
30+
data class Ready(
31+
val eventId: EventId,
32+
val content: EventContent,
33+
val senderId: UserId,
34+
val senderDisplayName: String?,
35+
val senderAvatarUrl: String?,
36+
) : InReplyTo
37+
38+
/**
39+
* Fetching the event details failed.
40+
*
41+
* We can try to fetch them again **with a proper retry strategy**, but not blindly:
42+
*
43+
* If the reason for the failure is consistent on the server, we'd enter a loop
44+
* where we keep trying to fetch the same event.
45+
* */
46+
data object Error : InReplyTo
47+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2023 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.libraries.matrix.api.timeline.item.event
18+
19+
enum class MembershipChange {
20+
NONE,
21+
ERROR,
22+
JOINED,
23+
LEFT,
24+
BANNED,
25+
UNBANNED,
26+
KICKED,
27+
INVITED,
28+
KICKED_AND_BANNED,
29+
INVITATION_ACCEPTED,
30+
INVITATION_REJECTED,
31+
INVITATION_REVOKED,
32+
KNOCKED,
33+
KNOCK_ACCEPTED,
34+
KNOCK_RETRACTED,
35+
KNOCK_DENIED,
36+
NOT_IMPLEMENTED;
37+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright (c) 2023 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.libraries.matrix.api.timeline.item.event
18+
19+
enum class MessageFormat {
20+
HTML, UNKNOWN
21+
}

0 commit comments

Comments
 (0)