mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 18:35:40 +03:00
Work on timeline
This commit is contained in:
parent
99c523b710
commit
f9487f8995
10 changed files with 112 additions and 109 deletions
|
@ -41,7 +41,8 @@ data class RoomSummary(
|
|||
val membership: Membership = Membership.NONE,
|
||||
val versioningState: VersioningState = VersioningState.NONE,
|
||||
val readMarkerId: String? = null,
|
||||
val userDrafts: List<UserDraft> = emptyList()
|
||||
val userDrafts: List<UserDraft> = emptyList(),
|
||||
var isEncrypted: Boolean
|
||||
) {
|
||||
|
||||
val isVersioned: Boolean
|
||||
|
|
|
@ -92,7 +92,7 @@ internal fun ChunkEntity.add(roomId: String,
|
|||
}
|
||||
}
|
||||
|
||||
val isUnlinked = isUnlinked
|
||||
val isChunkUnlinked = isUnlinked
|
||||
val localId = TimelineEventEntity.nextId(realm)
|
||||
val eventId = event.eventId ?: ""
|
||||
val senderId = event.senderId ?: ""
|
||||
|
@ -121,7 +121,7 @@ internal fun ChunkEntity.add(roomId: String,
|
|||
this.stateIndex = currentStateIndex
|
||||
this.displayIndex = currentDisplayIndex
|
||||
this.sendState = SendState.SYNCED
|
||||
this.isUnlinked = isUnlinked
|
||||
this.isUnlinked = isChunkUnlinked
|
||||
}
|
||||
val eventEntity = realm.createObject<TimelineEventEntity>().also {
|
||||
it.localId = localId
|
||||
|
|
|
@ -70,7 +70,8 @@ internal class RoomSummaryMapper @Inject constructor(
|
|||
readMarkerId = roomSummaryEntity.readMarkerId,
|
||||
userDrafts = roomSummaryEntity.userDrafts?.userDrafts?.map { DraftMapper.map(it) } ?: emptyList(),
|
||||
canonicalAlias = roomSummaryEntity.canonicalAlias,
|
||||
aliases = roomSummaryEntity.aliases.toList()
|
||||
aliases = roomSummaryEntity.aliases.toList(),
|
||||
isEncrypted = roomSummaryEntity.isEncrypted
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,8 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
|
|||
var canonicalAlias: String? = null,
|
||||
var aliases: RealmList<String> = RealmList(),
|
||||
// this is required for querying
|
||||
var flatAliases: String = ""
|
||||
var flatAliases: String = "",
|
||||
var isEncrypted: Boolean = false
|
||||
) : RealmObject() {
|
||||
|
||||
private var membershipStr: String = Membership.NONE.name
|
||||
|
|
|
@ -93,10 +93,12 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId
|
|||
val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).prev()
|
||||
val lastCanonicalAliasEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_CANONICAL_ALIAS).prev()
|
||||
val lastAliasesEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_ALIASES).prev()
|
||||
val encryptionEvent = EventEntity.where(realm, roomId, EventType.ENCRYPTION).prev()
|
||||
|
||||
roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0
|
||||
// avoid this call if we are sure there are unread events
|
||||
|| !isEventRead(monarchy, userId, roomId, latestPreviewableEvent?.eventId)
|
||||
// avoid this call if we are sure there are unread events
|
||||
|| !isEventRead(monarchy, userId, roomId, latestPreviewableEvent?.eventId)
|
||||
|
||||
|
||||
roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(roomId).toString()
|
||||
roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(roomId)
|
||||
|
@ -105,10 +107,12 @@ internal class RoomSummaryUpdater @Inject constructor(@UserId private val userId
|
|||
roomSummaryEntity.canonicalAlias = ContentMapper.map(lastCanonicalAliasEvent?.content).toModel<RoomCanonicalAliasContent>()
|
||||
?.canonicalAlias
|
||||
|
||||
val roomAliases = ContentMapper.map(lastAliasesEvent?.content).toModel<RoomAliasesContent>()?.aliases ?: emptyList()
|
||||
val roomAliases = ContentMapper.map(lastAliasesEvent?.content).toModel<RoomAliasesContent>()?.aliases
|
||||
?: emptyList()
|
||||
roomSummaryEntity.aliases.clear()
|
||||
roomSummaryEntity.aliases.addAll(roomAliases)
|
||||
roomSummaryEntity.flatAliases = roomAliases.joinToString(separator = "|", prefix = "|")
|
||||
roomSummaryEntity.isEncrypted = encryptionEvent != null
|
||||
|
||||
if (updateMembers) {
|
||||
val otherRoomMembers = RoomMembers(realm, roomId)
|
||||
|
|
|
@ -52,8 +52,8 @@ import io.realm.RealmQuery
|
|||
import io.realm.RealmResults
|
||||
import io.realm.Sort
|
||||
import timber.log.Timber
|
||||
import java.util.Collections
|
||||
import java.util.UUID
|
||||
import java.util.*
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.collections.ArrayList
|
||||
|
@ -77,11 +77,9 @@ internal class DefaultTimeline(
|
|||
private val hiddenReadReceipts: TimelineHiddenReadReceipts
|
||||
) : Timeline, TimelineHiddenReadReceipts.Delegate {
|
||||
|
||||
private companion object {
|
||||
val BACKGROUND_HANDLER = createBackgroundHandler("TIMELINE_DB_THREAD")
|
||||
}
|
||||
val backgroundHandler = createBackgroundHandler("TIMELINE_DB_THREAD_${System.currentTimeMillis()}")
|
||||
|
||||
private val listeners = ArrayList<Timeline.Listener>()
|
||||
private val listeners = CopyOnWriteArrayList<Timeline.Listener>()
|
||||
private val isStarted = AtomicBoolean(false)
|
||||
private val isReady = AtomicBoolean(false)
|
||||
private val mainHandler = createUIHandler()
|
||||
|
@ -137,7 +135,7 @@ internal class DefaultTimeline(
|
|||
// Public methods ******************************************************************************
|
||||
|
||||
override fun paginate(direction: Timeline.Direction, count: Int) {
|
||||
BACKGROUND_HANDLER.post {
|
||||
backgroundHandler.post {
|
||||
if (!canPaginate(direction)) {
|
||||
return@post
|
||||
}
|
||||
|
@ -165,7 +163,7 @@ internal class DefaultTimeline(
|
|||
override fun start() {
|
||||
if (isStarted.compareAndSet(false, true)) {
|
||||
Timber.v("Start timeline for roomId: $roomId and eventId: $initialEventId")
|
||||
BACKGROUND_HANDLER.post {
|
||||
backgroundHandler.post {
|
||||
eventDecryptor.start()
|
||||
val realm = Realm.getInstance(realmConfiguration)
|
||||
backgroundRealm.set(realm)
|
||||
|
@ -199,8 +197,8 @@ internal class DefaultTimeline(
|
|||
isReady.set(false)
|
||||
Timber.v("Dispose timeline for roomId: $roomId and eventId: $initialEventId")
|
||||
cancelableBag.cancel()
|
||||
BACKGROUND_HANDLER.removeCallbacksAndMessages(null)
|
||||
BACKGROUND_HANDLER.post {
|
||||
backgroundHandler.removeCallbacksAndMessages(null)
|
||||
backgroundHandler.post {
|
||||
roomEntity?.sendingTimelineEvents?.removeAllChangeListeners()
|
||||
if (this::eventRelations.isInitialized) {
|
||||
eventRelations.removeAllChangeListeners()
|
||||
|
@ -288,20 +286,20 @@ internal class DefaultTimeline(
|
|||
return hasMoreInCache(direction) || !hasReachedEnd(direction)
|
||||
}
|
||||
|
||||
override fun addListener(listener: Timeline.Listener) = synchronized(listeners) {
|
||||
override fun addListener(listener: Timeline.Listener): Boolean {
|
||||
if (listeners.contains(listener)) {
|
||||
return false
|
||||
}
|
||||
listeners.add(listener).also {
|
||||
return listeners.add(listener).also {
|
||||
postSnapshot()
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeListener(listener: Timeline.Listener) = synchronized(listeners) {
|
||||
listeners.remove(listener)
|
||||
override fun removeListener(listener: Timeline.Listener): Boolean {
|
||||
return listeners.remove(listener)
|
||||
}
|
||||
|
||||
override fun removeAllListeners() = synchronized(listeners) {
|
||||
override fun removeAllListeners() {
|
||||
listeners.clear()
|
||||
}
|
||||
|
||||
|
@ -497,9 +495,9 @@ internal class DefaultTimeline(
|
|||
return
|
||||
}
|
||||
val params = PaginationTask.Params(roomId = roomId,
|
||||
from = token,
|
||||
direction = direction.toPaginationDirection(),
|
||||
limit = limit)
|
||||
from = token,
|
||||
direction = direction.toPaginationDirection(),
|
||||
limit = limit)
|
||||
|
||||
Timber.v("Should fetch $limit items $direction")
|
||||
cancelableBag += paginationTask
|
||||
|
@ -516,7 +514,7 @@ internal class DefaultTimeline(
|
|||
}
|
||||
TokenChunkEventPersistor.Result.SHOULD_FETCH_MORE ->
|
||||
// Database won't be updated, so we force pagination request
|
||||
BACKGROUND_HANDLER.post {
|
||||
backgroundHandler.post {
|
||||
executePaginationTask(direction, limit)
|
||||
}
|
||||
}
|
||||
|
@ -575,7 +573,7 @@ internal class DefaultTimeline(
|
|||
val timelineEvent = buildTimelineEvent(eventEntity)
|
||||
|
||||
if (timelineEvent.isEncrypted()
|
||||
&& timelineEvent.root.mxDecryptionResult == null) {
|
||||
&& timelineEvent.root.mxDecryptionResult == null) {
|
||||
timelineEvent.root.eventId?.let { eventDecryptor.requestDecryption(it) }
|
||||
}
|
||||
|
||||
|
@ -649,17 +647,15 @@ internal class DefaultTimeline(
|
|||
}
|
||||
|
||||
private fun postSnapshot() {
|
||||
BACKGROUND_HANDLER.post {
|
||||
backgroundHandler.post {
|
||||
if (isReady.get().not()) {
|
||||
return@post
|
||||
}
|
||||
updateLoadingStates(filteredEvents)
|
||||
val snapshot = createSnapshot()
|
||||
val runnable = Runnable {
|
||||
synchronized(listeners) {
|
||||
listeners.forEach {
|
||||
it.onTimelineUpdated(snapshot)
|
||||
}
|
||||
listeners.forEach {
|
||||
it.onTimelineUpdated(snapshot)
|
||||
}
|
||||
}
|
||||
debouncer.debounce("post_snapshot", runnable, 50)
|
||||
|
@ -671,10 +667,8 @@ internal class DefaultTimeline(
|
|||
return
|
||||
}
|
||||
val runnable = Runnable {
|
||||
synchronized(listeners) {
|
||||
listeners.forEach {
|
||||
it.onTimelineFailure(throwable)
|
||||
}
|
||||
listeners.forEach {
|
||||
it.onTimelineFailure(throwable)
|
||||
}
|
||||
}
|
||||
mainHandler.post(runnable)
|
||||
|
|
|
@ -40,6 +40,7 @@ import androidx.core.util.Pair
|
|||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.forEach
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.observe
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -71,6 +72,7 @@ import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
|
|||
import im.vector.matrix.android.api.util.MatrixItem
|
||||
import im.vector.matrix.android.api.util.toMatrixItem
|
||||
import im.vector.matrix.android.api.util.toRoomAliasMatrixItem
|
||||
import im.vector.matrix.rx.rx
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.dialogs.withColoredButton
|
||||
import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer
|
||||
|
@ -225,6 +227,8 @@ class RoomDetailFragment @Inject constructor(
|
|||
setupNotificationView()
|
||||
setupJumpToReadMarkerView()
|
||||
setupJumpToBottomView()
|
||||
|
||||
|
||||
roomDetailViewModel.subscribe { renderState(it) }
|
||||
textComposerViewModel.subscribe { renderTextComposerState(it) }
|
||||
roomDetailViewModel.sendMessageResultLiveData.observeEvent(this) { renderSendMessageResult(it) }
|
||||
|
@ -325,12 +329,10 @@ class RoomDetailFragment @Inject constructor(
|
|||
jumpToBottomView.setOnClickListener {
|
||||
roomDetailViewModel.handle(RoomDetailAction.ExitTrackingUnreadMessagesState)
|
||||
jumpToBottomView.visibility = View.INVISIBLE
|
||||
withState(roomDetailViewModel) { state ->
|
||||
if (state.timeline?.isLive == false) {
|
||||
state.timeline.restartWithEventId(null)
|
||||
} else {
|
||||
layoutManager.scrollToPosition(0)
|
||||
}
|
||||
if (!roomDetailViewModel.timeline.isLive) {
|
||||
roomDetailViewModel.timeline.restartWithEventId(null)
|
||||
} else {
|
||||
layoutManager.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -343,9 +345,9 @@ class RoomDetailFragment @Inject constructor(
|
|||
AlertDialog.Builder(requireActivity())
|
||||
.setTitle(R.string.dialog_title_error)
|
||||
.setMessage(getString(R.string.error_file_too_big,
|
||||
error.filename,
|
||||
TextUtils.formatFileSize(requireContext(), error.fileSizeInBytes),
|
||||
TextUtils.formatFileSize(requireContext(), error.homeServerLimitInBytes)
|
||||
error.filename,
|
||||
TextUtils.formatFileSize(requireContext(), error.fileSizeInBytes),
|
||||
TextUtils.formatFileSize(requireContext(), error.homeServerLimitInBytes)
|
||||
))
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
|
@ -431,7 +433,8 @@ class RoomDetailFragment @Inject constructor(
|
|||
composerLayout.sendButton.setContentDescription(getString(descriptionRes))
|
||||
|
||||
avatarRenderer.render(
|
||||
MatrixItem.UserItem(event.root.senderId ?: "", event.getDisambiguatedDisplayName(), event.senderAvatar),
|
||||
MatrixItem.UserItem(event.root.senderId
|
||||
?: "", event.getDisambiguatedDisplayName(), event.senderAvatar),
|
||||
composerLayout.composerRelatedMessageAvatar
|
||||
)
|
||||
composerLayout.expand {
|
||||
|
@ -449,7 +452,7 @@ class RoomDetailFragment @Inject constructor(
|
|||
// Ignore update to avoid saving a draft
|
||||
composerLayout.composerEditText.setText(text)
|
||||
composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text?.length
|
||||
?: 0)
|
||||
?: 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -481,6 +484,9 @@ class RoomDetailFragment @Inject constructor(
|
|||
// PRIVATE METHODS *****************************************************************************
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
timelineEventController.callback = this
|
||||
timelineEventController.timeline = roomDetailViewModel.timeline
|
||||
|
||||
val epoxyVisibilityTracker = EpoxyVisibilityTracker()
|
||||
epoxyVisibilityTracker.attach(recyclerView)
|
||||
layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, true)
|
||||
|
@ -514,8 +520,6 @@ class RoomDetailFragment @Inject constructor(
|
|||
}
|
||||
})
|
||||
|
||||
timelineEventController.callback = this
|
||||
|
||||
if (vectorPreferences.swipeToReplyIsEnabled()) {
|
||||
val quickReplyHandler = object : RoomMessageTouchHelperCallback.QuickReplayHandler {
|
||||
override fun performQuickReplyOnHolder(model: EpoxyModel<*>) {
|
||||
|
@ -789,11 +793,12 @@ class RoomDetailFragment @Inject constructor(
|
|||
}
|
||||
|
||||
private fun renderState(state: RoomDetailViewState) {
|
||||
Timber.v("Render state summary complete: ${state.asyncRoomSummary.complete}")
|
||||
renderRoomSummary(state)
|
||||
val summary = state.asyncRoomSummary()
|
||||
val inviter = state.asyncInviter()
|
||||
if (summary?.membership == Membership.JOIN) {
|
||||
scrollOnHighlightedEventCallback.timeline = state.timeline
|
||||
scrollOnHighlightedEventCallback.timeline = roomDetailViewModel.timeline
|
||||
timelineEventController.update(state)
|
||||
inviteView.visibility = View.GONE
|
||||
val uid = session.myUserId
|
||||
|
@ -808,9 +813,10 @@ class RoomDetailFragment @Inject constructor(
|
|||
} else if (state.asyncInviter.complete) {
|
||||
vectorBaseActivity.finish()
|
||||
}
|
||||
val isRoomEncrypted = summary?.isEncrypted ?: false
|
||||
if (state.tombstoneEvent == null) {
|
||||
composerLayout.visibility = View.VISIBLE
|
||||
composerLayout.setRoomEncrypted(state.isEncrypted)
|
||||
composerLayout.setRoomEncrypted(isRoomEncrypted)
|
||||
notificationAreaView.render(NotificationAreaView.State.Hidden)
|
||||
} else {
|
||||
composerLayout.visibility = View.GONE
|
||||
|
@ -1312,7 +1318,7 @@ class RoomDetailFragment @Inject constructor(
|
|||
val startToCompose = composerLayout.composerEditText.text.isNullOrBlank()
|
||||
|
||||
if (startToCompose
|
||||
&& userId == session.myUserId) {
|
||||
&& userId == session.myUserId) {
|
||||
// Empty composer, current user: start an emote
|
||||
composerLayout.composerEditText.setText(Command.EMOTE.command + " ")
|
||||
composerLayout.composerEditText.setSelection(Command.EMOTE.length)
|
||||
|
|
|
@ -20,14 +20,18 @@ import android.net.Uri
|
|||
import androidx.annotation.IdRes
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.airbnb.mvrx.*
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.jakewharton.rxrelay2.BehaviorRelay
|
||||
import com.jakewharton.rxrelay2.PublishRelay
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.MatrixPatterns
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.isImageMessage
|
||||
|
@ -89,20 +93,21 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
private val visibleEventsObservable = BehaviorRelay.create<RoomDetailAction.TimelineEventTurnsVisible>()
|
||||
private val timelineSettings = if (userPreferencesProvider.shouldShowHiddenEvents()) {
|
||||
TimelineSettings(30,
|
||||
filterEdits = false,
|
||||
filterTypes = true,
|
||||
allowedTypes = TimelineDisplayableEvents.DEBUG_DISPLAYABLE_TYPES,
|
||||
buildReadReceipts = userPreferencesProvider.shouldShowReadReceipts())
|
||||
filterEdits = false,
|
||||
filterTypes = true,
|
||||
allowedTypes = TimelineDisplayableEvents.DEBUG_DISPLAYABLE_TYPES,
|
||||
buildReadReceipts = userPreferencesProvider.shouldShowReadReceipts())
|
||||
} else {
|
||||
TimelineSettings(30,
|
||||
filterEdits = true,
|
||||
filterTypes = true,
|
||||
allowedTypes = TimelineDisplayableEvents.DISPLAYABLE_TYPES,
|
||||
buildReadReceipts = userPreferencesProvider.shouldShowReadReceipts())
|
||||
filterEdits = true,
|
||||
filterTypes = true,
|
||||
allowedTypes = TimelineDisplayableEvents.DISPLAYABLE_TYPES,
|
||||
buildReadReceipts = userPreferencesProvider.shouldShowReadReceipts())
|
||||
}
|
||||
|
||||
private var timelineEvents = PublishRelay.create<List<TimelineEvent>>()
|
||||
private var timeline = room.createTimeline(eventId, timelineSettings)
|
||||
var timeline = room.createTimeline(eventId, timelineSettings)
|
||||
private set
|
||||
|
||||
private val _viewEvents = PublishDataSource<RoomDetailViewEvents>()
|
||||
val viewEvents: DataSource<RoomDetailViewEvents> = _viewEvents
|
||||
|
@ -138,18 +143,17 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
}
|
||||
|
||||
init {
|
||||
timeline.start()
|
||||
timeline.addListener(this)
|
||||
observeRoomSummary()
|
||||
observeSummaryState()
|
||||
getUnreadState()
|
||||
observeSyncState()
|
||||
observeRoomSummary()
|
||||
observeEventDisplayedActions()
|
||||
observeSummaryState()
|
||||
observeDrafts()
|
||||
observeUnreadState()
|
||||
room.getRoomSummaryLive()
|
||||
room.rx().loadRoomMembersIfNeeded().subscribeLogError().disposeOnClear()
|
||||
timeline.addListener(this)
|
||||
timeline.start()
|
||||
setState { copy(timeline = this@RoomDetailViewModel.timeline) }
|
||||
|
||||
// Inform the SDK that the room is displayed
|
||||
session.onRoomDisplayed(initialState.roomId)
|
||||
}
|
||||
|
@ -233,23 +237,23 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
copy(
|
||||
// Create a sendMode from a draft and retrieve the TimelineEvent
|
||||
sendMode = when (draft) {
|
||||
is UserDraft.REGULAR -> SendMode.REGULAR(draft.text)
|
||||
is UserDraft.QUOTE -> {
|
||||
room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent ->
|
||||
SendMode.QUOTE(timelineEvent, draft.text)
|
||||
}
|
||||
}
|
||||
is UserDraft.REPLY -> {
|
||||
room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent ->
|
||||
SendMode.REPLY(timelineEvent, draft.text)
|
||||
}
|
||||
}
|
||||
is UserDraft.EDIT -> {
|
||||
room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent ->
|
||||
SendMode.EDIT(timelineEvent, draft.text)
|
||||
}
|
||||
}
|
||||
} ?: SendMode.REGULAR("")
|
||||
is UserDraft.REGULAR -> SendMode.REGULAR(draft.text)
|
||||
is UserDraft.QUOTE -> {
|
||||
room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent ->
|
||||
SendMode.QUOTE(timelineEvent, draft.text)
|
||||
}
|
||||
}
|
||||
is UserDraft.REPLY -> {
|
||||
room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent ->
|
||||
SendMode.REPLY(timelineEvent, draft.text)
|
||||
}
|
||||
}
|
||||
is UserDraft.EDIT -> {
|
||||
room.getTimeLineEvent(draft.linkedEventId)?.let { timelineEvent ->
|
||||
SendMode.EDIT(timelineEvent, draft.text)
|
||||
}
|
||||
}
|
||||
} ?: SendMode.REGULAR("")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -258,7 +262,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
|
||||
private fun handleTombstoneEvent(action: RoomDetailAction.HandleTombstoneEvent) {
|
||||
val tombstoneContent = action.event.getClearContent().toModel<RoomTombstoneContent>()
|
||||
?: return
|
||||
?: return
|
||||
|
||||
val roomId = tombstoneContent.replacementRoom ?: ""
|
||||
val isRoomJoined = session.getRoom(roomId)?.roomSummary()?.membership == Membership.JOIN
|
||||
|
@ -310,7 +314,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
else -> false
|
||||
}
|
||||
|
||||
// PRIVATE METHODS *****************************************************************************
|
||||
// PRIVATE METHODS *****************************************************************************
|
||||
|
||||
private fun handleSendMessage(action: RoomDetailAction.SendMessage) {
|
||||
withState { state ->
|
||||
|
@ -396,7 +400,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
is SendMode.EDIT -> {
|
||||
// is original event a reply?
|
||||
val inReplyTo = state.sendMode.timelineEvent.root.getClearContent().toModel<MessageContent>()?.relatesTo?.inReplyTo?.eventId
|
||||
?: state.sendMode.timelineEvent.root.content.toModel<EncryptedEventContent>()?.relatesTo?.inReplyTo?.eventId
|
||||
?: state.sendMode.timelineEvent.root.content.toModel<EncryptedEventContent>()?.relatesTo?.inReplyTo?.eventId
|
||||
if (inReplyTo != null) {
|
||||
// TODO check if same content?
|
||||
room.getTimeLineEvent(inReplyTo)?.let {
|
||||
|
@ -405,13 +409,13 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
} else {
|
||||
val messageContent: MessageContent? =
|
||||
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||
val existingBody = messageContent?.body ?: ""
|
||||
if (existingBody != action.text) {
|
||||
room.editTextMessage(state.sendMode.timelineEvent.root.eventId ?: "",
|
||||
messageContent?.type ?: MessageType.MSGTYPE_TEXT,
|
||||
action.text,
|
||||
action.autoMarkdown)
|
||||
messageContent?.type ?: MessageType.MSGTYPE_TEXT,
|
||||
action.text,
|
||||
action.autoMarkdown)
|
||||
} else {
|
||||
Timber.w("Same message content, do not send edition")
|
||||
}
|
||||
|
@ -422,7 +426,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
is SendMode.QUOTE -> {
|
||||
val messageContent: MessageContent? =
|
||||
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
|
||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||
?: state.sendMode.timelineEvent.root.getClearContent().toModel()
|
||||
val textMsg = messageContent?.body
|
||||
|
||||
val finalText = legacyRiotQuoteText(textMsg, action.text.toString())
|
||||
|
@ -538,7 +542,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
when (val tooBigFile = attachments.find { it.size > maxUploadFileSize }) {
|
||||
null -> room.sendMedias(attachments)
|
||||
else -> _fileTooBigEvent.postValue(LiveEvent(FileTooBigError(tooBigFile.name
|
||||
?: tooBigFile.path, tooBigFile.size, maxUploadFileSize)))
|
||||
?: tooBigFile.path, tooBigFile.size, maxUploadFileSize)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -728,7 +732,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
.filter { it.isNotEmpty() }
|
||||
.subscribeBy(onNext = { actions ->
|
||||
val bufferedMostRecentDisplayedEvent = actions.maxBy { it.event.displayIndex }?.event
|
||||
?: return@subscribeBy
|
||||
?: return@subscribeBy
|
||||
val globalMostRecentDisplayedEvent = mostRecentDisplayedEvent
|
||||
if (trackUnreadMessages.get()) {
|
||||
if (globalMostRecentDisplayedEvent == null) {
|
||||
|
@ -791,10 +795,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
room.rx().liveRoomSummary()
|
||||
.unwrap()
|
||||
.execute { async ->
|
||||
copy(
|
||||
asyncRoomSummary = async,
|
||||
isEncrypted = room.isEncrypted()
|
||||
)
|
||||
copy(asyncRoomSummary = async)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -880,7 +881,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
|
||||
override fun onCleared() {
|
||||
timeline.dispose()
|
||||
timeline.removeListener(this)
|
||||
timeline.removeAllListeners()
|
||||
super.onCleared()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,11 +51,9 @@ sealed class UnreadState {
|
|||
data class RoomDetailViewState(
|
||||
val roomId: String,
|
||||
val eventId: String?,
|
||||
val timeline: Timeline? = null,
|
||||
val asyncInviter: Async<User> = Uninitialized,
|
||||
val asyncRoomSummary: Async<RoomSummary> = Uninitialized,
|
||||
val sendMode: SendMode = SendMode.REGULAR(""),
|
||||
val isEncrypted: Boolean = false,
|
||||
val tombstoneEvent: Event? = null,
|
||||
val tombstoneEventHandling: Async<String> = Uninitialized,
|
||||
val syncState: SyncState = SyncState.Idle,
|
||||
|
|
|
@ -95,12 +95,12 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||
private val modelCache = arrayListOf<CacheItemData?>()
|
||||
private var currentSnapshot: List<TimelineEvent> = emptyList()
|
||||
private var inSubmitList: Boolean = false
|
||||
private var timeline: Timeline? = null
|
||||
private var unreadState: UnreadState = UnreadState.Unknown
|
||||
private var positionOfReadMarker: Int? = null
|
||||
private var eventIdToHighlight: String? = null
|
||||
|
||||
var callback: Callback? = null
|
||||
var timeline: Timeline? = null
|
||||
|
||||
private val listUpdateCallback = object : ListUpdateCallback {
|
||||
|
||||
|
@ -176,10 +176,6 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||
}
|
||||
|
||||
fun update(viewState: RoomDetailViewState) {
|
||||
if (timeline?.timelineID != viewState.timeline?.timelineID) {
|
||||
timeline = viewState.timeline
|
||||
timeline?.addListener(this)
|
||||
}
|
||||
var requestModelBuild = false
|
||||
if (eventIdToHighlight != viewState.highlightedEventId) {
|
||||
// Clear cache to force a refresh
|
||||
|
@ -205,6 +201,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||
|
||||
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
|
||||
super.onAttachedToRecyclerView(recyclerView)
|
||||
timeline?.addListener(this)
|
||||
timelineMediaSizeProvider.recyclerView = recyclerView
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue