Pinned events : introduce banner formatter
This commit is contained in:
parent
4ac288aa3c
commit
e93772b4d8
4 changed files with 182 additions and 14 deletions
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 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
|
||||||
|
*
|
||||||
|
* https://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 PinnedMessagesBannerFormatter {
|
||||||
|
fun format(event: EventTimelineItem): CharSequence
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 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
|
||||||
|
*
|
||||||
|
* https://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.annotation.StringRes
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
import io.element.android.libraries.eventformatter.api.PinnedMessagesBannerFormatter
|
||||||
|
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
||||||
|
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.FileMessageType
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType
|
||||||
|
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.OtherMessageType
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.PollContent
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.StickerMessageType
|
||||||
|
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.VideoMessageType
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType
|
||||||
|
import io.element.android.libraries.matrix.api.timeline.item.event.getDisambiguatedDisplayName
|
||||||
|
import io.element.android.libraries.matrix.ui.messages.toPlainText
|
||||||
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ContributesBinding(SessionScope::class)
|
||||||
|
class DefaultPinnedMessagesBannerFormatter @Inject constructor(
|
||||||
|
private val sp: StringProvider,
|
||||||
|
private val permalinkParser: PermalinkParser,
|
||||||
|
) : PinnedMessagesBannerFormatter {
|
||||||
|
|
||||||
|
override fun format(event: EventTimelineItem): CharSequence {
|
||||||
|
return when (val content = event.content) {
|
||||||
|
is MessageContent -> processMessageContents(event, content)
|
||||||
|
is StickerContent -> {
|
||||||
|
content.body.prefixWith(CommonStrings.common_sticker)
|
||||||
|
}
|
||||||
|
is UnableToDecryptContent -> {
|
||||||
|
sp.getString(CommonStrings.common_waiting_for_decryption_key)
|
||||||
|
}
|
||||||
|
is PollContent -> {
|
||||||
|
content.question.prefixWith(CommonStrings.a11y_poll)
|
||||||
|
}
|
||||||
|
RedactedContent -> {
|
||||||
|
sp.getString(CommonStrings.common_message_removed)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
sp.getString(CommonStrings.common_unsupported_event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processMessageContents(
|
||||||
|
event: EventTimelineItem,
|
||||||
|
messageContent: MessageContent,
|
||||||
|
): CharSequence {
|
||||||
|
|
||||||
|
return when (val messageType: MessageType = messageContent.type) {
|
||||||
|
is EmoteMessageType -> {
|
||||||
|
val senderDisambiguatedDisplayName = event.senderProfile.getDisambiguatedDisplayName(event.sender)
|
||||||
|
"* $senderDisambiguatedDisplayName ${messageType.body}"
|
||||||
|
}
|
||||||
|
is TextMessageType -> {
|
||||||
|
messageType.toPlainText(permalinkParser)
|
||||||
|
}
|
||||||
|
is VideoMessageType -> {
|
||||||
|
messageType.body.prefixWith(CommonStrings.common_video)
|
||||||
|
}
|
||||||
|
is ImageMessageType -> {
|
||||||
|
messageType.body.prefixWith(CommonStrings.common_image)
|
||||||
|
}
|
||||||
|
is StickerMessageType -> {
|
||||||
|
messageType.body.prefixWith(CommonStrings.common_sticker)
|
||||||
|
}
|
||||||
|
is LocationMessageType -> {
|
||||||
|
messageType.body.prefixWith(CommonStrings.common_shared_location)
|
||||||
|
}
|
||||||
|
is FileMessageType -> {
|
||||||
|
messageType.body.prefixWith(CommonStrings.common_file)
|
||||||
|
}
|
||||||
|
is AudioMessageType -> {
|
||||||
|
messageType.body.prefixWith(CommonStrings.common_audio)
|
||||||
|
}
|
||||||
|
is VoiceMessageType -> {
|
||||||
|
messageType.body.prefixWith(CommonStrings.common_voice_message)
|
||||||
|
}
|
||||||
|
is OtherMessageType -> {
|
||||||
|
messageType.body
|
||||||
|
}
|
||||||
|
is NoticeMessageType -> {
|
||||||
|
messageType.body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CharSequence.prefixWith(@StringRes res: Int): AnnotatedString {
|
||||||
|
val prefix = sp.getString(res)
|
||||||
|
return prefixWith(prefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -79,7 +79,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
|
||||||
RedactedContent -> {
|
RedactedContent -> {
|
||||||
val message = sp.getString(CommonStrings.common_message_removed)
|
val message = sp.getString(CommonStrings.common_message_removed)
|
||||||
if (!isDmRoom) {
|
if (!isDmRoom) {
|
||||||
prefix(message, senderDisambiguatedDisplayName)
|
message.prefixWith(senderDisambiguatedDisplayName)
|
||||||
} else {
|
} else {
|
||||||
message
|
message
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +90,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
|
||||||
is UnableToDecryptContent -> {
|
is UnableToDecryptContent -> {
|
||||||
val message = sp.getString(CommonStrings.common_waiting_for_decryption_key)
|
val message = sp.getString(CommonStrings.common_waiting_for_decryption_key)
|
||||||
if (!isDmRoom) {
|
if (!isDmRoom) {
|
||||||
prefix(message, senderDisambiguatedDisplayName)
|
message.prefixWith(senderDisambiguatedDisplayName)
|
||||||
} else {
|
} else {
|
||||||
message
|
message
|
||||||
}
|
}
|
||||||
|
|
@ -113,7 +113,6 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
|
||||||
}
|
}
|
||||||
is LegacyCallInviteContent -> sp.getString(CommonStrings.common_call_invite)
|
is LegacyCallInviteContent -> sp.getString(CommonStrings.common_call_invite)
|
||||||
is CallNotifyContent -> sp.getString(CommonStrings.common_call_started)
|
is CallNotifyContent -> sp.getString(CommonStrings.common_call_started)
|
||||||
else -> null
|
|
||||||
}?.take(MAX_SAFE_LENGTH)
|
}?.take(MAX_SAFE_LENGTH)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,16 +167,6 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
|
||||||
): CharSequence = if (isDmRoom) {
|
): CharSequence = if (isDmRoom) {
|
||||||
message
|
message
|
||||||
} else {
|
} else {
|
||||||
prefix(message, senderDisambiguatedDisplayName)
|
message.prefixWith(senderDisambiguatedDisplayName)
|
||||||
}
|
|
||||||
|
|
||||||
private fun prefix(message: String, senderDisambiguatedDisplayName: String): AnnotatedString {
|
|
||||||
return buildAnnotatedString {
|
|
||||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
|
||||||
append(senderDisambiguatedDisplayName)
|
|
||||||
}
|
|
||||||
append(": ")
|
|
||||||
append(message)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 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
|
||||||
|
*
|
||||||
|
* https://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
|
||||||
|
|
||||||
|
internal fun CharSequence.prefixWith(prefix: String): AnnotatedString {
|
||||||
|
return buildAnnotatedString {
|
||||||
|
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||||
|
append(prefix)
|
||||||
|
}
|
||||||
|
append(": ")
|
||||||
|
append(this@prefixWith)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue