forked from GitHub-Mirror/riotX-android
Room list & event : decouple notice events formatting to be used within room controller
This commit is contained in:
parent
9f9f4c0755
commit
b9d76f5047
@ -20,6 +20,7 @@ import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.Types
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import timber.log.Timber
|
||||
import java.lang.reflect.ParameterizedType
|
||||
|
||||
typealias Content = Map<String, @JvmSuppressWildcards Any>
|
||||
@ -27,11 +28,20 @@ typealias Content = Map<String, @JvmSuppressWildcards Any>
|
||||
/**
|
||||
* This methods is a facility method to map a json content to a model.
|
||||
*/
|
||||
inline fun <reified T> Content?.toModel(): T? {
|
||||
inline fun <reified T> Content?.toModel(catchError: Boolean = true): T? {
|
||||
return this?.let {
|
||||
val moshi = MoshiProvider.providesMoshi()
|
||||
val moshiAdapter = moshi.adapter(T::class.java)
|
||||
return moshiAdapter.fromJsonValue(it)
|
||||
return try {
|
||||
moshiAdapter.fromJsonValue(it)
|
||||
} catch (e: Exception) {
|
||||
if (catchError) {
|
||||
Timber.e(e, "To model failed : $e")
|
||||
null
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,11 +25,16 @@ import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserControl
|
||||
import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserPresenter
|
||||
import im.vector.riotredesign.features.home.group.GroupSummaryController
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.*
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.DefaultItemFactory
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.MessageItemFactory
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.NoticeItemFactory
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.TimelineItemFactory
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
|
||||
import im.vector.riotredesign.features.home.room.list.RoomSummaryController
|
||||
import im.vector.riotredesign.features.html.EventHtmlRenderer
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import org.koin.dsl.module.module
|
||||
|
||||
class HomeModule {
|
||||
@ -37,8 +42,6 @@ class HomeModule {
|
||||
companion object {
|
||||
const val HOME_SCOPE = "HOME_SCOPE"
|
||||
const val ROOM_DETAIL_SCOPE = "ROOM_DETAIL_SCOPE"
|
||||
const val ROOM_LIST_SCOPE = "ROOM_LIST_SCOPE"
|
||||
const val GROUP_LIST_SCOPE = "GROUP_LIST_SCOPE"
|
||||
}
|
||||
|
||||
val definition = module {
|
||||
@ -59,26 +62,26 @@ class HomeModule {
|
||||
TimelineDateFormatter(get())
|
||||
}
|
||||
|
||||
factory {
|
||||
NoticeEventFormatter(get())
|
||||
}
|
||||
|
||||
factory { (fragment: Fragment) ->
|
||||
val eventHtmlRenderer = EventHtmlRenderer(GlideApp.with(fragment), fragment.requireContext(), get())
|
||||
val noticeEventFormatter = get<NoticeEventFormatter>(parameters = { parametersOf(fragment) })
|
||||
val timelineMediaSizeProvider = TimelineMediaSizeProvider()
|
||||
val colorProvider = ColorProvider(fragment.requireContext())
|
||||
val colorProvider = get<ColorProvider>()
|
||||
val timelineDateFormatter = get<TimelineDateFormatter>()
|
||||
val messageItemFactory = MessageItemFactory(colorProvider, timelineMediaSizeProvider, timelineDateFormatter, eventHtmlRenderer)
|
||||
|
||||
val timelineItemFactory = TimelineItemFactory(messageItemFactory = messageItemFactory,
|
||||
roomNameItemFactory = RoomNameItemFactory(get()),
|
||||
roomTopicItemFactory = RoomTopicItemFactory(get()),
|
||||
roomMemberItemFactory = RoomMemberItemFactory(get()),
|
||||
roomHistoryVisibilityItemFactory = RoomHistoryVisibilityItemFactory(get()),
|
||||
callItemFactory = CallItemFactory(get()),
|
||||
defaultItemFactory = DefaultItemFactory()
|
||||
val timelineItemFactory = TimelineItemFactory(messageItemFactory = MessageItemFactory(colorProvider, timelineMediaSizeProvider, timelineDateFormatter, eventHtmlRenderer),
|
||||
noticeItemFactory = NoticeItemFactory(noticeEventFormatter),
|
||||
defaultItemFactory = DefaultItemFactory()
|
||||
)
|
||||
TimelineEventController(timelineDateFormatter, timelineItemFactory, timelineMediaSizeProvider)
|
||||
}
|
||||
|
||||
factory {
|
||||
RoomSummaryController(get(), get())
|
||||
RoomSummaryController(get(), get(), get())
|
||||
}
|
||||
|
||||
factory {
|
||||
|
@ -48,7 +48,6 @@ class GroupListFragment : VectorBaseFragment(), GroupSummaryController.Callback
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
bindScope(getOrCreateScope(HomeModule.GROUP_LIST_SCOPE))
|
||||
groupController.callback = this
|
||||
stateView.contentView = epoxyRecyclerView
|
||||
epoxyRecyclerView.setController(groupController)
|
||||
|
@ -1,59 +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.riotredesign.features.home.room.detail.timeline.factory
|
||||
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.api.session.room.model.call.CallInviteContent
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_
|
||||
|
||||
class CallItemFactory(private val stringProvider: StringProvider) {
|
||||
|
||||
fun create(event: TimelineEvent): NoticeItem? {
|
||||
val text = buildNoticeText(event.root, event.senderName) ?: return null
|
||||
return NoticeItem_()
|
||||
.noticeText(text)
|
||||
.avatarUrl(event.senderAvatar)
|
||||
.memberName(event.senderName)
|
||||
}
|
||||
|
||||
private fun buildNoticeText(event: Event, senderName: String?): CharSequence? {
|
||||
return when {
|
||||
EventType.CALL_INVITE == event.type -> {
|
||||
val content = event.content.toModel<CallInviteContent>() ?: return null
|
||||
val isVideoCall = content.offer.sdp == CallInviteContent.Offer.SDP_VIDEO
|
||||
return if (isVideoCall) {
|
||||
stringProvider.getString(R.string.notice_placed_video_call, senderName)
|
||||
} else {
|
||||
stringProvider.getString(R.string.notice_placed_voice_call, senderName)
|
||||
}
|
||||
}
|
||||
EventType.CALL_ANSWER == event.type -> stringProvider.getString(R.string.notice_answered_call, senderName)
|
||||
EventType.CALL_HANGUP == event.type -> stringProvider.getString(R.string.notice_ended_call, senderName)
|
||||
else -> null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -16,28 +16,24 @@
|
||||
|
||||
package im.vector.riotredesign.features.home.room.detail.timeline.factory
|
||||
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomTopicContent
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderAvatar
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderName
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_
|
||||
|
||||
class RoomTopicItemFactory(private val stringProvider: StringProvider) {
|
||||
class NoticeItemFactory(private val eventFormatter: NoticeEventFormatter) {
|
||||
|
||||
fun create(event: TimelineEvent): NoticeItem? {
|
||||
val formattedText = eventFormatter.format(event) ?: return null
|
||||
val senderName = event.senderName()
|
||||
val senderAvatar = event.senderAvatar()
|
||||
|
||||
val content: RoomTopicContent = event.root.content.toModel() ?: return null
|
||||
val text = if (content.topic.isNullOrEmpty()) {
|
||||
stringProvider.getString(R.string.notice_room_topic_removed, event.senderName)
|
||||
} else {
|
||||
stringProvider.getString(R.string.notice_room_topic_changed, event.senderName, content.topic)
|
||||
}
|
||||
return NoticeItem_()
|
||||
.noticeText(text)
|
||||
.avatarUrl(event.senderAvatar)
|
||||
.memberName(event.senderName)
|
||||
.noticeText(formattedText)
|
||||
.avatarUrl(senderAvatar)
|
||||
.memberName(senderName)
|
||||
}
|
||||
|
||||
|
@ -1,55 +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.riotredesign.features.home.room.detail.timeline.factory
|
||||
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility
|
||||
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibilityContent
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_
|
||||
|
||||
|
||||
class RoomHistoryVisibilityItemFactory(private val stringProvider: StringProvider) {
|
||||
|
||||
fun create(event: TimelineEvent): NoticeItem? {
|
||||
val noticeText = buildNoticeText(event.root, event.senderName) ?: return null
|
||||
return NoticeItem_()
|
||||
.noticeText(noticeText)
|
||||
.avatarUrl(event.senderAvatar)
|
||||
.memberName(event.senderName)
|
||||
}
|
||||
|
||||
private fun buildNoticeText(event: Event, senderName: String?): CharSequence? {
|
||||
val content = event.content.toModel<RoomHistoryVisibilityContent>() ?: return null
|
||||
val formattedVisibility = when (content.historyVisibility) {
|
||||
RoomHistoryVisibility.SHARED -> stringProvider.getString(R.string.notice_room_visibility_shared)
|
||||
RoomHistoryVisibility.INVITED -> stringProvider.getString(R.string.notice_room_visibility_invited)
|
||||
RoomHistoryVisibility.JOINED -> stringProvider.getString(R.string.notice_room_visibility_joined)
|
||||
RoomHistoryVisibility.WORLD_READABLE -> stringProvider.getString(R.string.notice_room_visibility_world_readable)
|
||||
}
|
||||
return stringProvider.getString(R.string.notice_made_future_room_visibility, senderName, formattedVisibility)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,135 +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.riotredesign.features.home.room.detail.timeline.factory
|
||||
|
||||
import android.text.TextUtils
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.RoomMemberEventHelper
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_
|
||||
|
||||
|
||||
//TODO : complete with call membership events¬
|
||||
class RoomMemberItemFactory(private val stringProvider: StringProvider) {
|
||||
|
||||
fun create(event: TimelineEvent): NoticeItem? {
|
||||
val eventContent: RoomMember? = event.root.content.toModel()
|
||||
val prevEventContent: RoomMember? = event.root.prevContent.toModel()
|
||||
val noticeText = buildRoomMemberNotice(event, eventContent, prevEventContent) ?: return null
|
||||
val senderAvatar = RoomMemberEventHelper.senderAvatar(eventContent, prevEventContent, event)
|
||||
val senderName = RoomMemberEventHelper.senderName(eventContent, prevEventContent, event)
|
||||
|
||||
return NoticeItem_()
|
||||
.userId(event.root.sender ?: "")
|
||||
.noticeText(noticeText)
|
||||
.avatarUrl(senderAvatar)
|
||||
.memberName(senderName)
|
||||
}
|
||||
|
||||
private fun buildRoomMemberNotice(event: TimelineEvent, eventContent: RoomMember?, prevEventContent: RoomMember?): String? {
|
||||
val isMembershipEvent = prevEventContent?.membership != eventContent?.membership
|
||||
return if (isMembershipEvent) {
|
||||
buildMembershipNotice(event, eventContent, prevEventContent)
|
||||
} else {
|
||||
buildProfileNotice(event, eventContent, prevEventContent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildProfileNotice(event: TimelineEvent, eventContent: RoomMember?, prevEventContent: RoomMember?): String? {
|
||||
val displayText = StringBuilder()
|
||||
// Check display name has been changed
|
||||
if (!TextUtils.equals(eventContent?.displayName, prevEventContent?.displayName)) {
|
||||
val displayNameText = when {
|
||||
prevEventContent?.displayName.isNullOrEmpty() ->
|
||||
stringProvider.getString(R.string.notice_display_name_set, event.root.sender, eventContent?.displayName)
|
||||
eventContent?.displayName.isNullOrEmpty() ->
|
||||
stringProvider.getString(R.string.notice_display_name_removed, event.root.sender, prevEventContent?.displayName)
|
||||
else ->
|
||||
stringProvider.getString(R.string.notice_display_name_changed_from,
|
||||
event.root.sender, prevEventContent?.displayName, eventContent?.displayName)
|
||||
}
|
||||
displayText.append(displayNameText)
|
||||
}
|
||||
// Check whether the avatar has been changed
|
||||
if (!TextUtils.equals(eventContent?.avatarUrl, prevEventContent?.avatarUrl)) {
|
||||
val displayAvatarText = if (displayText.isNotEmpty()) {
|
||||
displayText.append(" ")
|
||||
stringProvider.getString(R.string.notice_avatar_changed_too)
|
||||
} else {
|
||||
stringProvider.getString(R.string.notice_avatar_url_changed, event.senderName)
|
||||
}
|
||||
displayText.append(displayAvatarText)
|
||||
}
|
||||
return displayText.toString()
|
||||
}
|
||||
|
||||
private fun buildMembershipNotice(event: TimelineEvent, eventContent: RoomMember?, prevEventContent: RoomMember?): String? {
|
||||
val senderDisplayName = event.senderName ?: event.root.sender
|
||||
val targetDisplayName = eventContent?.displayName ?: event.root.sender
|
||||
return when {
|
||||
Membership.INVITE == eventContent?.membership -> {
|
||||
// TODO get userId
|
||||
val selfUserId = ""
|
||||
when {
|
||||
eventContent.thirdPartyInvite != null ->
|
||||
stringProvider.getString(R.string.notice_room_third_party_registered_invite,
|
||||
targetDisplayName, eventContent.thirdPartyInvite?.displayName)
|
||||
TextUtils.equals(event.root.stateKey, selfUserId) ->
|
||||
stringProvider.getString(R.string.notice_room_invite_you, senderDisplayName)
|
||||
event.root.stateKey.isNullOrEmpty() ->
|
||||
stringProvider.getString(R.string.notice_room_invite_no_invitee, senderDisplayName)
|
||||
else ->
|
||||
stringProvider.getString(R.string.notice_room_invite, senderDisplayName, targetDisplayName)
|
||||
}
|
||||
}
|
||||
Membership.JOIN == eventContent?.membership ->
|
||||
stringProvider.getString(R.string.notice_room_join, senderDisplayName)
|
||||
Membership.LEAVE == eventContent?.membership ->
|
||||
// 2 cases here: this member may have left voluntarily or they may have been "left" by someone else ie. kicked
|
||||
return if (TextUtils.equals(event.root.sender, event.root.stateKey)) {
|
||||
if (prevEventContent?.membership == Membership.INVITE) {
|
||||
stringProvider.getString(R.string.notice_room_reject, senderDisplayName)
|
||||
} else {
|
||||
val leftDisplayName = RoomMemberEventHelper.senderName(eventContent, prevEventContent, event)
|
||||
stringProvider.getString(R.string.notice_room_leave, leftDisplayName)
|
||||
}
|
||||
} else if (prevEventContent?.membership == Membership.INVITE) {
|
||||
stringProvider.getString(R.string.notice_room_withdraw, senderDisplayName, targetDisplayName)
|
||||
} else if (prevEventContent?.membership == Membership.JOIN) {
|
||||
stringProvider.getString(R.string.notice_room_kick, senderDisplayName, targetDisplayName)
|
||||
} else if (prevEventContent?.membership == Membership.BAN) {
|
||||
stringProvider.getString(R.string.notice_room_unban, senderDisplayName, targetDisplayName)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
Membership.BAN == eventContent?.membership ->
|
||||
stringProvider.getString(R.string.notice_room_ban, senderDisplayName, targetDisplayName)
|
||||
Membership.KNOCK == eventContent?.membership ->
|
||||
stringProvider.getString(R.string.notice_room_kick, senderDisplayName, targetDisplayName)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,45 +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.riotredesign.features.home.room.detail.timeline.factory
|
||||
|
||||
import android.text.TextUtils
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomNameContent
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_
|
||||
|
||||
class RoomNameItemFactory(private val stringProvider: StringProvider) {
|
||||
|
||||
fun create(event: TimelineEvent): NoticeItem? {
|
||||
|
||||
val content: RoomNameContent = event.root.content.toModel() ?: return null
|
||||
val text = if (!TextUtils.isEmpty(content.name)) {
|
||||
stringProvider.getString(R.string.notice_room_name_changed, event.senderName, content.name)
|
||||
} else {
|
||||
stringProvider.getString(R.string.notice_room_name_removed, event.senderName)
|
||||
}
|
||||
return NoticeItem_()
|
||||
.noticeText(text)
|
||||
.avatarUrl(event.senderAvatar)
|
||||
.memberName(event.senderName)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -23,11 +23,7 @@ import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
|
||||
|
||||
class TimelineItemFactory(private val messageItemFactory: MessageItemFactory,
|
||||
private val roomNameItemFactory: RoomNameItemFactory,
|
||||
private val roomTopicItemFactory: RoomTopicItemFactory,
|
||||
private val roomMemberItemFactory: RoomMemberItemFactory,
|
||||
private val roomHistoryVisibilityItemFactory: RoomHistoryVisibilityItemFactory,
|
||||
private val callItemFactory: CallItemFactory,
|
||||
private val noticeItemFactory: NoticeItemFactory,
|
||||
private val defaultItemFactory: DefaultItemFactory) {
|
||||
|
||||
fun create(event: TimelineEvent,
|
||||
@ -36,23 +32,22 @@ class TimelineItemFactory(private val messageItemFactory: MessageItemFactory,
|
||||
|
||||
val computedModel = try {
|
||||
when (event.root.type) {
|
||||
EventType.MESSAGE -> messageItemFactory.create(event, nextEvent, callback)
|
||||
EventType.STATE_ROOM_NAME -> roomNameItemFactory.create(event)
|
||||
EventType.STATE_ROOM_TOPIC -> roomTopicItemFactory.create(event)
|
||||
EventType.STATE_ROOM_MEMBER -> roomMemberItemFactory.create(event)
|
||||
EventType.STATE_HISTORY_VISIBILITY -> roomHistoryVisibilityItemFactory.create(event)
|
||||
EventType.MESSAGE -> messageItemFactory.create(event, nextEvent, callback)
|
||||
|
||||
EventType.STATE_ROOM_NAME,
|
||||
EventType.STATE_ROOM_TOPIC,
|
||||
EventType.STATE_ROOM_MEMBER,
|
||||
EventType.STATE_HISTORY_VISIBILITY,
|
||||
EventType.CALL_INVITE,
|
||||
EventType.CALL_HANGUP,
|
||||
EventType.CALL_ANSWER -> callItemFactory.create(event)
|
||||
EventType.CALL_ANSWER -> noticeItemFactory.create(event)
|
||||
|
||||
EventType.ENCRYPTED,
|
||||
EventType.ENCRYPTION,
|
||||
EventType.STATE_ROOM_THIRD_PARTY_INVITE,
|
||||
EventType.STICKER,
|
||||
EventType.STATE_ROOM_CREATE -> defaultItemFactory.create(event)
|
||||
|
||||
else -> null
|
||||
EventType.STATE_ROOM_CREATE -> defaultItemFactory.create(event)
|
||||
else -> null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
defaultItemFactory.create(event, e)
|
||||
|
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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.riotredesign.features.home.room.detail.timeline.format
|
||||
|
||||
import android.text.TextUtils
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility
|
||||
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibilityContent
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.api.session.room.model.RoomNameContent
|
||||
import im.vector.matrix.android.api.session.room.model.RoomTopicContent
|
||||
import im.vector.matrix.android.api.session.room.model.call.CallInviteContent
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderName
|
||||
import timber.log.Timber
|
||||
|
||||
class NoticeEventFormatter(private val stringProvider: StringProvider) {
|
||||
|
||||
fun format(timelineEvent: TimelineEvent): CharSequence? {
|
||||
return when (timelineEvent.root.type) {
|
||||
EventType.STATE_ROOM_NAME -> formatRoomNameEvent(timelineEvent.root, timelineEvent.senderName)
|
||||
EventType.STATE_ROOM_TOPIC -> formatRoomTopicEvent(timelineEvent.root, timelineEvent.senderName)
|
||||
EventType.STATE_ROOM_MEMBER -> formatRoomMemberEvent(timelineEvent.root, timelineEvent.senderName())
|
||||
EventType.STATE_HISTORY_VISIBILITY -> formatRoomHistoryVisibilityEvent(timelineEvent.root, timelineEvent.senderName)
|
||||
EventType.CALL_INVITE,
|
||||
EventType.CALL_HANGUP,
|
||||
EventType.CALL_ANSWER -> formatCallEvent(timelineEvent.root, timelineEvent.senderName)
|
||||
else -> {
|
||||
Timber.v("Type ${timelineEvent.root.type} not handled by this formatter")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatRoomNameEvent(event: Event, senderName: String?): CharSequence? {
|
||||
val content = event.content.toModel<RoomNameContent>() ?: return null
|
||||
return if (!TextUtils.isEmpty(content.name)) {
|
||||
stringProvider.getString(R.string.notice_room_name_changed, senderName, content.name)
|
||||
} else {
|
||||
stringProvider.getString(R.string.notice_room_name_removed, senderName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatRoomTopicEvent(event: Event, senderName: String?): CharSequence? {
|
||||
val content = event.content.toModel<RoomTopicContent>() ?: return null
|
||||
return if (content.topic.isNullOrEmpty()) {
|
||||
stringProvider.getString(R.string.notice_room_topic_removed, senderName)
|
||||
} else {
|
||||
stringProvider.getString(R.string.notice_room_topic_changed, senderName, content.topic)
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatRoomHistoryVisibilityEvent(event: Event, senderName: String?): CharSequence? {
|
||||
val content = event.content.toModel<RoomHistoryVisibilityContent>() ?: return null
|
||||
val formattedVisibility = when (content.historyVisibility) {
|
||||
RoomHistoryVisibility.SHARED -> stringProvider.getString(R.string.notice_room_visibility_shared)
|
||||
RoomHistoryVisibility.INVITED -> stringProvider.getString(R.string.notice_room_visibility_invited)
|
||||
RoomHistoryVisibility.JOINED -> stringProvider.getString(R.string.notice_room_visibility_joined)
|
||||
RoomHistoryVisibility.WORLD_READABLE -> stringProvider.getString(R.string.notice_room_visibility_world_readable)
|
||||
}
|
||||
return stringProvider.getString(R.string.notice_made_future_room_visibility, senderName, formattedVisibility)
|
||||
}
|
||||
|
||||
private fun formatCallEvent(event: Event, senderName: String?): CharSequence? {
|
||||
return when {
|
||||
EventType.CALL_INVITE == event.type -> {
|
||||
val content = event.content.toModel<CallInviteContent>() ?: return null
|
||||
val isVideoCall = content.offer.sdp == CallInviteContent.Offer.SDP_VIDEO
|
||||
return if (isVideoCall) {
|
||||
stringProvider.getString(R.string.notice_placed_video_call, senderName)
|
||||
} else {
|
||||
stringProvider.getString(R.string.notice_placed_voice_call, senderName)
|
||||
}
|
||||
}
|
||||
EventType.CALL_ANSWER == event.type -> stringProvider.getString(R.string.notice_answered_call, senderName)
|
||||
EventType.CALL_HANGUP == event.type -> stringProvider.getString(R.string.notice_ended_call, senderName)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatRoomMemberEvent(event: Event, senderName: String?): String? {
|
||||
val eventContent: RoomMember? = event.content.toModel()
|
||||
val prevEventContent: RoomMember? = event.prevContent.toModel()
|
||||
val isMembershipEvent = prevEventContent?.membership != eventContent?.membership
|
||||
return if (isMembershipEvent) {
|
||||
buildMembershipNotice(event, senderName, eventContent, prevEventContent)
|
||||
} else {
|
||||
buildProfileNotice(event, senderName, eventContent, prevEventContent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildProfileNotice(event: Event, senderName: String?, eventContent: RoomMember?, prevEventContent: RoomMember?): String? {
|
||||
val displayText = StringBuilder()
|
||||
// Check display name has been changed
|
||||
if (!TextUtils.equals(eventContent?.displayName, prevEventContent?.displayName)) {
|
||||
val displayNameText = when {
|
||||
prevEventContent?.displayName.isNullOrEmpty() ->
|
||||
stringProvider.getString(R.string.notice_display_name_set, event.sender, eventContent?.displayName)
|
||||
eventContent?.displayName.isNullOrEmpty() ->
|
||||
stringProvider.getString(R.string.notice_display_name_removed, event.sender, prevEventContent?.displayName)
|
||||
else ->
|
||||
stringProvider.getString(R.string.notice_display_name_changed_from,
|
||||
event.sender, prevEventContent?.displayName, eventContent?.displayName)
|
||||
}
|
||||
displayText.append(displayNameText)
|
||||
}
|
||||
// Check whether the avatar has been changed
|
||||
if (!TextUtils.equals(eventContent?.avatarUrl, prevEventContent?.avatarUrl)) {
|
||||
val displayAvatarText = if (displayText.isNotEmpty()) {
|
||||
displayText.append(" ")
|
||||
stringProvider.getString(R.string.notice_avatar_changed_too)
|
||||
} else {
|
||||
stringProvider.getString(R.string.notice_avatar_url_changed, senderName)
|
||||
}
|
||||
displayText.append(displayAvatarText)
|
||||
}
|
||||
return displayText.toString()
|
||||
}
|
||||
|
||||
private fun buildMembershipNotice(event: Event, senderName: String?, eventContent: RoomMember?, prevEventContent: RoomMember?): String? {
|
||||
val senderDisplayName = senderName ?: event.sender
|
||||
val targetDisplayName = eventContent?.displayName ?: event.sender
|
||||
return when {
|
||||
Membership.INVITE == eventContent?.membership -> {
|
||||
// TODO get userId
|
||||
val selfUserId = ""
|
||||
when {
|
||||
eventContent.thirdPartyInvite != null ->
|
||||
stringProvider.getString(R.string.notice_room_third_party_registered_invite,
|
||||
targetDisplayName, eventContent.thirdPartyInvite?.displayName)
|
||||
TextUtils.equals(event.stateKey, selfUserId) ->
|
||||
stringProvider.getString(R.string.notice_room_invite_you, senderDisplayName)
|
||||
event.stateKey.isNullOrEmpty() ->
|
||||
stringProvider.getString(R.string.notice_room_invite_no_invitee, senderDisplayName)
|
||||
else ->
|
||||
stringProvider.getString(R.string.notice_room_invite, senderDisplayName, targetDisplayName)
|
||||
}
|
||||
}
|
||||
Membership.JOIN == eventContent?.membership ->
|
||||
stringProvider.getString(R.string.notice_room_join, senderDisplayName)
|
||||
Membership.LEAVE == eventContent?.membership ->
|
||||
// 2 cases here: this member may have left voluntarily or they may have been "left" by someone else ie. kicked
|
||||
return if (TextUtils.equals(event.sender, event.stateKey)) {
|
||||
if (prevEventContent?.membership == Membership.INVITE) {
|
||||
stringProvider.getString(R.string.notice_room_reject, senderDisplayName)
|
||||
} else {
|
||||
stringProvider.getString(R.string.notice_room_leave, senderDisplayName)
|
||||
}
|
||||
} else if (prevEventContent?.membership == Membership.INVITE) {
|
||||
stringProvider.getString(R.string.notice_room_withdraw, senderDisplayName, targetDisplayName)
|
||||
} else if (prevEventContent?.membership == Membership.JOIN) {
|
||||
stringProvider.getString(R.string.notice_room_kick, senderDisplayName, targetDisplayName)
|
||||
} else if (prevEventContent?.membership == Membership.BAN) {
|
||||
stringProvider.getString(R.string.notice_room_unban, senderDisplayName, targetDisplayName)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
Membership.BAN == eventContent?.membership ->
|
||||
stringProvider.getString(R.string.notice_room_ban, senderDisplayName, targetDisplayName)
|
||||
Membership.KNOCK == eventContent?.membership ->
|
||||
stringProvider.getString(R.string.notice_room_kick, senderDisplayName, targetDisplayName)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -16,25 +16,11 @@
|
||||
|
||||
package im.vector.riotredesign.features.home.room.detail.timeline.helper
|
||||
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
|
||||
object RoomMemberEventHelper {
|
||||
|
||||
fun senderAvatar(eventContent: RoomMember?, prevEventContent: RoomMember?, event: TimelineEvent): String? {
|
||||
return if (eventContent?.membership == Membership.LEAVE && eventContent.avatarUrl == null && prevEventContent?.avatarUrl != null) {
|
||||
prevEventContent.avatarUrl
|
||||
} else {
|
||||
event.senderAvatar
|
||||
}
|
||||
}
|
||||
|
||||
fun senderName(eventContent: RoomMember?, prevEventContent: RoomMember?, event: TimelineEvent): String? {
|
||||
return if (eventContent?.membership == Membership.LEAVE && eventContent.displayName == null && prevEventContent?.displayName != null) {
|
||||
prevEventContent.displayName
|
||||
} else {
|
||||
event.senderName
|
||||
}
|
||||
}
|
||||
}
|
@ -17,6 +17,8 @@
|
||||
package im.vector.riotredesign.features.home.room.detail.timeline.helper
|
||||
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotredesign.core.extensions.localDateTime
|
||||
|
||||
@ -49,6 +51,26 @@ fun List<TimelineEvent>.filterDisplayableEvents(): List<TimelineEvent> {
|
||||
}
|
||||
}
|
||||
|
||||
fun TimelineEvent.senderAvatar(): String? {
|
||||
// We might have no avatar when user leave, so we try to get it from prevContent
|
||||
return senderAvatar
|
||||
?: if (root.type == EventType.STATE_ROOM_MEMBER) {
|
||||
root.prevContent.toModel<RoomMember>()?.avatarUrl
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun TimelineEvent.senderName(): String? {
|
||||
// We might have no senderName when user leave, so we try to get it from prevContent
|
||||
return senderName
|
||||
?: if (root.type == EventType.STATE_ROOM_MEMBER) {
|
||||
root.prevContent.toModel<RoomMember>()?.displayName
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun TimelineEvent.canBeMerged(): Boolean {
|
||||
return root.type == EventType.STATE_ROOM_MEMBER
|
||||
}
|
||||
|
@ -71,7 +71,6 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback {
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
bindScope(getOrCreateScope(HomeModule.ROOM_LIST_SCOPE))
|
||||
setupRecyclerView()
|
||||
setupCreateRoomButton()
|
||||
roomListViewModel.subscribe { renderState(it) }
|
||||
|
@ -22,9 +22,11 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||
import im.vector.riotredesign.core.extensions.localDateTime
|
||||
import im.vector.riotredesign.core.resources.DateProvider
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
|
||||
|
||||
class RoomSummaryController(private val stringProvider: StringProvider,
|
||||
private val eventFormatter: NoticeEventFormatter,
|
||||
private val timelineDateFormatter: TimelineDateFormatter
|
||||
) : TypedEpoxyController<RoomListViewState>() {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user