Show new-messages badge in tab title when re-foregrounding (#332)
Co-authored-by: Aditya Chaudhary <30+useradityaa@noreply.coracle.social>
This commit is contained in:
parent
b2f58b11c0
commit
65c9eac5e5
3 changed files with 40 additions and 6 deletions
|
|
@ -15,6 +15,7 @@
|
|||
import Login2 from "@assets/icons/login-3.svg?dataurl"
|
||||
import {fade, fly} from "@lib/transition"
|
||||
import {popKey} from "@lib/implicit"
|
||||
import {documentActive} from "@lib/html"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Divider from "@lib/components/Divider.svelte"
|
||||
import Icon from "@lib/components/Icon.svelte"
|
||||
|
|
@ -301,8 +302,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
const onVisibilityChange = () => {
|
||||
if (document.hidden) {
|
||||
// A tab can be `visible` but unfocused (user alt-tabbed to another app), so we
|
||||
// can't rely on document.hidden alone to know the room is actually being watched.
|
||||
const onActiveChange = (active: boolean) => {
|
||||
if (!active) {
|
||||
lastVisibleAt = now()
|
||||
} else if ($events.some(e => e.pubkey !== $user.pubkey && e.created_at > lastVisibleAt)) {
|
||||
newMessagesAfter = lastVisibleAt
|
||||
|
|
@ -450,12 +453,12 @@
|
|||
onMount(() => {
|
||||
start()
|
||||
|
||||
document.addEventListener("visibilitychange", onVisibilityChange)
|
||||
const unsubscribeActive = documentActive.subscribe(onActiveChange)
|
||||
|
||||
return () => {
|
||||
// Wrap in a closure to avoid calling a stale cleanup function
|
||||
cleanup?.()
|
||||
document.removeEventListener("visibilitychange", onVisibilityChange)
|
||||
unsubscribeActive()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,32 @@
|
|||
import {readable} from "svelte/store"
|
||||
import {sleep, randomId} from "@welshman/lib"
|
||||
import {Capacitor} from "@capacitor/core"
|
||||
export {preventDefault, stopPropagation} from "svelte/legacy"
|
||||
|
||||
/** Whether the user is actually looking at this tab right now. Display-only concern,
|
||||
* kept separate from any data store — consult it wherever "is someone watching" should
|
||||
* affect what's rendered. `document.hidden` alone misses window blur: switching to
|
||||
* another app without switching tabs leaves visibilityState "visible", so we also track
|
||||
* focus and require both. */
|
||||
export const documentActive = readable(
|
||||
typeof document === "undefined" ? true : !document.hidden && document.hasFocus(),
|
||||
set => {
|
||||
if (typeof document === "undefined") return
|
||||
|
||||
const update = () => set(!document.hidden && document.hasFocus())
|
||||
|
||||
document.addEventListener("visibilitychange", update)
|
||||
window.addEventListener("blur", update)
|
||||
window.addEventListener("focus", update)
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("visibilitychange", update)
|
||||
window.removeEventListener("blur", update)
|
||||
window.removeEventListener("focus", update)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Anchors an @svelte-plugins/datepicker popup with fixed positioning so it
|
||||
// escapes scroll-container clipping (e.g. inside modals). Call when the picker
|
||||
// opens; returns a cleanup function that removes the listeners.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
import * as util from "@welshman/util"
|
||||
import * as lib from "@welshman/lib"
|
||||
import {Logger} from "@welshman/app"
|
||||
import {isMobile} from "@lib/html"
|
||||
import {isMobile, documentActive} from "@lib/html"
|
||||
import AppContainer from "@app/components/AppContainer.svelte"
|
||||
import ModalContainer from "@app/components/ModalContainer.svelte"
|
||||
import {app} from "@app/core"
|
||||
|
|
@ -33,6 +33,7 @@
|
|||
import {theme} from "@app/theme"
|
||||
import {toast, pushToast} from "@app/toast"
|
||||
import * as notifications from "@app/notifications"
|
||||
import {notifications as notificationPaths, allNotifications} from "@app/notifications"
|
||||
import {Push} from "@app/push"
|
||||
import {onPushNotificationAction, pushState} from "@app/push/adapters/common"
|
||||
import {syncKeyboard} from "@app/keyboard"
|
||||
|
|
@ -239,7 +240,12 @@
|
|||
})
|
||||
|
||||
$effect(() => {
|
||||
document.title = getPageTitle({page: $page, pubkey: $app.user?.pubkey})
|
||||
const title = getPageTitle({page: $page, pubkey: $app.user?.pubkey})
|
||||
// While the tab isn't actively focused the user isn't actually looking at the
|
||||
// active page, so count notifications for it too rather than treating it as read.
|
||||
const unreadCount = $documentActive ? $notificationPaths.size : $allNotifications.size
|
||||
|
||||
document.title = unreadCount > 0 ? `(${unreadCount}) ${title}` : title
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue