ReadMarker: extract from ViewModel the jump to read marker visibility logic as it's easier to deal with.
This commit is contained in:
parent
05d09bf950
commit
c6d01fbcf4
@ -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.riotx.features.home.room.detail
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import im.vector.riotx.core.di.ScreenScope
|
||||
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ScreenScope
|
||||
class ReadMarkerHelper @Inject constructor() {
|
||||
|
||||
lateinit var timelineEventController: TimelineEventController
|
||||
lateinit var layoutManager: LinearLayoutManager
|
||||
var callback: Callback? = null
|
||||
|
||||
private var state: RoomDetailViewState? = null
|
||||
|
||||
fun updateState(state: RoomDetailViewState) {
|
||||
this.state = state
|
||||
checkJumpToReadMarkerVisibility()
|
||||
}
|
||||
|
||||
fun onTimelineScrolled() {
|
||||
checkJumpToReadMarkerVisibility()
|
||||
}
|
||||
|
||||
private fun checkJumpToReadMarkerVisibility() {
|
||||
val nonNullState = this.state ?: return
|
||||
val lastVisibleItem = layoutManager.findLastVisibleItemPosition()
|
||||
val readMarkerId = nonNullState.asyncRoomSummary()?.readMarkerId
|
||||
if (readMarkerId == null) {
|
||||
callback?.onVisibilityUpdated(false, null)
|
||||
}
|
||||
val positionOfReadMarker = timelineEventController.searchPositionOfEvent(readMarkerId)
|
||||
Timber.v("Position of readMarker: $positionOfReadMarker")
|
||||
Timber.v("Position of lastVisibleItem: $lastVisibleItem")
|
||||
if (positionOfReadMarker == null) {
|
||||
if (nonNullState.timeline?.isLive == true && lastVisibleItem > 0) {
|
||||
callback?.onVisibilityUpdated(true, readMarkerId)
|
||||
} else {
|
||||
callback?.onVisibilityUpdated(false, readMarkerId)
|
||||
}
|
||||
} else {
|
||||
if (positionOfReadMarker > lastVisibleItem) {
|
||||
callback?.onVisibilityUpdated(true, readMarkerId)
|
||||
} else {
|
||||
callback?.onVisibilityUpdated(false, readMarkerId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface Callback {
|
||||
fun onVisibilityUpdated(show: Boolean, readMarkerId: String?)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -227,6 +227,7 @@ class RoomDetailFragment :
|
||||
@Inject lateinit var errorFormatter: ErrorFormatter
|
||||
@Inject lateinit var eventHtmlRenderer: EventHtmlRenderer
|
||||
@Inject lateinit var vectorPreferences: VectorPreferences
|
||||
@Inject lateinit var readMarkerHelper: ReadMarkerHelper
|
||||
|
||||
private lateinit var scrollOnNewMessageCallback: ScrollOnNewMessageCallback
|
||||
private lateinit var scrollOnHighlightedEventCallback: ScrollOnHighlightedEventCallback
|
||||
@ -479,6 +480,13 @@ class RoomDetailFragment :
|
||||
it.dispatchTo(scrollOnNewMessageCallback)
|
||||
it.dispatchTo(scrollOnHighlightedEventCallback)
|
||||
}
|
||||
readMarkerHelper.timelineEventController = timelineEventController
|
||||
readMarkerHelper.layoutManager = layoutManager
|
||||
readMarkerHelper.callback = object : ReadMarkerHelper.Callback {
|
||||
override fun onVisibilityUpdated(show: Boolean, readMarkerId: String?) {
|
||||
jumpToReadMarkerView.render(show, readMarkerId)
|
||||
}
|
||||
}
|
||||
recyclerView.setController(timelineEventController)
|
||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
|
||||
@ -486,6 +494,7 @@ class RoomDetailFragment :
|
||||
if (recyclerView.scrollState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
updateJumpToBottomViewVisibility()
|
||||
}
|
||||
readMarkerHelper.onTimelineScrolled()
|
||||
}
|
||||
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
@ -698,6 +707,7 @@ class RoomDetailFragment :
|
||||
}
|
||||
|
||||
private fun renderState(state: RoomDetailViewState) {
|
||||
readMarkerHelper.updateState(state)
|
||||
renderRoomSummary(state)
|
||||
val summary = state.asyncRoomSummary()
|
||||
val inviter = state.asyncInviter()
|
||||
@ -726,7 +736,6 @@ class RoomDetailFragment :
|
||||
composerLayout.visibility = View.GONE
|
||||
notificationAreaView.render(NotificationAreaView.State.Tombstone(state.tombstoneEvent))
|
||||
}
|
||||
jumpToReadMarkerView.render(state.showJumpToReadMarker, summary?.readMarkerId)
|
||||
}
|
||||
|
||||
private fun renderRoomSummary(state: RoomDetailViewState) {
|
||||
@ -1151,7 +1160,7 @@ class RoomDetailFragment :
|
||||
roomDetailViewModel.process(RoomDetailActions.RejectInvite)
|
||||
}
|
||||
|
||||
// JumpToReadMarkerView.Callback
|
||||
// JumpToReadMarkerView.Callback
|
||||
|
||||
override fun onJumpToReadMarkerClicked(readMarkerId: String) {
|
||||
roomDetailViewModel.process(RoomDetailActions.NavigateToEvent(readMarkerId, false))
|
||||
|
@ -119,7 +119,6 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||
observeRoomSummary()
|
||||
observeEventDisplayedActions()
|
||||
observeSummaryState()
|
||||
observeJumpToReadMarkerViewVisibility()
|
||||
observeReadMarkerVisibility()
|
||||
observeDrafts()
|
||||
room.rx().loadRoomMembersIfNeeded().subscribeLogError().disposeOnClear()
|
||||
@ -702,45 +701,6 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||
.disposeOnClear()
|
||||
}
|
||||
|
||||
private fun observeJumpToReadMarkerViewVisibility() {
|
||||
Observable.combineLatest(
|
||||
room.rx().liveRoomSummary()
|
||||
.map {
|
||||
val readMarkerId = it.readMarkerId
|
||||
if (readMarkerId == null) {
|
||||
Option.empty()
|
||||
} else {
|
||||
val readMarkerIndex = room.getTimeLineEvent(readMarkerId)?.displayIndex
|
||||
?: Int.MIN_VALUE
|
||||
Option.just(readMarkerIndex)
|
||||
}
|
||||
}
|
||||
.distinctUntilChanged(),
|
||||
visibleEventsObservable.distinctUntilChanged(),
|
||||
isEventVisibleObservable { it.hasReadMarker }.startWith(false).takeUntil { it },
|
||||
Function3<Option<Int>, RoomDetailActions.TimelineEventTurnsVisible, Boolean, Boolean> { readMarkerIndex, currentVisibleEvent, isReadMarkerViewVisible ->
|
||||
if (readMarkerIndex.isEmpty() || isReadMarkerViewVisible) {
|
||||
false
|
||||
} else {
|
||||
val currentVisibleEventPosition = currentVisibleEvent.event.displayIndex
|
||||
readMarkerIndex.getOrElse { Int.MIN_VALUE } < currentVisibleEventPosition
|
||||
}
|
||||
}
|
||||
)
|
||||
.distinctUntilChanged()
|
||||
.subscribe {
|
||||
setState { copy(showJumpToReadMarker = it) }
|
||||
}
|
||||
.disposeOnClear()
|
||||
}
|
||||
|
||||
private fun isEventVisibleObservable(filterEvent: (TimelineEvent) -> Boolean): Observable<Boolean> {
|
||||
return Observable.merge(
|
||||
visibleEventsObservable.filter { filterEvent(it.event) }.map { true },
|
||||
invisibleEventsObservable.filter { filterEvent(it.event) }.map { false }
|
||||
)
|
||||
}
|
||||
|
||||
private fun observeRoomSummary() {
|
||||
room.rx().liveRoomSummary()
|
||||
.execute { async ->
|
||||
|
@ -52,7 +52,6 @@ data class RoomDetailViewState(
|
||||
val tombstoneEvent: Event? = null,
|
||||
val tombstoneEventHandling: Async<String> = Uninitialized,
|
||||
val syncState: SyncState = SyncState.IDLE,
|
||||
val showJumpToReadMarker: Boolean = false,
|
||||
val highlightedEventId: String? = null,
|
||||
val hideReadMarker: Boolean = false
|
||||
) : MvRxState {
|
||||
|
@ -56,7 +56,6 @@ class ScrollOnHighlightedEventCallback(private val layoutManager: LinearLayoutMa
|
||||
// Do not scroll it item is already visible
|
||||
if (positionToScroll !in firstVisibleItem..lastVisibleItem) {
|
||||
Timber.v("Scroll to $positionToScroll")
|
||||
// Note: Offset will be from the bottom, since the layoutManager is reversed
|
||||
layoutManager.scrollToPosition(positionToScroll)
|
||||
}
|
||||
scheduledEventId.set(null)
|
||||
|
Loading…
x
Reference in New Issue
Block a user