Lab setting to load rooms at first unread message

Change-Id: I781e5a32d8557939c51387eadf1387cba0d3b149
This commit is contained in:
SpiritCroc 2021-08-02 10:51:15 +02:00
parent 401424ff96
commit 2db315219a
7 changed files with 58 additions and 10 deletions

View File

@ -66,6 +66,16 @@ interface Timeline {
*/
fun setInitialEventId(eventId: String?)
/**
* Offset for the initial event, e.g. if we want to load the event just below said id
*/
fun getInitialEventIdOffset(): Int
/**
* Set the offset for the initial event, e.g. if we want to load the event just below said id
*/
fun setInitialEventIdOffset(offset: Int)
/**
* Check if the timeline can be enriched by paginating.
* @param direction the direction to check in

View File

@ -61,6 +61,7 @@ private const val MIN_FETCHING_COUNT = 30
internal class DefaultTimeline(
private val roomId: String,
private var initialEventId: String? = null,
private var initialEventIdOffset: Int = 0,
private val realmConfiguration: RealmConfiguration,
private val taskExecutor: TaskExecutor,
private val contextOfEventTask: GetContextOfEventTask,
@ -145,7 +146,7 @@ internal class DefaultTimeline(
override fun start() {
if (isStarted.compareAndSet(false, true)) {
Timber.v("Start timeline for roomId: $roomId and eventId: $initialEventId")
Timber.v("Start timeline for roomId: $roomId and eventId: $initialEventId (offset $initialEventIdOffset)")
timelineInput.listeners.add(this)
BACKGROUND_HANDLER.post {
eventDecryptor.start()
@ -195,7 +196,7 @@ internal class DefaultTimeline(
if (isStarted.compareAndSet(true, false)) {
isReady.set(false)
timelineInput.listeners.remove(this)
Timber.v("Dispose timeline for roomId: $roomId and eventId: $initialEventId")
Timber.v("Dispose timeline for roomId: $roomId and eventId: $initialEventId (offset $initialEventIdOffset)")
cancelableBag.cancel()
BACKGROUND_HANDLER.removeCallbacksAndMessages(null)
BACKGROUND_HANDLER.post {
@ -217,6 +218,7 @@ internal class DefaultTimeline(
override fun restartWithEventId(eventId: String?) {
dispose()
initialEventId = eventId
initialEventIdOffset = 0
start()
postSnapshot()
}
@ -229,6 +231,14 @@ internal class DefaultTimeline(
initialEventId = eventId
}
override fun getInitialEventIdOffset(): Int {
return initialEventIdOffset
}
override fun setInitialEventIdOffset(offset: Int) {
initialEventIdOffset = offset
}
override fun getTimelineEventAtIndex(index: Int): TimelineEvent? {
return builtEvents.getOrNull(index)
}

View File

@ -131,11 +131,16 @@ class RoomDetailViewModel @AssistedInject constructor(
Timeline.Listener, ChatEffectManager.Delegate, CallProtocolsChecker.Listener {
private val room = session.getRoom(initialState.roomId)!!
private val eventId = initialState.eventId
private val eventId = initialState.eventId ?: if (vectorPreferences.loadRoomAtFirstUnread()) room.roomSummary()?.readMarkerId else null
private val invisibleEventsObservable = BehaviorRelay.create<RoomDetailAction.TimelineEventTurnsInvisible>()
private val visibleEventsObservable = BehaviorRelay.create<RoomDetailAction.TimelineEventTurnsVisible>()
private var timelineEvents = PublishRelay.create<List<TimelineEvent>>()
val timeline = timelineFactory.createTimeline(viewModelScope, room, eventId)
val timeline = timelineFactory.createTimeline(viewModelScope, room, eventId).apply {
// Target the event just below $eventId in case that it is the readMarkerId
if (initialState.eventId == null && eventId != null) {
setInitialEventIdOffset(-1)
}
}
// Same lifecycle than the ViewModel (survive to screen rotation)
val previewUrlRetriever = PreviewUrlRetriever(session, viewModelScope)
@ -184,8 +189,10 @@ class RoomDetailViewModel @AssistedInject constructor(
observeActiveRoomWidgets()
observePowerLevel()
room.getRoomSummaryLive()
viewModelScope.launch(Dispatchers.IO) {
tryOrNull { room.markAsRead(ReadService.MarkAsReadParams.READ_RECEIPT) }
if (!vectorPreferences.loadRoomAtFirstUnread()) {
viewModelScope.launch(Dispatchers.IO) {
tryOrNull { room.markAsRead(ReadService.MarkAsReadParams.READ_RECEIPT) }
}
}
// Inform the SDK that the room is displayed
viewModelScope.launch(Dispatchers.IO) {
@ -507,6 +514,9 @@ class RoomDetailViewModel @AssistedInject constructor(
mostRecentDisplayedEvent?.root?.eventId?.also {
session.coroutineScope.launch(NonCancellable) {
tryOrNull { room.setReadMarker(it) }
if (vectorPreferences.loadRoomAtFirstUnread()) {
tryOrNull { room.setReadReceipt(it) }
}
}
}
mostRecentDisplayedEvent = null
@ -1312,10 +1322,10 @@ class RoomDetailViewModel @AssistedInject constructor(
}
private fun observeEventDisplayedActions() {
// We are buffering scroll events for one second
// We are buffering scroll events for half a second
// and keep the most recent one to set the read receipt on.
visibleEventsObservable
.buffer(1, TimeUnit.SECONDS)
.buffer(500, TimeUnit.MILLISECONDS)
.filter { it.isNotEmpty() }
.subscribeBy(onNext = { actions ->
val bufferedMostRecentDisplayedEvent = actions.maxByOrNull { it.event.displayIndex }?.event ?: return@subscribeBy

View File

@ -23,6 +23,7 @@ import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import im.vector.app.features.home.room.detail.timeline.item.ItemWithEvents
import org.matrix.android.sdk.api.extensions.tryOrNull
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.math.max
class ScrollOnNewMessageCallback(private val layoutManager: LinearLayoutManager,
private val timelineEventController: TimelineEventController,
@ -32,7 +33,6 @@ class ScrollOnNewMessageCallback(private val layoutManager: LinearLayoutManager,
private var forceScroll = false
var initialForceScroll = false
var initialForceScrollEventId: String? = null
get() = field ?: timelineEventController.timeline?.getInitialEventId()
fun addNewTimelineEventIds(eventIds: List<String>) {
// Disable initial force scroll
@ -57,8 +57,10 @@ class ScrollOnNewMessageCallback(private val layoutManager: LinearLayoutManager,
override fun onInserted(position: Int, count: Int) {
if (initialForceScroll) {
var scrollToEvent = initialForceScrollEventId
var scrollOffset = 0
if (initialForceScrollEventId == null) {
scrollToEvent = timelineEventController.timeline?.getInitialEventId()
scrollOffset = timelineEventController.timeline?.getInitialEventIdOffset() ?: 0
}
if (scrollToEvent == null) {
layoutManager.scrollToPositionWithOffset(0, 0)
@ -67,7 +69,8 @@ class ScrollOnNewMessageCallback(private val layoutManager: LinearLayoutManager,
// Scroll such that the scrolled-to event is moved down 1/3 of the screen.
// To do that, we actually scroll the view above out by 2/3 (since we can only control the distance
// from the bottom of the view, not the top).
layoutManager.scrollToPositionWithOffset(it + 1, parentView.measuredHeight * 2 / 3)
val scrollToPosition = max(it + scrollOffset + 1, 0)
layoutManager.scrollToPositionWithOffset(scrollToPosition, parentView.measuredHeight * 2 / 3)
}
}
return

View File

@ -203,6 +203,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
private const val SETTINGS_USER_COLOR_MODE_DM = "SETTINGS_USER_COLOR_MODE_DM"
private const val SETTINGS_USER_COLOR_MODE_DEFAULT = "SETTINGS_USER_COLOR_MODE_DEFAULT"
private const val SETTINGS_USER_COLOR_MODE_PUBLIC_ROOM = "SETTINGS_USER_COLOR_MODE_PUBLIC_ROOM"
private const val SETTINGS_OPEN_CHATS_AT_FIRST_UNREAD = "SETTINGS_OPEN_CHATS_AT_FIRST_UNREAD"
private const val DID_ASK_TO_ENABLE_SESSION_PUSH = "DID_ASK_TO_ENABLE_SESSION_PUSH"
@ -997,6 +998,11 @@ class VectorPreferences @Inject constructor(private val context: Context) {
}, MatrixItemColorProvider.USER_COLORING_DEFAULT) ?: MatrixItemColorProvider.USER_COLORING_DEFAULT
}
// SC addition
fun loadRoomAtFirstUnread(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_OPEN_CHATS_AT_FIRST_UNREAD, false)
}
/**
* The user enable protecting app access with pin code.
* Currently we use the pin code store to know if the pin is enabled, so this is not used

View File

@ -84,6 +84,9 @@
<string name="setting_sc_accent_color_dark">Accent color for dark themes</string>
<string name="settings_sc_accent_disclaimer">Note that these color settings only apply to SC themes, and not Element themes.</string>
<string name="settings_open_chats_at_first_unread">Open at first unread</string>
<string name="settings_open_chats_at_first_unread_summary">Open chats at the first unread message instead of at the bottom.</string>
<!-- Accent colors -->
<string name="sc_accent_greenlight">Light green</string>
<string name="sc_accent_greendark">Dark green</string>

View File

@ -34,6 +34,12 @@
<!--android:summary="@string/settings_labs_enable_send_voice_summary"-->
<!--android:title="@string/settings_labs_enable_send_voice" />-->
<im.vector.app.core.preference.VectorSwitchPreference
android:defaultValue="false"
android:key="SETTINGS_OPEN_CHATS_AT_FIRST_UNREAD"
android:title="@string/settings_open_chats_at_first_unread"
android:summary="@string/settings_open_chats_at_first_unread_summary" />
<im.vector.app.core.preference.VectorSwitchPreference
android:defaultValue="false"
android:key="SYSTEM_DARK_THEME_PRE_TEN"