mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-05-17 13:45:54 +08:00
fix: make feed notifications robust against subscription changes
This commit is contained in:
@ -140,11 +140,13 @@ object PreferenceKeys {
|
||||
|
||||
// Internally saved data / not a preference
|
||||
const val ERROR_LOG = "error_log"
|
||||
const val LAST_STREAM_VIDEO_ID = "last_stream_video_id"
|
||||
const val LAST_WATCHED_FEED_TIME = "last_watched_feed_time"
|
||||
const val AUTH_PREF_FILE = "auth"
|
||||
const val IMAGE_PROXY_URL = "image_proxy_url"
|
||||
const val SELECTED_CHANNEL_GROUP = "selected_channel_group"
|
||||
const val SELECTED_DOWNLOAD_SORT_TYPE = "selected_download_sort_type"
|
||||
const val LAST_SHOWN_INFO_MESSAGE_VERSION_CODE = "last_shown_info_message_version"
|
||||
|
||||
// use the helper methods at PreferenceHelper to access these
|
||||
const val LAST_USER_SEEN_FEED_TIME = "last_watched_feed_time"
|
||||
const val LAST_REFRESHED_FEED_TIME = "last_refreshed_feed_time"
|
||||
}
|
||||
|
@ -96,24 +96,24 @@ object PreferenceHelper {
|
||||
authSettings.edit { putString(PreferenceKeys.USERNAME, newValue) }
|
||||
}
|
||||
|
||||
fun setLastSeenVideoId(videoId: String) {
|
||||
putString(PreferenceKeys.LAST_STREAM_VIDEO_ID, videoId)
|
||||
}
|
||||
|
||||
fun getLastSeenVideoId(): String {
|
||||
return getString(PreferenceKeys.LAST_STREAM_VIDEO_ID, "")
|
||||
}
|
||||
|
||||
fun updateLastFeedWatchedTime(time: Long) {
|
||||
fun updateLastFeedWatchedTime(time: Long, seenByUser: Boolean) {
|
||||
// only update the time if the time is newer
|
||||
// this avoids cases, where the user last saw an older video, which had already been seen,
|
||||
// causing all following video to be incorrectly marked as unseen again
|
||||
if (getLastCheckedFeedTime() < time)
|
||||
putLong(PreferenceKeys.LAST_WATCHED_FEED_TIME, time)
|
||||
if (getLastCheckedFeedTime(false) < time)
|
||||
putLong(PreferenceKeys.LAST_REFRESHED_FEED_TIME, time)
|
||||
|
||||
// this value holds the last time the user opened the subscriptions feed
|
||||
// whereas [LAST_REFRESHED_FEED_TIME] considers the last time the feed was loaded,
|
||||
// which could also be possible in the background (e.g. via notifications)
|
||||
if (seenByUser && getLastCheckedFeedTime(true) < time)
|
||||
putLong(PreferenceKeys.LAST_USER_SEEN_FEED_TIME, time)
|
||||
}
|
||||
|
||||
fun getLastCheckedFeedTime(): Long {
|
||||
return getLong(PreferenceKeys.LAST_WATCHED_FEED_TIME, 0)
|
||||
fun getLastCheckedFeedTime(seenByUser: Boolean): Long {
|
||||
val key =
|
||||
if (seenByUser) PreferenceKeys.LAST_USER_SEEN_FEED_TIME else PreferenceKeys.LAST_REFRESHED_FEED_TIME
|
||||
return getLong(key, 0)
|
||||
}
|
||||
|
||||
fun saveErrorLog(log: String) {
|
||||
|
@ -37,7 +37,6 @@ import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.databinding.ActivityMainBinding
|
||||
import com.github.libretube.enums.ImportFormat
|
||||
import com.github.libretube.extensions.toID
|
||||
import com.github.libretube.helpers.ImportHelper
|
||||
import com.github.libretube.helpers.IntentHelper
|
||||
import com.github.libretube.helpers.NavBarHelper
|
||||
@ -240,9 +239,12 @@ class MainActivity : BaseActivity() {
|
||||
subscriptionsViewModel.fetchSubscriptions(this)
|
||||
|
||||
subscriptionsViewModel.videoFeed.observe(this) { feed ->
|
||||
val lastCheckedFeedTime = PreferenceHelper.getLastCheckedFeedTime(seenByUser = true)
|
||||
val lastSeenVideoIndex = feed.orEmpty()
|
||||
.indexOfFirst { PreferenceHelper.getLastSeenVideoId() == it.url?.toID() }
|
||||
.filter { !it.isUpcoming }
|
||||
.indexOfFirst { it.uploaded <= lastCheckedFeedTime }
|
||||
if (lastSeenVideoIndex < 1) return@observe
|
||||
|
||||
binding.bottomNav.getOrCreateBadge(R.id.subscriptionsFragment).apply {
|
||||
number = lastSeenVideoIndex
|
||||
backgroundColor = ThemeHelper.getThemeColor(
|
||||
|
@ -140,13 +140,17 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
|
||||
// any other feed updates are caused by manual refreshing and thus should reset the scroll
|
||||
// position to zero
|
||||
var alreadyShowedFeedOnce = false
|
||||
viewModel.videoFeed.observe(viewLifecycleOwner) {
|
||||
if (!viewModel.isCurrentTabSubChannels && it != null) {
|
||||
viewModel.videoFeed.observe(viewLifecycleOwner) { feed ->
|
||||
if (!viewModel.isCurrentTabSubChannels && feed != null) {
|
||||
lifecycleScope.launch {
|
||||
showFeed(!alreadyShowedFeedOnce)
|
||||
}
|
||||
alreadyShowedFeedOnce = true
|
||||
}
|
||||
|
||||
feed?.firstOrNull { !it.isUpcoming }?.uploaded?.let {
|
||||
PreferenceHelper.updateLastFeedWatchedTime(it, true)
|
||||
}
|
||||
}
|
||||
|
||||
// restore the scroll position, same conditions as above
|
||||
@ -384,7 +388,7 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
|
||||
|
||||
// add an "all caught up item"
|
||||
if (selectedSortOrder == 0) {
|
||||
val lastCheckedFeedTime = PreferenceHelper.getLastCheckedFeedTime()
|
||||
val lastCheckedFeedTime = PreferenceHelper.getLastCheckedFeedTime(seenByUser = true)
|
||||
val caughtUpIndex =
|
||||
feed.indexOfFirst { it.uploaded <= lastCheckedFeedTime && !it.isUpcoming }
|
||||
if (caughtUpIndex > 0 && !feed[caughtUpIndex - 1].isUpcoming) {
|
||||
@ -404,10 +408,6 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
|
||||
|
||||
binding.toggleSubs.text = getString(R.string.subscriptions)
|
||||
|
||||
feed.firstOrNull { !it.isUpcoming }?.uploaded?.let {
|
||||
PreferenceHelper.updateLastFeedWatchedTime(it)
|
||||
}
|
||||
|
||||
binding.subRefresh.isRefreshing = false
|
||||
|
||||
feedAdapter.submitList(sortedFeed) {
|
||||
|
@ -11,7 +11,6 @@ import com.github.libretube.api.SubscriptionHelper
|
||||
import com.github.libretube.api.obj.StreamItem
|
||||
import com.github.libretube.api.obj.Subscription
|
||||
import com.github.libretube.extensions.TAG
|
||||
import com.github.libretube.extensions.toID
|
||||
import com.github.libretube.extensions.toastFromMainDispatcher
|
||||
import com.github.libretube.helpers.PreferenceHelper
|
||||
import com.github.libretube.repo.FeedProgress
|
||||
@ -41,9 +40,8 @@ class SubscriptionsViewModel : ViewModel() {
|
||||
return@launch
|
||||
}
|
||||
this@SubscriptionsViewModel.videoFeed.postValue(videoFeed)
|
||||
if (videoFeed.isNotEmpty()) {
|
||||
// save the last recent video to the prefs for the notification worker
|
||||
PreferenceHelper.setLastSeenVideoId(videoFeed[0].url!!.toID())
|
||||
videoFeed.firstOrNull { !it.isUpcoming }?.uploaded?.let {
|
||||
PreferenceHelper.updateLastFeedWatchedTime(it, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,18 +83,15 @@ class NotificationWorker(appContext: Context, parameters: WorkerParameters) :
|
||||
val videoFeed = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
SubscriptionHelper.getFeed(forceRefresh = true)
|
||||
}
|
||||
}.filter { !it.isUpcoming }
|
||||
} catch (e: Exception) {
|
||||
return false
|
||||
}
|
||||
|
||||
val lastUserSeenVideoId = PreferenceHelper.getLastSeenVideoId()
|
||||
val mostRecentStreamId = videoFeed.firstOrNull()?.url?.toID() ?: return true
|
||||
// save the latest streams that got notified about
|
||||
PreferenceHelper.setLastSeenVideoId(mostRecentStreamId)
|
||||
val lastFeedCheckMillis = PreferenceHelper.getLastCheckedFeedTime(seenByUser = false)
|
||||
|
||||
// first time notifications are enabled or no new video available
|
||||
if (lastUserSeenVideoId.isEmpty() || lastUserSeenVideoId == mostRecentStreamId) return true
|
||||
if (lastFeedCheckMillis == 0L || videoFeed.none { it.uploaded > lastFeedCheckMillis }) return true
|
||||
|
||||
val channelsToIgnore = PreferenceHelper.getIgnorableNotificationChannels()
|
||||
val enableShortsNotification =
|
||||
@ -102,7 +99,7 @@ class NotificationWorker(appContext: Context, parameters: WorkerParameters) :
|
||||
|
||||
val channelGroups = videoFeed.asSequence()
|
||||
// filter the new videos until the last seen video in the feed
|
||||
.takeWhile { it.url!!.toID() != lastUserSeenVideoId }
|
||||
.filter { it.uploaded > lastFeedCheckMillis }
|
||||
// don't show notifications for shorts videos if not enabled
|
||||
.filter { enableShortsNotification || !it.isShort }
|
||||
// hide for notifications unsubscribed channels
|
||||
@ -110,6 +107,9 @@ class NotificationWorker(appContext: Context, parameters: WorkerParameters) :
|
||||
// group the new streams by the uploader
|
||||
.groupBy { it.uploaderUrl!!.toID() }
|
||||
|
||||
// update the last feed check time in order to not show the same notification again
|
||||
PreferenceHelper.updateLastFeedWatchedTime(videoFeed.first().uploaded, seenByUser = false)
|
||||
|
||||
// return if the previous video didn't get found or all the channels have notifications disabled
|
||||
if (channelGroups.isEmpty()) return true
|
||||
|
||||
@ -226,6 +226,6 @@ class NotificationWorker(appContext: Context, parameters: WorkerParameters) :
|
||||
|
||||
companion object {
|
||||
private const val INTENT_FLAGS = Intent.FLAG_ACTIVITY_CLEAR_TOP or
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user