Files
session-notify/src/client/ui/hooks.js
T

50 lines
1.8 KiB
JavaScript

/**
* The React seams the components share: two subscriptions this plugin needs
* beyond the ones the slot owner already provides, plus the seat rule for
* translation.
*
* @module @dsh-plugin/session-notify/client/ui/hooks
*/
import * as React from 'react'
import { windowIsAway } from '../platform.js'
/** Subscribe to this plugin's own store from inside a component. */
export function useOwnStore(source) {
return React.useSyncExternalStore(source.subscribe, source.getSnapshot, source.getSnapshot)
}
/**
* Subscribe to every browser signal that says whether the app is visible:
* the window's focus events and the document's visibility change. The value
* itself is a boolean, so the observer re-renders once per transition.
*/
export function useWindowState() {
const subscribe = React.useCallback((listener) => {
try {
window.addEventListener('focus', listener)
window.addEventListener('blur', listener)
document.addEventListener('visibilitychange', listener)
} catch {
return () => {}
}
return () => {
window.removeEventListener('focus', listener)
window.removeEventListener('blur', listener)
document.removeEventListener('visibilitychange', listener)
}
}, [])
return React.useSyncExternalStore(subscribe, () => !windowIsAway(), () => true)
}
/**
* Every component translates through this: the slot's own seat when the owner
* supplies one, else this package's own translator.
* @param props - the slot entry's props, which carry the owner's `t` when it has one.
* @param fallback - the plugin's own translator, used when the seat has none.
* @returns the translate function this render should use.
*/
export function seatTranslator(props, fallback) {
return typeof props.t === 'function' ? props.t : fallback
}