Room update: start handling tombstone and room create events [WIP]

This commit is contained in:
ganfra
2019-07-25 19:34:39 +02:00
parent 6176520805
commit 9e5c70dda3
23 changed files with 362 additions and 18 deletions

View File

@ -244,7 +244,8 @@ class RoomDetailFragment :
composerLayout.collapse()
}
private fun enterSpecialMode(event: TimelineEvent, @DrawableRes iconRes: Int, useText: Boolean) {
private fun enterSpecialMode(event: TimelineEvent, @DrawableRes
iconRes: Int, useText: Boolean) {
commandAutocompletePolicy.enabled = false
//switch to expanded bar
composerLayout.composerRelatedMessageTitle.apply {
@ -533,7 +534,13 @@ class RoomDetailFragment :
} else if (state.asyncInviter.complete) {
vectorBaseActivity.finish()
}
composerLayout.setRoomEncrypted(state.isEncrypted)
if (state.tombstoneContent == null) {
composerLayout.visibility = View.VISIBLE
composerLayout.setRoomEncrypted(state.isEncrypted)
} else {
composerLayout.visibility = View.GONE
showSnackWithMessage("TOMBSTONED", duration = Snackbar.LENGTH_INDEFINITE)
}
}
private fun renderRoomSummary(state: RoomDetailViewState) {

View File

@ -30,12 +30,14 @@ import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.content.ContentAttachmentData
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.file.FileService
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.model.message.MessageType
import im.vector.matrix.android.api.session.room.model.message.getFileUrl
import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
@ -94,7 +96,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
init {
observeRoomSummary()
observeEventDisplayedActions()
observeInvitationState()
observeSummaryState()
cancelableBag += room.loadRoomMembersIfNeeded()
timeline.start()
setState { copy(timeline = this@RoomDetailViewModel.timeline) }
@ -547,7 +549,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
}
}
private fun observeInvitationState() {
private fun observeSummaryState() {
asyncSubscribe(RoomDetailViewState::asyncRoomSummary) { summary ->
if (summary.membership == Membership.INVITE) {
summary.latestEvent?.root?.senderId?.let { senderId ->
@ -556,6 +558,13 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
setState { copy(asyncInviter = Success(it)) }
}
}
if (summary.isVersioned) {
room.getStateEvent(EventType.STATE_ROOM_TOMBSTONE)
?.getClearContent()
?.toModel<RoomTombstoneContent>()?.also {
setState { copy(tombstoneContent = it) }
}
}
}
}

View File

@ -20,6 +20,7 @@ import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent
import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.user.model.User
@ -46,7 +47,8 @@ data class RoomDetailViewState(
val asyncInviter: Async<User> = Uninitialized,
val asyncRoomSummary: Async<RoomSummary> = Uninitialized,
val sendMode: SendMode = SendMode.REGULAR,
val isEncrypted: Boolean = false
val isEncrypted: Boolean = false,
val tombstoneContent: RoomTombstoneContent? = null
) : MvRxState {
constructor(args: RoomDetailArgs) : this(roomId = args.roomId, eventId = args.eventId)

View File

@ -0,0 +1,61 @@
/*
*
* * 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.riotx.features.home.room.detail.timeline.factory
import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan
import im.vector.matrix.android.api.permalinks.PermalinkFactory
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.create.RoomCreateContent
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotx.R
import im.vector.riotx.core.resources.ColorProvider
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotx.features.home.room.detail.timeline.item.RoomCreateItem
import im.vector.riotx.features.home.room.detail.timeline.item.RoomCreateItem_
import me.gujun.android.span.span
import javax.inject.Inject
class RoomCreateItemFactory @Inject constructor(private val colorProvider: ColorProvider,
private val stringProvider: StringProvider) {
fun create(event: TimelineEvent, callback: TimelineEventController.Callback?): RoomCreateItem? {
val createRoomContent = event.root.getClearContent().toModel<RoomCreateContent>()
?: return null
val predecessor = createRoomContent.predecessor ?: return null
val roomLink = PermalinkFactory.createPermalink(predecessor.roomId) ?: return null
val urlSpan = MatrixPermalinkSpan(roomLink, object : MatrixPermalinkSpan.Callback {
override fun onUrlClicked(url: String) {
callback?.onUrlClicked(roomLink)
}
})
val textColorInt = colorProvider.getColor(R.color.riot_primary_text_color_light)
val text = span {
text = stringProvider.getString(R.string.room_tombstone_continuation_description)
append("\n")
append(
stringProvider.getString(R.string.room_tombstone_predecessor_link)
)
}
return RoomCreateItem_()
.text(text)
}
}

View File

@ -33,6 +33,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
private val encryptedItemFactory: EncryptedItemFactory,
private val noticeItemFactory: NoticeItemFactory,
private val defaultItemFactory: DefaultItemFactory,
private val roomCreateItemFactory: RoomCreateItemFactory,
private val avatarRenderer: AvatarRenderer) {
fun create(event: TimelineEvent,
@ -45,6 +46,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
when (event.root.getClearType()) {
EventType.MESSAGE -> messageItemFactory.create(event, nextEvent, highlight, callback)
// State and call
EventType.STATE_ROOM_TOMBSTONE,
EventType.STATE_ROOM_NAME,
EventType.STATE_ROOM_TOPIC,
EventType.STATE_ROOM_MEMBER,
@ -52,7 +54,8 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
EventType.CALL_INVITE,
EventType.CALL_HANGUP,
EventType.CALL_ANSWER -> noticeItemFactory.create(event, highlight, callback)
// State room create
EventType.STATE_ROOM_CREATE -> roomCreateItemFactory.create(event, callback)
// Crypto
EventType.ENCRYPTION -> encryptionItemFactory.create(event, highlight, callback)
EventType.ENCRYPTED -> {
@ -66,8 +69,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
// Unhandled event types (yet)
EventType.STATE_ROOM_THIRD_PARTY_INVITE,
EventType.STICKER,
EventType.STATE_ROOM_CREATE -> defaultItemFactory.create(event, highlight)
EventType.STICKER -> defaultItemFactory.create(event, highlight)
else -> {
//These are just for debug to display hidden event, they should be filtered out in normal mode

View File

@ -22,6 +22,7 @@ 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.*
import im.vector.matrix.android.api.session.room.model.call.CallInviteContent
import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotx.R
import im.vector.riotx.core.resources.StringProvider
@ -37,6 +38,7 @@ class NoticeEventFormatter @Inject constructor(private val stringProvider: Strin
EventType.STATE_ROOM_TOPIC -> formatRoomTopicEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName())
EventType.STATE_ROOM_MEMBER -> formatRoomMemberEvent(timelineEvent.root, timelineEvent.senderName())
EventType.STATE_HISTORY_VISIBILITY -> formatRoomHistoryVisibilityEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName())
EventType.STATE_ROOM_TOMBSTONE -> formatRoomTombstoneEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName())
EventType.CALL_INVITE,
EventType.CALL_HANGUP,
EventType.CALL_ANSWER -> formatCallEvent(timelineEvent.root, timelineEvent.getDisambiguatedDisplayName())
@ -56,6 +58,7 @@ class NoticeEventFormatter @Inject constructor(private val stringProvider: Strin
EventType.CALL_INVITE,
EventType.CALL_HANGUP,
EventType.CALL_ANSWER -> formatCallEvent(event, senderName)
EventType.STATE_ROOM_TOMBSTONE -> formatRoomTombstoneEvent(event, senderName)
else -> {
Timber.v("Type $type not handled by this formatter")
null
@ -72,6 +75,10 @@ class NoticeEventFormatter @Inject constructor(private val stringProvider: Strin
}
}
private fun formatRoomTombstoneEvent(event: Event, senderName: String?): CharSequence? {
return stringProvider.getString(R.string.notice_room_update, senderName)
}
private fun formatRoomTopicEvent(event: Event, senderName: String?): CharSequence? {
val content = event.getClearContent().toModel<RoomTopicContent>() ?: return null
return if (content.topic.isNullOrEmpty()) {

View File

@ -39,7 +39,8 @@ object TimelineDisplayableEvents {
EventType.ENCRYPTION,
EventType.STATE_ROOM_THIRD_PARTY_INVITE,
EventType.STICKER,
EventType.STATE_ROOM_CREATE
EventType.STATE_ROOM_CREATE,
EventType.STATE_ROOM_TOMBSTONE
)
val DEBUG_DISPLAYABLE_TYPES = DISPLAYABLE_TYPES + listOf(

View File

@ -0,0 +1,41 @@
/*
*
* * 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.riotx.features.home.room.detail.timeline.item
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import com.airbnb.epoxy.EpoxyModelWithHolder
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel
@EpoxyModelClass(layout = R.layout.item_timeline_event_create)
abstract class RoomCreateItem : VectorEpoxyModel<RoomCreateItem.Holder>() {
@EpoxyAttribute lateinit var text: CharSequence
override fun bind(holder: Holder) {
holder.description.text = text
}
class Holder : VectorEpoxyHolder() {
val description by bind<TextView>(R.id.roomCreateItemDescription)
}
}

View File

@ -101,4 +101,5 @@
app:layout_constraintTop_toBottomOf="@+id/roomToolbar"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/roomCreateItemDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:background="@drawable/bg_tombstone_predecessor"
android:drawableStart="@drawable/error"
android:drawableLeft="@drawable/error"
android:drawablePadding="16dp"
android:gravity="center|start"
android:minHeight="80dp"
android:padding="16dp"
tools:text="This room is continuation…" />
</FrameLayout>