@@ -106,6 +106,7 @@ import com.nononsenseapps.feeder.ApplicationCoroutineScope
106
106
import com.nononsenseapps.feeder.R
107
107
import com.nononsenseapps.feeder.archmodel.FeedItemStyle
108
108
import com.nononsenseapps.feeder.archmodel.FeedType
109
+ import com.nononsenseapps.feeder.db.FAR_FUTURE
109
110
import com.nononsenseapps.feeder.db.room.FeedItemCursor
110
111
import com.nononsenseapps.feeder.db.room.ID_SAVED_ARTICLES
111
112
import com.nononsenseapps.feeder.db.room.ID_UNSET
@@ -142,12 +143,11 @@ import com.nononsenseapps.feeder.ui.compose.utils.ImmutableHolder
142
143
import com.nononsenseapps.feeder.ui.compose.utils.addMargin
143
144
import com.nononsenseapps.feeder.ui.compose.utils.isCompactDevice
144
145
import com.nononsenseapps.feeder.ui.compose.utils.onKeyEventLikeEscape
145
- import com.nononsenseapps.feeder.ui.compose.utils.rememberApplicationCoroutineScope
146
146
import com.nononsenseapps.feeder.ui.compose.utils.rememberIsItemMostlyVisible
147
- import com.nononsenseapps.feeder.ui.compose.utils.rememberIsItemVisible
148
147
import com.nononsenseapps.feeder.util.ActivityLauncher
149
148
import com.nononsenseapps.feeder.util.ToastMaker
150
149
import com.nononsenseapps.feeder.util.emailBugReportIntent
150
+ import kotlinx.coroutines.CoroutineScope
151
151
import kotlinx.coroutines.Dispatchers
152
152
import kotlinx.coroutines.delay
153
153
import kotlinx.coroutines.launch
@@ -1135,18 +1135,15 @@ fun FeedListContent(
1135
1135
val previewItem = pagedFeedItems[itemIndex] ? : PLACEHOLDER_ITEM
1136
1136
1137
1137
if (viewState.markAsReadOnScroll && previewItem.unread) {
1138
- val visible: Boolean by listState.rememberIsItemVisible(
1139
- key = previewItem.id,
1140
- )
1141
1138
val mostlyVisible: Boolean by listState.rememberIsItemMostlyVisible(
1142
1139
key = previewItem.id,
1143
1140
screenHeightPx = screenHeightPx,
1144
1141
)
1145
1142
MarkItemAsReadOnScroll (
1146
1143
itemId = previewItem.id,
1147
- visible = visible,
1148
1144
mostlyVisible = mostlyVisible,
1149
1145
currentFeedOrTag = viewState.currentFeedOrTag,
1146
+ coroutineScope = coroutineScope,
1150
1147
markAsRead = markAsUnread,
1151
1148
)
1152
1149
}
@@ -1335,18 +1332,15 @@ fun FeedGridContent(
1335
1332
// Very important that items don't change size or disappear when scrolling
1336
1333
// Placeholder will have no id
1337
1334
if (previewItem.id > ID_UNSET && viewState.markAsReadOnScroll && previewItem.unread) {
1338
- val visible: Boolean by gridState.rememberIsItemVisible(
1339
- key = previewItem.id,
1340
- )
1341
1335
val mostlyVisible: Boolean by gridState.rememberIsItemMostlyVisible(
1342
1336
key = previewItem.id,
1343
1337
screenHeightPx = screenHeightPx,
1344
1338
)
1345
1339
MarkItemAsReadOnScroll (
1346
1340
itemId = previewItem.id,
1347
- visible = visible,
1348
1341
mostlyVisible = mostlyVisible,
1349
1342
currentFeedOrTag = viewState.currentFeedOrTag,
1343
+ coroutineScope = coroutineScope,
1350
1344
markAsRead = markAsUnread,
1351
1345
)
1352
1346
}
@@ -1445,57 +1439,56 @@ fun <T : Any> LazyPagingItems<T>.rememberLazyListState(): LazyListState {
1445
1439
1446
1440
/* *
1447
1441
* @param itemId id of item to mark as read
1448
- * @param visible if item is visible at all
1449
1442
* @param mostlyVisible if item is mostly visible
1450
1443
* @param currentFeedOrTag current feed or tag at the time of display
1444
+ * @param coroutineScope a scope which will be cancelled when navigated away from feed screen
1451
1445
* @param markAsRead action to run
1452
1446
*/
1453
1447
@Composable
1454
1448
fun MarkItemAsReadOnScroll (
1455
1449
itemId : Long ,
1456
- visible : Boolean ,
1457
1450
mostlyVisible : Boolean ,
1458
1451
currentFeedOrTag : FeedOrTag ,
1452
+ coroutineScope : CoroutineScope ,
1459
1453
markAsRead : (Long , Boolean , FeedOrTag ? ) -> Unit ,
1460
1454
) {
1461
- val coroutineScope = rememberApplicationCoroutineScope()
1462
-
1463
- var debounced by remember {
1464
- mutableStateOf(false )
1455
+ var visibleTime by remember {
1456
+ mutableStateOf(FAR_FUTURE )
1465
1457
}
1466
- if (mostlyVisible) {
1467
- LaunchedEffect (null ) {
1468
- delay(800 )
1469
- debounced = true
1458
+ LaunchedEffect (mostlyVisible) {
1459
+ if (mostlyVisible && visibleTime == FAR_FUTURE ) {
1460
+ visibleTime = Instant .now()
1470
1461
}
1471
1462
}
1472
- if (visible) {
1473
- DisposableEffect (null ) {
1474
- onDispose {
1475
- if (debounced) {
1476
- coroutineScope.launch( Dispatchers . IO ) {
1477
- // Why Coroutine? Why a delay?
1478
- // Because this scope will be cancelled if the screen
1479
- // is navigated away from and I only want things to be marked
1480
- // during scroll - not during navigation.
1481
- // Navigating between feeds is a special case, which is
1482
- // why the currentFeedOrTag needs to be passed to the
1483
- // view model.
1484
- @Suppress( " UNNECESSARYVARIABLE " )
1485
- val feedOrTag = currentFeedOrTag
1486
- delay( 100 )
1487
- markAsRead(
1488
- itemId,
1489
- false ,
1490
- feedOrTag ,
1491
- )
1492
- }
1463
+
1464
+ DisposableEffect (visibleTime ) {
1465
+ onDispose {
1466
+ // Check time BEFORE delaying action
1467
+ if (visibleTime.isBefore( Instant .now().minusMillis( REQUIRED_VISIBLE_TIME_FOR_MARK_AS_READ )) ) {
1468
+ coroutineScope.launch( Dispatchers . IO ) {
1469
+ // Why Coroutine? Why a delay?
1470
+ // Because this scope will be cancelled if the screen
1471
+ // is navigated away from and I only want things to be marked
1472
+ // during scroll - not during navigation.
1473
+ // Navigating between feeds is a special case, which is
1474
+ // why the currentFeedOrTag needs to be passed to the
1475
+ // view model.
1476
+ @Suppress( " UNNECESSARYVARIABLE " )
1477
+ val feedOrTag = currentFeedOrTag
1478
+ delay( 100 )
1479
+ markAsRead(
1480
+ itemId ,
1481
+ false ,
1482
+ feedOrTag,
1483
+ )
1493
1484
}
1494
1485
}
1495
1486
}
1496
1487
}
1497
1488
}
1498
1489
1490
+ private const val REQUIRED_VISIBLE_TIME_FOR_MARK_AS_READ = 500L
1491
+
1499
1492
private val PLACEHOLDER_ITEM =
1500
1493
FeedListItem (
1501
1494
id = ID_UNSET ,
0 commit comments