Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ AND (
(SELECT pub_date FROM feed_item WHERE url_hash = :targetItemId) IS NULL
AND (
pub_date IS NOT NULL
OR (pub_date IS NULL AND url_hash >= :targetItemId)
OR (pub_date IS NULL AND url_hash > :targetItemId)
)
)
OR (
Expand All @@ -331,7 +331,7 @@ AND (
pub_date > (SELECT pub_date FROM feed_item WHERE url_hash = :targetItemId)
OR (
pub_date = (SELECT pub_date FROM feed_item WHERE url_hash = :targetItemId)
AND url_hash >= :targetItemId
AND url_hash > :targetItemId
)
)
)
Expand Down Expand Up @@ -362,7 +362,7 @@ AND (
(
(SELECT pub_date FROM feed_item WHERE url_hash = :targetItemId) IS NULL
AND pub_date IS NULL
AND url_hash <= :targetItemId
AND url_hash < :targetItemId
)
OR (
(SELECT pub_date FROM feed_item WHERE url_hash = :targetItemId) IS NOT NULL
Expand All @@ -371,7 +371,7 @@ AND (
OR pub_date < (SELECT pub_date FROM feed_item WHERE url_hash = :targetItemId)
OR (
pub_date = (SELECT pub_date FROM feed_item WHERE url_hash = :targetItemId)
AND url_hash <= :targetItemId
AND url_hash < :targetItemId
)
)
)
Expand Down Expand Up @@ -404,7 +404,7 @@ AND (
(SELECT pub_date FROM feed_item WHERE url_hash = :targetItemId) IS NULL
AND (
pub_date IS NOT NULL
OR (pub_date IS NULL AND url_hash >= :targetItemId)
OR (pub_date IS NULL AND url_hash > :targetItemId)
)
)
OR (
Expand All @@ -414,7 +414,7 @@ AND (
pub_date > (SELECT pub_date FROM feed_item WHERE url_hash = :targetItemId)
OR (
pub_date = (SELECT pub_date FROM feed_item WHERE url_hash = :targetItemId)
AND url_hash >= :targetItemId
AND url_hash > :targetItemId
)
)
)
Expand Down Expand Up @@ -446,7 +446,7 @@ AND (
(
(SELECT pub_date FROM feed_item WHERE url_hash = :targetItemId) IS NULL
AND pub_date IS NULL
AND url_hash <= :targetItemId
AND url_hash < :targetItemId
)
OR (
(SELECT pub_date FROM feed_item WHERE url_hash = :targetItemId) IS NOT NULL
Expand All @@ -455,7 +455,7 @@ AND (
OR pub_date < (SELECT pub_date FROM feed_item WHERE url_hash = :targetItemId)
OR (
pub_date = (SELECT pub_date FROM feed_item WHERE url_hash = :targetItemId)
AND url_hash <= :targetItemId
AND url_hash < :targetItemId
)
)
)
Expand Down
34 changes: 21 additions & 13 deletions e2e/maestro/android/regression/168-mark-read-filter-boundaries.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ appId: com.prof18.feedflow.debug
- tapOn:
id: drawer_bookmarks
- assertVisible:
id: article_row_e2e_article_bookmarked_unread
id: article_row_e2e_article_bookmarked_read
- longPressOn:
id: article_row_e2e_article_bookmarked_unread
id: article_row_e2e_article_bookmarked_read
- tapOn:
text: "Mark all above as read"
text: "Mark all below as read"
- tapOn: "Confirm"
- tapOn:
id: drawer_menu_button
Expand All @@ -26,8 +26,13 @@ appId: com.prof18.feedflow.debug
id: drawer_menu_button
- tapOn:
id: drawer_timeline
- scrollUntilVisible:
element:
id: article_row_e2e_article_newest_unread
direction: DOWN
timeout: 10000
- assertVisible:
id: article_row_e2e_article_old
id: article_row_e2e_article_newest_unread

- openLink: feedflow://e2e/reset-and-seed?profile=oldest-first
- assertVisible: "E2E seed complete"
Expand All @@ -38,20 +43,23 @@ appId: com.prof18.feedflow.debug
- tapOn:
id: drawer_bookmarks
- assertVisible:
id: article_row_e2e_article_bookmarked_read
id: article_row_e2e_article_bookmarked_unread
- longPressOn:
id: article_row_e2e_article_bookmarked_read
id: article_row_e2e_article_bookmarked_unread
- tapOn:
text: "Mark all below as read"
text: "Mark all above as read"
- tapOn: "Confirm"
- tapOn:
id: drawer_menu_button
- tapOn:
id: drawer_read
- assertVisible:
id: article_row_e2e_article_bookmarked_read
- assertNotVisible:
id: article_row_e2e_article_bookmarked_unread
- tapOn:
id: drawer_menu_button
- tapOn:
id: drawer_timeline
- scrollUntilVisible:
element:
id: article_row_e2e_article_newest_unread
direction: DOWN
timeout: 10000
- assertVisible:
id: article_row_e2e_article_newest_unread
id: article_row_e2e_article_old
34 changes: 21 additions & 13 deletions e2e/maestro/ios/regression/168-mark-read-filter-boundaries.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ appId: com.prof18.feedflow.dev
- tapOn:
id: drawer_bookmarks
- assertVisible:
id: article_row_e2e_article_bookmarked_unread
id: article_row_e2e_article_bookmarked_read
- longPressOn:
id: article_row_e2e_article_bookmarked_unread
id: article_row_e2e_article_bookmarked_read
- tapOn:
text: "Mark all above as read"
text: "Mark all below as read"
- tapOn: "Confirm"
- tapOn:
id: drawer_menu_button
Expand All @@ -28,8 +28,13 @@ appId: com.prof18.feedflow.dev
id: drawer_menu_button
- tapOn:
id: drawer_timeline
- scrollUntilVisible:
element:
id: article_row_e2e_article_newest_unread
direction: DOWN
timeout: 10000
- assertVisible:
id: article_row_e2e_article_old
id: article_row_e2e_article_newest_unread

- openLink: feedflow://e2e/reset-and-seed?profile=oldest-first
- tapOn:
Expand All @@ -42,20 +47,23 @@ appId: com.prof18.feedflow.dev
- tapOn:
id: drawer_bookmarks
- assertVisible:
id: article_row_e2e_article_bookmarked_read
id: article_row_e2e_article_bookmarked_unread
- longPressOn:
id: article_row_e2e_article_bookmarked_read
id: article_row_e2e_article_bookmarked_unread
- tapOn:
text: "Mark all below as read"
text: "Mark all above as read"
- tapOn: "Confirm"
- tapOn:
id: drawer_menu_button
- tapOn:
id: drawer_read
- assertVisible:
id: article_row_e2e_article_bookmarked_read
- assertNotVisible:
id: article_row_e2e_article_bookmarked_unread
- tapOn:
id: drawer_menu_button
- tapOn:
id: drawer_timeline
- scrollUntilVisible:
element:
id: article_row_e2e_article_newest_unread
direction: DOWN
timeout: 10000
- assertVisible:
id: article_row_e2e_article_newest_unread
id: article_row_e2e_article_old
2 changes: 1 addition & 1 deletion e2e/maestro/maestro-e2e-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ Run for broader functional coverage. Flow files live in `e2e/maestro/{android,io
| REG-165 | `165-ios-reader-scroll-retention.yaml` | `reader-mode` | iOS | Reader mode keeps its scroll position and exposes Listen after a background/foreground cycle. Backgrounding makes iOS render the app-switcher snapshot in both appearances, flipping the SwiftUI color scheme twice; the reader must restyle the loaded document with JS instead of regenerating its HTML, because any HTML change reloads the web view and sends the article back to the top. Android is not covered: its reader web view is driven by separate Compose code and never regenerated from the color scheme. |
| REG-166 | `166-reader-rotation-retention.yaml` | `reader-mode` | Android | Guards issue #1401: rotating while reading keeps the reading position, and an open full-screen image viewer survives the rotation instead of being dismissed. `MainActivity` declares the size and orientation config changes, so the activity is not recreated and the reader's `loadDataWithBaseURL` document is never reloaded; the image URL is held in `rememberSaveable`. The post-rotation assertions check that the head anchor is gone and a late paragraph is on screen rather than re-asserting the tail anchor, because the text reflows at the new width and the anchoring can shift by a paragraph. The flow sets `PORTRAIT` before seeding so it does not inherit an orientation from an earlier flow. iOS is not covered: its reader is separate SwiftUI code, and REG-165 covers its own scroll-retention path. |
| REG-167 | `167-scroll-read-auto-hide.yaml` | `pagination-scroll-read` | Android, iOS | With both preferences saved on, scrolling marks articles read while keeping earlier rows reachable in the current list. Scrolling onward still loads the next page and reaches Article 050. |
| REG-168 | `168-mark-read-filter-boundaries.yaml` | `oldest-first` | Android, iOS | Mark all above/below from Bookmarks: the bookmarked unread article moves into Read, while unbookmarked old/new articles stay unread and visible on Timeline with Show read articles disabled. |
| REG-168 | `168-mark-read-filter-boundaries.yaml` | `oldest-first` | Android, iOS | Mark all above/below from Bookmarks: marking below the bookmarked read article moves the bookmarked unread article into Read, while unbookmarked old/new articles stay unread and visible on Timeline with Show read articles disabled. Marking above the bookmarked unread article leaves that article unread, because the range never includes the long-pressed article (issue #1556). |
| REG-169 | `169-bookmarks-mark-all-read-confirmation.yaml` | `content-rich` | Android | Toolbar and footer each show the Bookmarks-specific bulk-read confirmation; cancel preserves unread state, confirm marks saved articles read and retains their bookmarks, and an unbookmarked Timeline article remains unread. |
| REG-170 | `170-force-english.yaml` | `content-rich` | Android | Force English updates the interface immediately, survives a process restart, and can be disabled. iOS uses the system per-app language setting. |
| REG-171 | `171-search-reader-navigation.yaml` | `reader-mode` | Android | Cached search results follow Success → Fallback → Long at both reader boundaries; toolbar and system Back after article switching preserve the query/results, and leaving Search restores Home timeline navigation for the overlapping Success article. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ internal class FeedStateRepository(
currentItems
} else {
currentItems.mapIndexedNotNull { index, feedItem ->
if (index <= targetIndex && !feedItem.isRead) {
if (index < targetIndex && !feedItem.isRead) {
val updatedItem = feedItem.copy(isRead = true)
if (hideReadItems && currentFilter != FeedFilter.Read) {
null
Expand All @@ -360,7 +360,7 @@ internal class FeedStateRepository(
currentItems
} else {
currentItems.mapIndexedNotNull { index, feedItem ->
if (index >= targetIndex && !feedItem.isRead) {
if (index > targetIndex && !feedItem.isRead) {
val updatedItem = feedItem.copy(isRead = true)
if (hideReadItems && currentFilter != FeedFilter.Read) {
null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ class SearchViewModel internal constructor(
val targetIndex = items.indexOfFirst { it.id == targetItemId }

if (targetIndex != -1) {
// Get all items from the beginning up to and including the target item
val itemsToMark = items.subList(0, targetIndex + 1)
// Get all items before the target item
val itemsToMark = items.subList(0, targetIndex)
.filter { !it.isRead }
.map { FeedItemId(it.id) }

Expand All @@ -203,8 +203,8 @@ class SearchViewModel internal constructor(
val targetIndex = items.indexOfFirst { it.id == targetItemId }

if (targetIndex != -1) {
// Get all items from the target item to the end
val itemsToMark = items.subList(targetIndex, items.size)
// Get all items after the target item
val itemsToMark = items.subList(targetIndex + 1, items.size)
.filter { !it.isRead }
.map { FeedItemId(it.id) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ class DatabaseHelperRelativeItemsFilterTest : KoinTestBase() {
FeedFilter.Category(category) to setOf("new", "target", "bookmarked", "sibling", "old"),
FeedFilter.Uncategorized to setOf("uncategorized"),
)
for ((filter, expectedIds) in cases) {
for ((filter, filterIds) in cases) {
for (newer in listOf(true, false)) {
seedItems()
databaseHelper.ensureCloudSyncState(SESSION_ID)
val target = if (newer) "old" else "new"
val expectedIds = filterIds - target
val selectedIds = if (newer) {
databaseHelper.getNewerItems(target, filter)
} else {
Expand Down Expand Up @@ -86,9 +87,9 @@ class DatabaseHelperRelativeItemsFilterTest : KoinTestBase() {

val newer = above == (order == FeedOrder.NEWEST_FIRST)
val expected = if (newer) {
setOf("read", "target")
setOf("read")
} else {
setOf("bookmarked", "hidden", "read", "target")
setOf("bookmarked", "hidden", "read")
}
assertEquals(expected, readItemIds(), "$order, above=$above")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,10 @@ class FeedActionsRepositoryBazquxTest : KoinTestBase() {
feedItems.forEachIndexed { index, feedItem ->
val item = updatedItems.find { it.url_hash == feedItem.id }
assertNotNull(item, "Item ${feedItem.id} should exist")
if (index <= targetIndex) {
assertTrue(item.is_read, "Item above or equal to target should be marked as read: ${feedItem.id}")
if (index < targetIndex) {
assertTrue(item.is_read, "Item above target should be marked as read: ${feedItem.id}")
} else {
assertFalse(item.is_read, "Item below target should not be marked as read: ${feedItem.id}")
assertFalse(item.is_read, "Target or item below it should not be marked as read: ${feedItem.id}")
}
}
}
Expand Down Expand Up @@ -156,10 +156,10 @@ class FeedActionsRepositoryBazquxTest : KoinTestBase() {
feedItems.forEachIndexed { index, feedItem ->
val item = updatedItems.find { it.url_hash == feedItem.id }
assertNotNull(item, "Item ${feedItem.id} should exist")
if (index >= targetIndex) {
assertTrue(item.is_read, "Item below or equal to target should be marked as read: ${feedItem.id}")
if (index > targetIndex) {
assertTrue(item.is_read, "Item below target should be marked as read: ${feedItem.id}")
} else {
assertFalse(item.is_read, "Item above target should not be marked as read: ${feedItem.id}")
assertFalse(item.is_read, "Target or item above it should not be marked as read: ${feedItem.id}")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,10 @@ class FeedActionsRepositoryFeedbinTest : KoinTestBase() {
feedItems.forEachIndexed { index, feedItem ->
val item = updatedItems.find { it.url_hash == feedItem.id }
assertNotNull(item, "Item ${feedItem.id} should exist")
if (index <= targetIndex) {
assertTrue(item.is_read, "Item above or equal to target should be marked as read: ${feedItem.id}")
if (index < targetIndex) {
assertTrue(item.is_read, "Item above target should be marked as read: ${feedItem.id}")
} else {
assertFalse(item.is_read, "Item below target should not be marked as read: ${feedItem.id}")
assertFalse(item.is_read, "Target or item below it should not be marked as read: ${feedItem.id}")
}
}
}
Expand Down Expand Up @@ -178,10 +178,10 @@ class FeedActionsRepositoryFeedbinTest : KoinTestBase() {
feedItems.forEachIndexed { index, feedItem ->
val item = updatedItems.find { it.url_hash == feedItem.id }
assertNotNull(item, "Item ${feedItem.id} should exist")
if (index >= targetIndex) {
assertTrue(item.is_read, "Item below or equal to target should be marked as read: ${feedItem.id}")
if (index > targetIndex) {
assertTrue(item.is_read, "Item below target should be marked as read: ${feedItem.id}")
} else {
assertFalse(item.is_read, "Item above target should not be marked as read: ${feedItem.id}")
assertFalse(item.is_read, "Target or item above it should not be marked as read: ${feedItem.id}")
}
}
}
Expand Down Expand Up @@ -377,7 +377,7 @@ class FeedActionsRepositoryFeedbinTest : KoinTestBase() {
advanceUntilIdle()

assertEquals(
setOf(items[0].id, items[2].id),
setOf(items[0].id),
databaseHelper.getAllFeedItemFlagsForCloud().filter { it.isRead }.map { it.id }.toSet(),
)

Expand All @@ -386,7 +386,7 @@ class FeedActionsRepositoryFeedbinTest : KoinTestBase() {
advanceUntilIdle()

assertEquals(
setOf(items[2].id, items[3].id),
setOf(items[3].id),
databaseHelper.getAllFeedItemFlagsForCloud().filter { it.isRead }.map { it.id }.toSet(),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,10 @@ class FeedActionsRepositoryFreshRssTest : KoinTestBase() {
feedItems.forEachIndexed { index, feedItem ->
val item = updatedItems.find { it.url_hash == feedItem.id }
assertNotNull(item, "Item ${feedItem.id} should exist")
if (index <= targetIndex) {
assertTrue(item.is_read, "Item above or equal to target should be marked as read: ${feedItem.id}")
if (index < targetIndex) {
assertTrue(item.is_read, "Item above target should be marked as read: ${feedItem.id}")
} else {
assertFalse(item.is_read, "Item below target should not be marked as read: ${feedItem.id}")
assertFalse(item.is_read, "Target or item below it should not be marked as read: ${feedItem.id}")
}
}
}
Expand Down Expand Up @@ -156,10 +156,10 @@ class FeedActionsRepositoryFreshRssTest : KoinTestBase() {
feedItems.forEachIndexed { index, feedItem ->
val item = updatedItems.find { it.url_hash == feedItem.id }
assertNotNull(item, "Item ${feedItem.id} should exist")
if (index >= targetIndex) {
assertTrue(item.is_read, "Item below or equal to target should be marked as read: ${feedItem.id}")
if (index > targetIndex) {
assertTrue(item.is_read, "Item below target should be marked as read: ${feedItem.id}")
} else {
assertFalse(item.is_read, "Item above target should not be marked as read: ${feedItem.id}")
assertFalse(item.is_read, "Target or item above it should not be marked as read: ${feedItem.id}")
}
}
}
Expand Down
Loading
Loading