Fix new messages indicator when coming back to the tab

This commit is contained in:
Jon Staab 2026-07-27 13:38:45 -07:00
parent cf938c6397
commit 2ffe51180b
2 changed files with 56 additions and 20 deletions

View file

@ -57,8 +57,6 @@
import {pushToast} from "@app/toast" import {pushToast} from "@app/toast"
const {h, relay} = $page.params as MakeNonOptional<typeof $page.params> const {h, relay} = $page.params as MakeNonOptional<typeof $page.params>
const mounted = now()
const lastChecked = $checked[$page.url.pathname]
const url = decodeRelay(relay) const url = decodeRelay(relay)
const room = deriveRoom(url, h) const room = deriveRoom(url, h)
const isVoiceRoom = $derived(getRoomType($room) === RoomType.Voice) const isVoiceRoom = $derived(getRoomType($room) === RoomType.Voice)
@ -292,6 +290,16 @@
} }
} }
const onVisibilityChange = () => {
if (document.hidden) {
lastVisibleAt = now()
} else if ($events.some(e => e.pubkey !== $pubkey && e.created_at > lastVisibleAt)) {
newMessagesAfter = lastVisibleAt
newMessagesBefore = now()
newMessagesSeen = false
}
}
let joining = $state(false) let joining = $state(false)
let leaving = $state(false) let leaving = $state(false)
let userHasScrolled = $state(false) let userHasScrolled = $state(false)
@ -302,6 +310,9 @@
let share = $state(popKey<TrustedEvent | undefined>("share")) let share = $state(popKey<TrustedEvent | undefined>("share"))
let parent: TrustedEvent | undefined = $state() let parent: TrustedEvent | undefined = $state()
let element: HTMLElement | undefined = $state() let element: HTMLElement | undefined = $state()
let lastVisibleAt = now()
let newMessagesAfter = $state($checked[$page.url.pathname])
let newMessagesBefore = $state(now())
let newMessagesSeen = false let newMessagesSeen = false
let showFixedNewMessages = $state(false) let showFixedNewMessages = $state(false)
let showScrollButton = $state(false) let showScrollButton = $state(false)
@ -327,9 +338,11 @@
if (events) { if (events) {
const lastUserEvent = $events.findLast(e => e.pubkey === $pubkey) const lastUserEvent = $events.findLast(e => e.pubkey === $pubkey)
// Adjust last checked to account for messages that came from a different device // Adjust the boundary to account for messages that came from a different device
const adjustedLastChecked = const adjustedAfter =
lastChecked && lastUserEvent ? Math.max(lastUserEvent.created_at, lastChecked) : lastChecked newMessagesAfter && lastUserEvent
? Math.max(lastUserEvent.created_at, newMessagesAfter)
: newMessagesAfter
for (const event of $events) { for (const event of $events) {
if (seen.has(event.id)) { if (seen.has(event.id)) {
@ -340,10 +353,10 @@
if ( if (
!newMessagesSeen && !newMessagesSeen &&
adjustedLastChecked && adjustedAfter &&
event.pubkey !== $pubkey && event.pubkey !== $pubkey &&
event.created_at > adjustedLastChecked && event.created_at > adjustedAfter &&
event.created_at < mounted event.created_at < newMessagesBefore
) { ) {
elements.push({type: "new-messages", id: "new-messages"}) elements.push({type: "new-messages", id: "new-messages"})
newMessagesSeen = true newMessagesSeen = true
@ -424,8 +437,13 @@
onMount(() => { onMount(() => {
start() start()
// Wrap in a closure to avoid calling a stale cleanup function document.addEventListener("visibilitychange", onVisibilityChange)
return () => cleanup?.()
return () => {
// Wrap in a closure to avoid calling a stale cleanup function
cleanup?.()
document.removeEventListener("visibilitychange", onVisibilityChange)
}
}) })
</script> </script>

View file

@ -36,8 +36,6 @@
import {makeFeed} from "@app/feeds" import {makeFeed} from "@app/feeds"
import {popKey} from "@lib/implicit" import {popKey} from "@lib/implicit"
const mounted = now()
const lastChecked = $checked[$page.url.pathname]
const url = decodeRelay($page.params.relay!) const url = decodeRelay($page.params.relay!)
const shouldProtect = canEnforceNip70(url) const shouldProtect = canEnforceNip70(url)
const at = $derived(parseInt($page.url.searchParams.get("at")!)) const at = $derived(parseInt($page.url.searchParams.get("at")!))
@ -171,6 +169,16 @@
} }
} }
const onVisibilityChange = () => {
if (document.hidden) {
lastVisibleAt = now()
} else if ($events.some(e => e.pubkey !== $pubkey && e.created_at > lastVisibleAt)) {
newMessagesAfter = lastVisibleAt
newMessagesBefore = now()
newMessagesSeen = false
}
}
let loadingBackward = $state(true) let loadingBackward = $state(true)
let loadingForward = $state(true) let loadingForward = $state(true)
let userHasScrolled = $state(false) let userHasScrolled = $state(false)
@ -179,6 +187,9 @@
let share = $state(popKey<TrustedEvent | undefined>("share")) let share = $state(popKey<TrustedEvent | undefined>("share"))
let parent: TrustedEvent | undefined = $state() let parent: TrustedEvent | undefined = $state()
let element: HTMLElement | undefined = $state() let element: HTMLElement | undefined = $state()
let lastVisibleAt = now()
let newMessagesAfter = $state($checked[$page.url.pathname])
let newMessagesBefore = $state(now())
let newMessagesSeen = false let newMessagesSeen = false
let showFixedNewMessages = $state(false) let showFixedNewMessages = $state(false)
let showScrollButton = $state(false) let showScrollButton = $state(false)
@ -204,9 +215,11 @@
if (events) { if (events) {
const lastUserEvent = $events.findLast(e => e.pubkey === $pubkey) const lastUserEvent = $events.findLast(e => e.pubkey === $pubkey)
// Adjust last checked to account for messages that came from a different device // Adjust the boundary to account for messages that came from a different device
const adjustedLastChecked = const adjustedAfter =
lastChecked && lastUserEvent ? Math.max(lastUserEvent.created_at, lastChecked) : lastChecked newMessagesAfter && lastUserEvent
? Math.max(lastUserEvent.created_at, newMessagesAfter)
: newMessagesAfter
for (const event of $events) { for (const event of $events) {
if (seen.has(event.id)) { if (seen.has(event.id)) {
@ -217,10 +230,10 @@
if ( if (
!newMessagesSeen && !newMessagesSeen &&
adjustedLastChecked && adjustedAfter &&
event.pubkey !== $pubkey && event.pubkey !== $pubkey &&
event.created_at > adjustedLastChecked && event.created_at > adjustedAfter &&
event.created_at < mounted event.created_at < newMessagesBefore
) { ) {
elements.push({type: "new-messages", id: "new-messages"}) elements.push({type: "new-messages", id: "new-messages"})
newMessagesSeen = true newMessagesSeen = true
@ -301,8 +314,13 @@
onMount(() => { onMount(() => {
start() start()
// Wrap in a closure to avoid calling a stale cleanup function document.addEventListener("visibilitychange", onVisibilityChange)
return () => cleanup?.()
return () => {
// Wrap in a closure to avoid calling a stale cleanup function
cleanup?.()
document.removeEventListener("visibilitychange", onVisibilityChange)
}
}) })
</script> </script>