diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt index 129c35a17e..c18645ddbd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/RoomSummary.kt @@ -41,7 +41,8 @@ data class RoomSummary( val membership: Membership = Membership.NONE, val versioningState: VersioningState = VersioningState.NONE, val readMarkerId: String? = null, - val userDrafts: List = emptyList() + val userDrafts: List = emptyList(), + var isEncrypted: Boolean ) { val isVersioned: Boolean diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt index 4060e21102..3fa355fe3c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt @@ -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().also { it.localId = localId diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt index eeb340eacb..7d25a846ff 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/RoomSummaryMapper.kt @@ -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 ) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt index 2fa892d874..4c99832b39 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt @@ -43,7 +43,8 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "", var canonicalAlias: String? = null, var aliases: RealmList = RealmList(), // this is required for querying - var flatAliases: String = "" + var flatAliases: String = "", + var isEncrypted: Boolean = false ) : RealmObject() { private var membershipStr: String = Membership.NONE.name diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt index 536484de5a..30d9969f15 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomSummaryUpdater.kt @@ -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() ?.canonicalAlias - val roomAliases = ContentMapper.map(lastAliasesEvent?.content).toModel()?.aliases ?: emptyList() + val roomAliases = ContentMapper.map(lastAliasesEvent?.content).toModel()?.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) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt index 85bab5d706..7835cf7e3e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt @@ -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() + private val listeners = CopyOnWriteArrayList() 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) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index fa483d675c..8c985c27c0 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -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) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 467148302f..c93358a04e 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -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() 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>() - private var timeline = room.createTimeline(eventId, timelineSettings) + var timeline = room.createTimeline(eventId, timelineSettings) + private set private val _viewEvents = PublishDataSource() val viewEvents: DataSource = _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() - ?: 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()?.relatesTo?.inReplyTo?.eventId - ?: state.sendMode.timelineEvent.root.content.toModel()?.relatesTo?.inReplyTo?.eventId + ?: state.sendMode.timelineEvent.root.content.toModel()?.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() } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt index b2ad29668e..3067b7f1b2 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewState.kt @@ -51,11 +51,9 @@ sealed class UnreadState { data class RoomDetailViewState( val roomId: String, val eventId: String?, - val timeline: Timeline? = null, val asyncInviter: Async = Uninitialized, val asyncRoomSummary: Async = Uninitialized, val sendMode: SendMode = SendMode.REGULAR(""), - val isEncrypted: Boolean = false, val tombstoneEvent: Event? = null, val tombstoneEventHandling: Async = Uninitialized, val syncState: SyncState = SyncState.Idle, diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt index 582544ce8a..a08669da3b 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt @@ -95,12 +95,12 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec private val modelCache = arrayListOf() private var currentSnapshot: List = 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 }