mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 13:38:49 +03:00
Use room member instead of user when it makes sense
This commit is contained in:
parent
eb1fa0919f
commit
6dfdc77ebd
15 changed files with 179 additions and 142 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)) }
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue