Save the user's reading position in the home timeline (#3614)

- Add a field to AccountEntity to hold the reading position
- Provide a method to save in the viewmodel to save the position
- Save the position when TimelineFragment pauses

Does not restore the position yet.
This commit is contained in:
Nik Clayton 2023-05-08 13:57:17 +02:00 committed by GitHub
parent 0c02dd18bf
commit 367240a612
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 1050 additions and 5 deletions

File diff suppressed because it is too large Load diff

View file

@ -77,7 +77,6 @@ import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import java.io.IOException
@ -578,6 +577,17 @@ class TimelineFragment :
private var talkBackWasEnabled = false
override fun onPause() {
super.onPause()
(binding.recyclerView.layoutManager as? LinearLayoutManager)?.findFirstVisibleItemPosition()?.let { position ->
if (position != RecyclerView.NO_POSITION) {
adapter.snapshot().getOrNull(position)?.id?.let { statusId ->
viewModel.saveReadingPosition(statusId)
}
}
}
}
override fun onResume() {
super.onResume()
val a11yManager =

View file

@ -289,6 +289,14 @@ class CachedTimelineViewModel @Inject constructor(
}
}
override fun saveReadingPosition(statusId: String) {
accountManager.activeAccount?.let { account ->
Log.d(TAG, "Saving position at: $statusId")
account.lastVisibleHomeTimelineStatusId = statusId
accountManager.saveAccount(account)
}
}
override suspend fun invalidate() {
// invalidating when we don't have statuses yet can cause empty timelines because it cancels the network load
if (db.timelineDao().getStatusCount(accountManager.activeAccount!!.id) > 0) {
@ -297,6 +305,7 @@ class CachedTimelineViewModel @Inject constructor(
}
companion object {
private const val TAG = "CachedTimelineViewModel"
private const val MAX_STATUSES_IN_CACHE = 1000
}
}

View file

@ -255,6 +255,10 @@ class NetworkTimelineViewModel @Inject constructor(
}
}
override fun saveReadingPosition(statusId: String) {
/** Does nothing for non-cached timelines */
}
override suspend fun invalidate() {
currentSource?.invalidate()
}

View file

@ -182,6 +182,9 @@ abstract class TimelineViewModel(
abstract fun clearWarning(status: StatusViewData.Concrete)
/** Saves the user's reading position so it can be restored later */
abstract fun saveReadingPosition(statusId: String)
/** Triggered when currently displayed data must be reloaded. */
protected abstract suspend fun invalidate()

View file

@ -82,7 +82,13 @@ data class AccountEntity(
var pushPubKey: String = "",
var pushPrivKey: String = "",
var pushAuth: String = "",
var pushServerKey: String = ""
var pushServerKey: String = "",
/**
* ID of the status at the top of the visible list in the home timeline when the
* user navigated away.
*/
var lastVisibleHomeTimelineStatusId: String? = null
) {
val identifier: String

View file

@ -16,6 +16,7 @@
package com.keylesspalace.tusky.db;
import androidx.annotation.NonNull;
import androidx.room.AutoMigration;
import androidx.room.Database;
import androidx.room.RoomDatabase;
import androidx.room.migration.Migration;
@ -29,9 +30,20 @@ import java.io.File;
/**
* DB version & declare DAO
*/
@Database(entities = { DraftEntity.class, AccountEntity.class, InstanceEntity.class, TimelineStatusEntity.class,
TimelineAccountEntity.class, ConversationEntity.class
}, version = 48)
@Database(
entities = {
DraftEntity.class,
AccountEntity.class,
InstanceEntity.class,
TimelineStatusEntity.class,
TimelineAccountEntity.class,
ConversationEntity.class
},
version = 49,
autoMigrations = {
@AutoMigration(from = 48, to = 49)
}
)
public abstract class AppDatabase extends RoomDatabase {
public abstract AccountDao accountDao();