Use room member instead of user when it makes sense

This commit is contained in:
ganfra 2020-11-06 14:41:43 +01:00
parent eb1fa0919f
commit 6dfdc77ebd
15 changed files with 179 additions and 142 deletions

View file

@ -137,7 +137,7 @@ class VectorCallViewModel @AssistedInject constructor(
session.callSignalingService().getCallWithId(it)?.let { mxCall ->
this.call = mxCall
mxCall.otherUserId
val item: MatrixItem? = session.getUser(mxCall.otherUserId)?.toMatrixItem()
val item: MatrixItem? = session.getRoomMember(mxCall.otherUserId, mxCall.roomId)?.toMatrixItem()
mxCall.addListener(callStateListener)

View file

@ -42,6 +42,7 @@ import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent
import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent
import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
import org.matrix.android.sdk.api.util.toMatrixItem
import org.webrtc.AudioSource
import org.webrtc.AudioTrack
import org.webrtc.Camera1Enumerator
@ -330,8 +331,8 @@ class WebRtcPeerConnectionManager @Inject constructor(
currentCall?.mxCall
?.takeIf { it.state is CallState.Connected }
?.let { mxCall ->
val name = currentSession?.getUser(mxCall.otherUserId)?.getBestName()
?: mxCall.roomId
val name = currentSession?.getRoomMember(mxCall.otherUserId, mxCall.roomId)?.toMatrixItem()?.getBestName()
?: mxCall.otherUserId
// Start background service with notification
CallService.onPendingCall(
context = context,
@ -388,7 +389,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
val mxCall = callContext.mxCall
// Update service state
val name = currentSession?.getUser(mxCall.otherUserId)?.getBestName()
val name = currentSession?.getRoomMember(mxCall.otherUserId, mxCall.roomId)?.toMatrixItem()?.getBestName()
?: mxCall.roomId
CallService.onPendingCall(
context = context,
@ -576,7 +577,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
?.let { mxCall ->
// Start background service with notification
val name = currentSession?.getUser(mxCall.otherUserId)?.getBestName()
val name = currentSession?.getRoomMember(mxCall.otherUserId, mxCall.roomId)?.toMatrixItem()?.getBestName()
?: mxCall.otherUserId
CallService.onOnGoingCallBackground(
context = context,
@ -650,7 +651,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
callAudioManager.startForCall(createdCall)
currentCall = callContext
val name = currentSession?.getUser(createdCall.otherUserId)?.getBestName()
val name = currentSession?.getRoomMember(createdCall.otherUserId, createdCall.roomId)?.toMatrixItem()?.getBestName()
?: createdCall.otherUserId
CallService.onOutgoingCallRinging(
context = context.applicationContext,
@ -706,7 +707,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
}
// Start background service with notification
val name = currentSession?.getUser(mxCall.otherUserId)?.getBestName()
val name = currentSession?.getRoomMember(mxCall.otherUserId, mxCall.roomId)?.toMatrixItem()?.getBestName()
?: mxCall.otherUserId
CallService.onIncomingCallRinging(
context = context,
@ -845,7 +846,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
}
val mxCall = call.mxCall
// Update service state
val name = currentSession?.getUser(mxCall.otherUserId)?.getBestName()
val name = currentSession?.getRoomMember(mxCall.otherUserId, mxCall.roomId)?.toMatrixItem()?.getBestName()
?: mxCall.otherUserId
CallService.onPendingCall(
context = context,

View file

@ -46,7 +46,7 @@ class JitsiCallViewModel @AssistedInject constructor(
}
init {
val me = session.getUser(session.myUserId)?.toMatrixItem()
val me = session.getRoomMember(session.myUserId, args.roomId)?.toMatrixItem()
val userInfo = JitsiMeetUserInfo().apply {
displayName = me?.getBestName()
avatar = me?.avatarUrl?.let { session.contentUrlResolver().resolveFullSize(it) }?.let { URL(it) }

View file

@ -141,6 +141,7 @@ import im.vector.app.features.home.room.detail.timeline.reactions.ViewReactionsB
import im.vector.app.features.home.room.detail.widget.RoomWidgetsBottomSheet
import im.vector.app.features.html.EventHtmlRenderer
import im.vector.app.features.html.PillImageSpan
import im.vector.app.features.html.PillsPostProcessor
import im.vector.app.features.invite.VectorInviteView
import im.vector.app.features.media.ImageContentRenderer
import im.vector.app.features.media.VideoContentRenderer
@ -221,7 +222,8 @@ class RoomDetailFragment @Inject constructor(
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager,
private val matrixItemColorProvider: MatrixItemColorProvider,
private val imageContentRenderer: ImageContentRenderer,
private val roomDetailPendingActionStore: RoomDetailPendingActionStore
private val roomDetailPendingActionStore: RoomDetailPendingActionStore,
private val pillsPostProcessorFactory: PillsPostProcessor.Factory
) :
VectorBaseFragment(),
TimelineEventController.Callback,
@ -254,6 +256,9 @@ class RoomDetailFragment @Inject constructor(
private val glideRequests by lazy {
GlideApp.with(this)
}
private val pillsPostProcessor by lazy {
pillsPostProcessorFactory.create(roomDetailArgs.roomId)
}
private val autoCompleter: AutoCompleter by lazy {
autoCompleterFactory.create(roomDetailArgs.roomId)
@ -848,7 +853,7 @@ class RoomDetailFragment @Inject constructor(
if (messageContent is MessageTextContent && messageContent.format == MessageFormat.FORMAT_MATRIX_HTML) {
val parser = Parser.builder().build()
val document = parser.parse(messageContent.formattedBody ?: messageContent.body)
formattedBody = eventHtmlRenderer.render(document)
formattedBody = eventHtmlRenderer.render(document, pillsPostProcessor)
}
composerLayout.composerRelatedMessageContent.text = (formattedBody ?: nonFormattedBody)

View file

@ -1300,7 +1300,7 @@ class RoomDetailViewModel @AssistedInject constructor(
}
if (summary.membership == Membership.INVITE) {
summary.inviterId?.let { inviterId ->
session.getUser(inviterId)
session.getRoomMember(inviterId, summary.roomId)
}?.also {
setState { copy(asyncInviter = Success(it)) }
}

View file

@ -60,7 +60,7 @@ data class RoomDetailViewState(
val roomId: String,
val eventId: String?,
val myRoomMember: Async<RoomMemberSummary> = Uninitialized,
val asyncInviter: Async<User> = Uninitialized,
val asyncInviter: Async<RoomMemberSummary> = Uninitialized,
val asyncRoomSummary: Async<RoomSummary> = Uninitialized,
val activeRoomWidgets: Async<List<Widget>> = Uninitialized,
val typingMessage: String? = null,

View file

@ -123,7 +123,7 @@ class SearchResultController @Inject constructor(
.formattedDate(dateFormatter.format(event.originServerTs, DateFormatKind.MESSAGE_SIMPLE))
.spannable(spannable)
.sender(eventAndSender.sender
?: eventAndSender.event.senderId?.let { session.getUser(it) }?.toMatrixItem())
?: eventAndSender.event.senderId?.let { session.getRoomMember(it, data.roomId) }?.toMatrixItem())
.listener { listener?.onItemClicked(eventAndSender.event) }
.let { result.add(it) }
}

View file

@ -28,6 +28,7 @@ import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.home.room.detail.timeline.format.NoticeEventFormatter
import im.vector.app.features.html.EventHtmlRenderer
import im.vector.app.features.html.PillsPostProcessor
import im.vector.app.features.html.VectorHtmlCompressor
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
import im.vector.app.features.reactions.data.EmojiDataSource
@ -57,18 +58,22 @@ import java.util.ArrayList
* Information related to an event and used to display preview in contextual bottom sheet.
*/
class MessageActionsViewModel @AssistedInject constructor(@Assisted
initialState: MessageActionState,
private val initialState: MessageActionState,
private val eventHtmlRenderer: Lazy<EventHtmlRenderer>,
private val htmlCompressor: VectorHtmlCompressor,
private val session: Session,
private val noticeEventFormatter: NoticeEventFormatter,
private val stringProvider: StringProvider,
private val pillsPostProcessorFactory: PillsPostProcessor.Factory,
private val vectorPreferences: VectorPreferences
) : VectorViewModel<MessageActionState, MessageActionsAction, EmptyViewEvents>(initialState) {
private val eventId = initialState.eventId
private val informationData = initialState.informationData
private val room = session.getRoom(initialState.roomId)
private val pillsPostProcessor by lazy {
pillsPostProcessorFactory.create(initialState.roomId)
}
@AssistedInject.Factory
interface Factory {
@ -164,7 +169,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
return when (timelineEvent.root.getClearType()) {
EventType.MESSAGE,
EventType.STICKER -> {
EventType.STICKER -> {
val messageContent: MessageContent? = timelineEvent.getLastMessageContent()
if (messageContent is MessageTextContent && messageContent.format == MessageFormat.FORMAT_MATRIX_HTML) {
val html = messageContent.formattedBody
@ -172,7 +177,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
?.let { htmlCompressor.compress(it) }
?: messageContent.body
eventHtmlRenderer.get().render(html)
eventHtmlRenderer.get().render(html, pillsPostProcessor)
} else if (messageContent is MessageVerificationRequestContent) {
stringProvider.getString(R.string.verification_request)
} else {

View file

@ -60,6 +60,7 @@ import im.vector.app.features.home.room.detail.timeline.tools.createLinkMovement
import im.vector.app.features.home.room.detail.timeline.tools.linkify
import im.vector.app.features.html.CodeVisitor
import im.vector.app.features.html.EventHtmlRenderer
import im.vector.app.features.html.PillsPostProcessor
import im.vector.app.features.html.VectorHtmlCompressor
import im.vector.app.features.media.ImageContentRenderer
import im.vector.app.features.media.VideoContentRenderer
@ -106,15 +107,19 @@ class MessageItemFactory @Inject constructor(
private val defaultItemFactory: DefaultItemFactory,
private val noticeItemFactory: NoticeItemFactory,
private val avatarSizeProvider: AvatarSizeProvider,
private val pillsPostProcessorFactory: PillsPostProcessor.Factory,
private val session: Session) {
private val pillsPostProcessor by lazy {
pillsPostProcessorFactory.create(roomSummaryHolder.roomSummary?.roomId)
}
fun create(event: TimelineEvent,
nextEvent: TimelineEvent?,
highlight: Boolean,
callback: TimelineEventController.Callback?
): VectorEpoxyModel<*>? {
event.root.eventId ?: return null
val informationData = messageInformationDataFactory.create(event, nextEvent)
if (event.root.isRedacted()) {
@ -139,16 +144,16 @@ class MessageItemFactory @Inject constructor(
// val all = event.root.toContent()
// val ev = all.toModel<Event>()
return when (messageContent) {
is MessageEmoteContent -> buildEmoteMessageItem(messageContent, informationData, highlight, callback, attributes)
is MessageTextContent -> buildItemForTextContent(messageContent, informationData, highlight, callback, attributes)
is MessageImageInfoContent -> buildImageMessageItem(messageContent, informationData, highlight, callback, attributes)
is MessageNoticeContent -> buildNoticeMessageItem(messageContent, informationData, highlight, callback, attributes)
is MessageVideoContent -> buildVideoMessageItem(messageContent, informationData, highlight, callback, attributes)
is MessageFileContent -> buildFileMessageItem(messageContent, highlight, attributes)
is MessageAudioContent -> buildAudioMessageItem(messageContent, informationData, highlight, attributes)
is MessageEmoteContent -> buildEmoteMessageItem(messageContent, informationData, highlight, callback, attributes)
is MessageTextContent -> buildItemForTextContent(messageContent, informationData, highlight, callback, attributes)
is MessageImageInfoContent -> buildImageMessageItem(messageContent, informationData, highlight, callback, attributes)
is MessageNoticeContent -> buildNoticeMessageItem(messageContent, informationData, highlight, callback, attributes)
is MessageVideoContent -> buildVideoMessageItem(messageContent, informationData, highlight, callback, attributes)
is MessageFileContent -> buildFileMessageItem(messageContent, highlight, attributes)
is MessageAudioContent -> buildAudioMessageItem(messageContent, informationData, highlight, attributes)
is MessageVerificationRequestContent -> buildVerificationRequestMessageItem(messageContent, informationData, highlight, callback, attributes)
is MessageOptionsContent -> buildOptionsMessageItem(messageContent, informationData, highlight, callback, attributes)
is MessagePollResponseContent -> noticeItemFactory.create(event, highlight, roomSummaryHolder.roomSummary, callback)
is MessageOptionsContent -> buildOptionsMessageItem(messageContent, informationData, highlight, callback, attributes)
is MessagePollResponseContent -> noticeItemFactory.create(event, highlight, roomSummaryHolder.roomSummary, callback)
else -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes)
}
}
@ -159,7 +164,7 @@ class MessageItemFactory @Inject constructor(
callback: TimelineEventController.Callback?,
attributes: AbsMessageItem.Attributes): VectorEpoxyModel<*>? {
return when (messageContent.optionType) {
OPTION_TYPE_POLL -> {
OPTION_TYPE_POLL -> {
MessagePollItem_()
.attributes(attributes)
.callback(callback)
@ -217,13 +222,17 @@ class MessageItemFactory @Inject constructor(
attributes: AbsMessageItem.Attributes): VerificationRequestItem? {
// If this request is not sent by me or sent to me, we should ignore it in timeline
val myUserId = session.myUserId
val roomId = roomSummaryHolder.roomSummary?.roomId
if (informationData.senderId != myUserId && messageContent.toUserId != myUserId) {
return null
}
val otherUserId = if (informationData.sentByMe) messageContent.toUserId else informationData.senderId
val otherUserName = if (informationData.sentByMe) session.getUser(messageContent.toUserId)?.displayName
else informationData.memberName
val otherUserName = if (informationData.sentByMe) {
session.getRoomMember(messageContent.toUserId, roomId ?: "")?.displayName
} else {
informationData.memberName
}
return VerificationRequestItem_()
.attributes(
VerificationRequestItem.Attributes(
@ -362,7 +371,7 @@ class MessageItemFactory @Inject constructor(
val codeVisitor = CodeVisitor()
codeVisitor.visit(localFormattedBody)
when (codeVisitor.codeKind) {
CodeVisitor.Kind.BLOCK -> {
CodeVisitor.Kind.BLOCK -> {
val codeFormattedBlock = htmlRenderer.get().render(localFormattedBody)
if (codeFormattedBlock == null) {
buildFormattedTextItem(messageContent, informationData, highlight, callback, attributes)
@ -378,7 +387,7 @@ class MessageItemFactory @Inject constructor(
buildMessageTextItem(codeFormatted, false, informationData, highlight, callback, attributes)
}
}
CodeVisitor.Kind.NONE -> {
CodeVisitor.Kind.NONE -> {
buildFormattedTextItem(messageContent, informationData, highlight, callback, attributes)
}
}
@ -393,7 +402,7 @@ class MessageItemFactory @Inject constructor(
callback: TimelineEventController.Callback?,
attributes: AbsMessageItem.Attributes): MessageTextItem? {
val compressed = htmlCompressor.compress(messageContent.formattedBody!!)
val formattedBody = htmlRenderer.get().render(compressed)
val formattedBody = htmlRenderer.get().render(compressed, pillsPostProcessor)
return buildMessageTextItem(formattedBody, true, informationData, highlight, callback, attributes)
}
@ -528,7 +537,7 @@ class MessageItemFactory @Inject constructor(
private fun MessageContentWithFormattedBody.getHtmlBody(): CharSequence {
return matrixFormattedBody
?.let { htmlCompressor.compress(it) }
?.let { htmlRenderer.get().render(it) }
?.let { htmlRenderer.get().render(it, pillsPostProcessor) }
?: body
}

View file

@ -17,21 +17,23 @@
package im.vector.app.features.html
import android.content.Context
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.glide.GlideApp
import android.text.Spannable
import androidx.core.text.toSpannable
import im.vector.app.core.resources.ColorProvider
import im.vector.app.features.home.AvatarRenderer
import io.noties.markwon.Markwon
import io.noties.markwon.html.HtmlPlugin
import io.noties.markwon.html.TagHandlerNoOp
import org.commonmark.node.Node
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class EventHtmlRenderer @Inject constructor(context: Context,
htmlConfigure: MatrixHtmlPluginConfigure) {
class EventHtmlRenderer @Inject constructor(htmlConfigure: MatrixHtmlPluginConfigure,
context: Context) {
interface PostProcessor {
fun afterRender(renderedText: Spannable)
}
private val markwon = Markwon.builder(context)
.usePlugin(HtmlPlugin.create(htmlConfigure))
@ -41,35 +43,47 @@ class EventHtmlRenderer @Inject constructor(context: Context,
return markwon.parse(text)
}
fun render(text: String): CharSequence {
/**
* @param text the text you want to render
* @param postProcessors an optional array of post processor to add any span if needed
*/
fun render(text: String, vararg postProcessors: PostProcessor): CharSequence {
return try {
markwon.toMarkdown(text)
val parsed = markwon.parse(text)
renderAndProcess(parsed, postProcessors)
} catch (failure: Throwable) {
Timber.v("Fail to render $text to html")
text
}
}
fun render(node: Node): CharSequence? {
/**
* @param node the node you want to render
* @param postProcessors an optional array of post processor to add any span if needed
*/
fun render(node: Node, vararg postProcessors: PostProcessor): CharSequence? {
return try {
markwon.render(node)
renderAndProcess(node, postProcessors)
} catch (failure: Throwable) {
Timber.v("Fail to render $node to html")
return null
}
}
private fun renderAndProcess(node: Node, postProcessors: Array<out PostProcessor>): CharSequence {
val renderedText = markwon.render(node).toSpannable()
postProcessors.forEach {
it.afterRender(renderedText)
}
return renderedText
}
}
class MatrixHtmlPluginConfigure @Inject constructor(private val context: Context,
private val colorProvider: ColorProvider,
private val avatarRenderer: AvatarRenderer,
private val session: ActiveSessionHolder) : HtmlPlugin.HtmlConfigure {
class MatrixHtmlPluginConfigure @Inject constructor(private val colorProvider: ColorProvider) : HtmlPlugin.HtmlConfigure {
override fun configureHtml(plugin: HtmlPlugin) {
plugin
.addHandler(TagHandlerNoOp.create("a"))
.addHandler(FontTagHandler())
.addHandler(MxLinkTagHandler(GlideApp.with(context), context, avatarRenderer, session))
.addHandler(MxReplyTagHandler())
.addHandler(SpanHandler(colorProvider))
}

View file

@ -1,89 +0,0 @@
/*
* Copyright 2019 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 im.vector.app.features.html
import android.content.Context
import android.text.style.URLSpan
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.glide.GlideRequests
import im.vector.app.features.home.AvatarRenderer
import io.noties.markwon.MarkwonVisitor
import io.noties.markwon.SpannableBuilder
import io.noties.markwon.html.HtmlTag
import io.noties.markwon.html.MarkwonHtmlRenderer
import io.noties.markwon.html.tag.LinkHandler
import org.matrix.android.sdk.api.session.permalinks.PermalinkData
import org.matrix.android.sdk.api.session.permalinks.PermalinkParser
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.util.MatrixItem
class MxLinkTagHandler(private val glideRequests: GlideRequests,
private val context: Context,
private val avatarRenderer: AvatarRenderer,
private val sessionHolder: ActiveSessionHolder) : LinkHandler() {
override fun handle(visitor: MarkwonVisitor, renderer: MarkwonHtmlRenderer, tag: HtmlTag) {
val link = tag.attributes()["href"]
if (link != null) {
val permalinkData = PermalinkParser.parse(link)
val matrixItem = when (permalinkData) {
is PermalinkData.UserLink -> {
val user = sessionHolder.getSafeActiveSession()?.getUser(permalinkData.userId)
MatrixItem.UserItem(permalinkData.userId, user?.displayName, user?.avatarUrl)
}
is PermalinkData.RoomLink -> {
if (permalinkData.eventId == null) {
val room: RoomSummary? = sessionHolder.getSafeActiveSession()?.getRoomSummary(permalinkData.roomIdOrAlias)
if (permalinkData.isRoomAlias) {
MatrixItem.RoomAliasItem(permalinkData.roomIdOrAlias, room?.displayName, room?.avatarUrl)
} else {
MatrixItem.RoomItem(permalinkData.roomIdOrAlias, room?.displayName, room?.avatarUrl)
}
} else {
// Exclude event link (used in reply events, we do not want to pill the "in reply to")
null
}
}
is PermalinkData.GroupLink -> {
val group = sessionHolder.getSafeActiveSession()?.getGroupSummary(permalinkData.groupId)
MatrixItem.GroupItem(permalinkData.groupId, group?.displayName, group?.avatarUrl)
}
else -> null
}
if (matrixItem == null) {
super.handle(visitor, renderer, tag)
} else {
val span = PillImageSpan(glideRequests, avatarRenderer, context, matrixItem)
SpannableBuilder.setSpans(
visitor.builder(),
span,
tag.start(),
tag.end()
)
SpannableBuilder.setSpans(
visitor.builder(),
URLSpan(link),
tag.start(),
tag.end()
)
}
} else {
super.handle(visitor, renderer, tag)
}
}
}

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2020 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 im.vector.app.features.html
import android.content.Context
import android.text.Spannable
import android.text.Spanned
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.glide.GlideApp
import im.vector.app.features.home.AvatarRenderer
import io.noties.markwon.core.spans.LinkSpan
import org.matrix.android.sdk.api.session.permalinks.PermalinkData
import org.matrix.android.sdk.api.session.permalinks.PermalinkParser
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.util.MatrixItem
import org.matrix.android.sdk.api.util.toMatrixItem
class PillsPostProcessor @AssistedInject constructor(@Assisted private val roomId: String?,
private val context: Context,
private val avatarRenderer: AvatarRenderer,
private val sessionHolder: ActiveSessionHolder)
: EventHtmlRenderer.PostProcessor {
@AssistedInject.Factory
interface Factory {
fun create(roomId: String?): PillsPostProcessor
}
override fun afterRender(renderedText: Spannable) {
addPillSpans(renderedText, roomId)
}
private fun addPillSpans(renderedText: Spannable, roomId: String?) {
// We let markdown handle links and then we add PillImageSpan if needed.
val linkSpans = renderedText.getSpans(0, renderedText.length, LinkSpan::class.java)
linkSpans.forEach { linkSpan ->
val pillSpan = linkSpan.createPillSpan(roomId) ?: return@forEach
val startSpan = renderedText.getSpanStart(linkSpan)
val endSpan = renderedText.getSpanEnd(linkSpan)
renderedText.setSpan(pillSpan, startSpan, endSpan, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
private fun LinkSpan.createPillSpan(roomId: String?): PillImageSpan? {
val permalinkData = PermalinkParser.parse(url)
val matrixItem = when (permalinkData) {
is PermalinkData.UserLink -> {
if (roomId == null) {
sessionHolder.getSafeActiveSession()?.getUser(permalinkData.userId)?.toMatrixItem()
} else {
sessionHolder.getSafeActiveSession()?.getRoomMember(permalinkData.userId, roomId)?.toMatrixItem()
}
}
is PermalinkData.RoomLink -> {
if (permalinkData.eventId == null) {
val room: RoomSummary? = sessionHolder.getSafeActiveSession()?.getRoomSummary(permalinkData.roomIdOrAlias)
if (permalinkData.isRoomAlias) {
MatrixItem.RoomAliasItem(permalinkData.roomIdOrAlias, room?.displayName, room?.avatarUrl)
} else {
MatrixItem.RoomItem(permalinkData.roomIdOrAlias, room?.displayName, room?.avatarUrl)
}
} else {
// Exclude event link (used in reply events, we do not want to pill the "in reply to")
null
}
}
is PermalinkData.GroupLink -> {
val group = sessionHolder.getSafeActiveSession()?.getGroupSummary(permalinkData.groupId)
MatrixItem.GroupItem(permalinkData.groupId, group?.displayName, group?.avatarUrl)
}
else -> null
} ?: return null
return PillImageSpan(GlideApp.with(context), avatarRenderer, context, matrixItem)
}
}

View file

@ -27,6 +27,7 @@ import im.vector.app.core.platform.ButtonStateView
import im.vector.app.features.home.AvatarRenderer
import kotlinx.android.synthetic.main.vector_invite_view.view.*
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.session.user.model.User
import org.matrix.android.sdk.api.util.toMatrixItem
import javax.inject.Inject
@ -73,7 +74,7 @@ class VectorInviteView @JvmOverloads constructor(context: Context, attrs: Attrib
}
}
fun render(sender: User, mode: Mode = Mode.LARGE, changeMembershipState: ChangeMembershipState) {
fun render(sender: RoomMemberSummary, mode: Mode = Mode.LARGE, changeMembershipState: ChangeMembershipState) {
if (mode == Mode.LARGE) {
updateLayoutParams { height = LayoutParams.MATCH_CONSTRAINT }
avatarRenderer.render(sender.toMatrixItem(), inviteAvatarView)

View file

@ -163,7 +163,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
private fun resolveStateRoomEvent(event: Event, session: Session): NotifiableEvent? {
val content = event.content?.toModel<RoomMemberContent>() ?: return null
val roomId = event.roomId ?: return null
val dName = event.senderId?.let { session.getUser(it)?.displayName }
val dName = event.senderId?.let { session.getRoomMember(it, roomId)?.displayName }
if (Membership.INVITE == content.membership) {
val body = noticeEventFormatter.format(event, dName, session.getRoomSummary(roomId))
?: stringProvider.getString(R.string.notification_new_invitation)

View file

@ -120,7 +120,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
null,
false,
System.currentTimeMillis(),
session.getUser(session.myUserId)?.displayName
session.getRoomMember(session.myUserId, room.roomId)?.displayName
?: context?.getString(R.string.notification_sender_me),
session.myUserId,
message,