Keep the unread dot on a content item you have not opened (#592)

This commit is contained in:
Coracle-Bot 2026-09-19 18:27:38 +00:00 committed by hodlbod
parent c4a67a4716
commit 444d36ac35
3 changed files with 11 additions and 20 deletions

View file

@ -1716,8 +1716,10 @@ Acceptance:
posted himself does not. posted himself does not.
- Opening the list keeps the dot on alice's row, so he can tell which thread is - Opening the list keeps the dot on alice's row, so he can tell which thread is
new, and bob's row still carries none. new, and bob's row still carries none.
- Leaving the list marks its threads read, and neither the row nor the nav item - Opening a thread takes its own row's dot down, and one he left alone keeps its
shows a dot afterwards. dot.
- The nav item stays clear once he has opened the list, whether or not a thread
under it is still unread.
### US-113 — See which threads are unread on a phone ### US-113 — See which threads are unread on a phone

View file

@ -761,6 +761,7 @@ test("US-112 see which threads are unread", async ({seed, as}) => {
// A thread bob wrote raises no indicator of its own, so it is the control for a row with no dot // A thread bob wrote raises no indicator of its own, so it is the control for a row with no dot
seedThread(space, user.bob, "where is the sextant", at(3, HOUR)) seedThread(space, user.bob, "where is the sextant", at(3, HOUR))
seedThread(space, user.alice, "the server is on fire", at(2, HOUR)) seedThread(space, user.alice, "the server is on fire", at(2, HOUR))
seedThread(space, user.alice, "the anchor is dragging", at(1, HOUR))
}) })
const space = scenario.space("space") const space = scenario.space("space")
@ -774,6 +775,7 @@ test("US-112 see which threads are unread", async ({seed, as}) => {
await threadsNav.click() await threadsNav.click()
const hers = bob.getByRole("row").filter({hasText: "the server is on fire"}) const hers = bob.getByRole("row").filter({hasText: "the server is on fire"})
const unopened = bob.getByRole("row").filter({hasText: "the anchor is dragging"})
const his = bob.getByRole("row").filter({hasText: "where is the sextant"}) const his = bob.getByRole("row").filter({hasText: "where is the sextant"})
await expect(his).toBeVisible() await expect(his).toBeVisible()
@ -784,20 +786,22 @@ test("US-112 see which threads are unread", async ({seed, as}) => {
await bob.waitForTimeout(1500) await bob.waitForTimeout(1500)
await expect(unreadDot(hers)).toBeVisible() await expect(unreadDot(hers)).toBeVisible()
await expect(unreadDot(unopened)).toBeVisible()
await expect(unreadDot(his)).toHaveCount(0) await expect(unreadDot(his)).toHaveCount(0)
await hers.click() await hers.click()
await expect(pageBar(bob)).toContainText("the server is on fire") await expect(pageBar(bob)).toContainText("the server is on fire")
// Leaving the list is what marks its threads read, so the dot is gone on the way back // Opening a thread is what marks it read, so only the one he opened loses its dot
await bob.goBack() await bob.goBack()
await expect(hers).toBeVisible()
await expect(unreadDot(hers)).toHaveCount(0) await expect(unreadDot(hers)).toHaveCount(0)
await expect(unreadDot(unopened)).toBeVisible()
await roomLink(bob, "General").click() await roomLink(bob, "General").click()
// The nav item is clear even with a thread under it still unread
await expect(threadsNav).toBeVisible() await expect(threadsNav).toBeVisible()
await expect(unreadDot(threadsNav)).toHaveCount(0) await expect(unreadDot(threadsNav)).toHaveCount(0)
}) })

View file

@ -18,7 +18,6 @@ import {Relays, RoomLists} from "@welshman/app"
import {deriveEventsByIdByUrl} from "@app/repository" import {deriveEventsByIdByUrl} from "@app/repository"
import {app, fromApp} from "@app/core" import {app, fromApp} from "@app/core"
import {makeRoomPath, makeSpaceChatPath, makeChatPath, makeContentPath} from "@app/routes" import {makeRoomPath, makeSpaceChatPath, makeChatPath, makeContentPath} from "@app/routes"
import {decodeRelay} from "@app/relays"
import {CONTENT_KINDS, makeCommentFilter} from "@app/content" import {CONTENT_KINDS, makeCommentFilter} from "@app/content"
import {getIsMuted, notificationSettings, userSettingsValues} from "@app/settings" import {getIsMuted, notificationSettings, userSettingsValues} from "@app/settings"
import {chatsById} from "@app/chats" import {chatsById} from "@app/chats"
@ -48,20 +47,6 @@ const getPaths = (path: string) =>
.map((_, i, segments) => segments.slice(0, i + 1).join("/")) .map((_, i, segments) => segments.slice(0, i + 1).join("/"))
.slice(1) .slice(1)
const getExitPaths = (pathname: string, relay?: string) => {
const paths = getPaths(pathname)
if (relay) {
const url = decodeRelay(relay)
if (CONTENT_KINDS.some(kind => makeContentPath(url, kind) === pathname)) {
paths.push(pathname + "*")
}
}
return paths
}
export const syncChecked = () => { export const syncChecked = () => {
let prev: string[] = [] let prev: string[] = []
@ -91,7 +76,7 @@ export const syncChecked = () => {
}) })
}, 300) }, 300)
prev = getExitPaths($page.url.pathname, $page.params.relay) prev = paths
}) })
} }