Room update: start handling tombstone and room create events [WIP]
This commit is contained in:
parent
6176520805
commit
9e5c70dda3
@ -34,5 +34,6 @@ data class RoomSummary(
|
||||
val notificationCount: Int = 0,
|
||||
val highlightCount: Int = 0,
|
||||
val tags: List<RoomTag> = emptyList(),
|
||||
val membership: Membership = Membership.NONE
|
||||
val membership: Membership = Membership.NONE,
|
||||
val isVersioned: Boolean = false
|
||||
)
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
*
|
||||
* * 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.matrix.android.api.session.room.model.create
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* Content of a m.room.create type event
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class RoomCreateContent(
|
||||
@Json(name = "creator") val creator: String,
|
||||
@Json(name = "room_version") val roomVersion: String?,
|
||||
@Json(name = "predecessor") val predecessor: Predecessor?
|
||||
)
|
||||
|
||||
/**
|
||||
* A link to an old room in case of room versioning
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Predecessor(
|
||||
@Json(name = "room_id") val roomId: String,
|
||||
@Json(name = "event_id") val eventId: String
|
||||
)
|
||||
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
*
|
||||
* * 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.matrix.android.api.session.room.model.tombstone
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* Class to contains Tombstone information
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class RoomTombstoneContent(
|
||||
@Json(name = "body") val body: String? = null,
|
||||
@Json(name = "replacement_room") val replacementRoom: String
|
||||
)
|
@ -17,6 +17,7 @@
|
||||
package im.vector.matrix.android.api.session.room.state
|
||||
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
|
||||
interface StateService {
|
||||
|
||||
@ -25,4 +26,6 @@ interface StateService {
|
||||
*/
|
||||
fun updateTopic(topic: String, callback: MatrixCallback<Unit>)
|
||||
|
||||
fun getStateEvent(eventType: String): Event?
|
||||
|
||||
}
|
@ -61,7 +61,8 @@ internal class RoomSummaryMapper @Inject constructor(
|
||||
highlightCount = roomSummaryEntity.highlightCount,
|
||||
notificationCount = roomSummaryEntity.notificationCount,
|
||||
tags = tags,
|
||||
membership = roomSummaryEntity.membership
|
||||
membership = roomSummaryEntity.membership,
|
||||
isVersioned = roomSummaryEntity.isVersioned
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,8 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
|
||||
var otherMemberIds: RealmList<String> = RealmList(),
|
||||
var notificationCount: Int = 0,
|
||||
var highlightCount: Int = 0,
|
||||
var tags: RealmList<RoomTagEntity> = RealmList()
|
||||
var tags: RealmList<RoomTagEntity> = RealmList(),
|
||||
var isVersioned: Boolean = false
|
||||
) : RealmObject() {
|
||||
|
||||
private var membershipStr: String = Membership.NONE.name
|
||||
|
@ -37,6 +37,7 @@ import im.vector.matrix.android.internal.network.AccessTokenInterceptor
|
||||
import im.vector.matrix.android.internal.network.RetrofitFactory
|
||||
import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater
|
||||
import im.vector.matrix.android.internal.session.room.EventRelationsAggregationUpdater
|
||||
import im.vector.matrix.android.internal.session.room.tombstone.RoomTombstoneEventLiveObserver
|
||||
import im.vector.matrix.android.internal.session.room.prune.EventsPruner
|
||||
import im.vector.matrix.android.internal.util.md5
|
||||
import io.realm.RealmConfiguration
|
||||
@ -128,6 +129,11 @@ internal abstract class SessionModule {
|
||||
@IntoSet
|
||||
abstract fun bindEventRelationsAggregationUpdater(groupSummaryUpdater: EventRelationsAggregationUpdater): LiveEntityObserver
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun bindRoomCreateEventLiveObserver(roomTombstoneEventLiveObserver: RoomTombstoneEventLiveObserver): LiveEntityObserver
|
||||
|
||||
|
||||
@Binds
|
||||
abstract fun bindInitialSyncProgressService(initialSyncProgressService: DefaultInitialSyncProgressService): InitialSyncProgressService
|
||||
|
||||
|
@ -54,7 +54,11 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona
|
||||
|
||||
override fun liveRoomSummaries(): LiveData<List<RoomSummary>> {
|
||||
return monarchy.findAllMappedWithChanges(
|
||||
{ realm -> RoomSummaryEntity.where(realm).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) },
|
||||
{ realm ->
|
||||
RoomSummaryEntity.where(realm)
|
||||
.isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME)
|
||||
.notEqualTo(RoomSummaryEntityFields.IS_VERSIONED, true)
|
||||
},
|
||||
{ roomSummaryMapper.map(it) }
|
||||
)
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.room.Room
|
||||
import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper
|
||||
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||
import im.vector.matrix.android.internal.session.room.membership.DefaultMembershipService
|
||||
import im.vector.matrix.android.internal.session.room.membership.LoadRoomMembersTask
|
||||
import im.vector.matrix.android.internal.session.room.membership.joining.InviteTask
|
||||
@ -40,11 +41,14 @@ import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineSe
|
||||
import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask
|
||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationTask
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import io.realm.RealmConfiguration
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class RoomFactory @Inject constructor(private val context: Context,
|
||||
private val credentials: Credentials,
|
||||
private val monarchy: Monarchy,
|
||||
@SessionDatabase
|
||||
private val realmConfiguration: RealmConfiguration,
|
||||
private val eventFactory: LocalEchoEventFactory,
|
||||
private val roomSummaryMapper: RoomSummaryMapper,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
@ -63,7 +67,7 @@ internal class RoomFactory @Inject constructor(private val context: Context,
|
||||
fun create(roomId: String): Room {
|
||||
val timelineService = DefaultTimelineService(roomId, monarchy, taskExecutor, contextOfEventTask, cryptoService, paginationTask)
|
||||
val sendService = DefaultSendService(context, credentials, roomId, eventFactory, cryptoService, monarchy)
|
||||
val stateService = DefaultStateService(roomId, taskExecutor, sendStateTask)
|
||||
val stateService = DefaultStateService(roomId, realmConfiguration, taskExecutor, sendStateTask)
|
||||
val roomMembersService = DefaultMembershipService(roomId, monarchy, taskExecutor, loadRoomMembersTask, inviteTask, joinRoomTask, leaveRoomTask)
|
||||
val readService = DefaultReadService(roomId, monarchy, taskExecutor, setReadMarkersTask, credentials)
|
||||
val relationService = DefaultRelationService(context,
|
||||
|
@ -17,16 +17,32 @@
|
||||
package im.vector.matrix.android.internal.session.room.state
|
||||
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
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.room.state.StateService
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.query.prev
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultStateService @Inject constructor(private val roomId: String,
|
||||
@SessionDatabase
|
||||
private val realmConfiguration: RealmConfiguration,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val sendStateTask: SendStateTask) : StateService {
|
||||
|
||||
override fun getStateEvent(eventType: String): Event? {
|
||||
return Realm.getInstance(realmConfiguration).use { realm ->
|
||||
EventEntity.where(realm, roomId, eventType).prev()?.asDomain()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateTopic(topic: String, callback: MatrixCallback<Unit>) {
|
||||
val params = SendStateTask.Params(roomId,
|
||||
EventType.STATE_ROOM_TOPIC,
|
||||
@ -39,4 +55,6 @@ internal class DefaultStateService @Inject constructor(private val roomId: Strin
|
||||
.dispatchTo(callback)
|
||||
.executeBy(taskExecutor)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
*
|
||||
* * 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.matrix.android.internal.session.room.tombstone
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
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.tombstone.RoomTombstoneContent
|
||||
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
|
||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||
import im.vector.matrix.android.internal.database.query.types
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.di.SessionDatabase
|
||||
import io.realm.OrderedCollectionChangeSet
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import io.realm.RealmResults
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class RoomTombstoneEventLiveObserver @Inject constructor(@SessionDatabase
|
||||
realmConfiguration: RealmConfiguration)
|
||||
: RealmLiveEntityObserver<EventEntity>(realmConfiguration) {
|
||||
|
||||
override val query = Monarchy.Query<EventEntity> {
|
||||
EventEntity.types(it, listOf(EventType.STATE_ROOM_TOMBSTONE))
|
||||
}
|
||||
|
||||
override fun onChange(results: RealmResults<EventEntity>, changeSet: OrderedCollectionChangeSet) {
|
||||
changeSet.insertions
|
||||
.asSequence()
|
||||
.mapNotNull {
|
||||
results[it]?.asDomain()
|
||||
}
|
||||
.toList()
|
||||
.also {
|
||||
handleRoomTombstoneEvents(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRoomTombstoneEvents(tombstoneEvents: List<Event>) = Realm.getInstance(realmConfiguration).use {
|
||||
it.executeTransactionAsync { realm ->
|
||||
for (event in tombstoneEvents) {
|
||||
if (event.roomId == null) continue
|
||||
val createRoomContent = event.getClearContent().toModel<RoomTombstoneContent>()
|
||||
if (createRoomContent?.replacementRoom == null) continue
|
||||
|
||||
val predecessorRoomSummary = RoomSummaryEntity.where(realm, event.roomId).findFirst()
|
||||
?: RoomSummaryEntity(event.roomId)
|
||||
predecessorRoomSummary.isVersioned = true
|
||||
realm.insertOrUpdate(predecessorRoomSummary)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -146,8 +146,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
||||
roomEntity,
|
||||
roomSync.timeline.events,
|
||||
roomSync.timeline.prevToken,
|
||||
roomSync.timeline.limited,
|
||||
0
|
||||
roomSync.timeline.limited
|
||||
)
|
||||
roomEntity.addOrUpdate(chunkEntity)
|
||||
}
|
||||
@ -195,15 +194,18 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
|
||||
roomEntity: RoomEntity,
|
||||
eventList: List<Event>,
|
||||
prevToken: String? = null,
|
||||
isLimited: Boolean = true,
|
||||
stateIndexOffset: Int = 0): ChunkEntity {
|
||||
isLimited: Boolean = true): ChunkEntity {
|
||||
|
||||
val lastChunk = ChunkEntity.findLastLiveChunkFromRoom(realm, roomEntity.roomId)
|
||||
var stateIndexOffset = 0
|
||||
val chunkEntity = if (!isLimited && lastChunk != null) {
|
||||
lastChunk
|
||||
} else {
|
||||
realm.createObject<ChunkEntity>().apply { this.prevToken = prevToken }
|
||||
}
|
||||
if (isLimited && lastChunk != null) {
|
||||
stateIndexOffset = lastChunk.lastStateIndex(PaginationDirection.FORWARDS)
|
||||
}
|
||||
lastChunk?.isLastForward = false
|
||||
chunkEntity.isLastForward = true
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
<string name="notice_room_visibility_world_readable">anyone.</string>
|
||||
<string name="notice_room_visibility_unknown">unknown (%s).</string>
|
||||
<string name="notice_end_to_end">%1$s turned on end-to-end encryption (%2$s)</string>
|
||||
<string name="notice_room_update">%s upgraded this room.</string>
|
||||
|
||||
<string name="notice_requested_voip_conference">%1$s requested a VoIP conference</string>
|
||||
<string name="notice_voip_started">VoIP conference started</string>
|
||||
|
@ -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) {
|
||||
|
@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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()) {
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -101,4 +101,5 @@
|
||||
app:layout_constraintTop_toBottomOf="@+id/roomToolbar"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
24
vector/src/main/res/layout/item_timeline_event_create.xml
Normal file
24
vector/src/main/res/layout/item_timeline_event_create.xml
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user