92 lines
3.6 KiB
JavaScript
92 lines
3.6 KiB
JavaScript
// The browser tab title, including WHICH record is open.
|
|
//
|
|
// The router can only name the KIND of page, because it runs before the view
|
|
// has fetched anything: every application read "<facility> ShopDB -
|
|
// Application", so two tabs on two different machines were indistinguishable,
|
|
// and a shared link previewed as the section rather than the thing.
|
|
//
|
|
// So a detail view names itself once its record arrives:
|
|
//
|
|
// setRecordTitle(machine.assetnumber)
|
|
//
|
|
// THE ORDERING IS THE WHOLE PROBLEM. The router's afterEach awaits the site
|
|
// settings before it can build "<facility> ShopDB - <page>", so on a cold load
|
|
// it finishes AFTER a view that fetched quickly has already set a good title,
|
|
// and would overwrite it with the generic one. Both writers therefore go
|
|
// through here, and the generic write is dropped when a record title already
|
|
// belongs to the route being landed on. The key is the route's fullPath, so
|
|
// navigating away invalidates the previous record's claim.
|
|
|
|
import { getFacilityName } from '@/utils/siteSettings'
|
|
|
|
let activeKey = null // fullPath of the route the tab is currently showing
|
|
let recordTitle = null // record name set by a view for activeKey, if any
|
|
let sitePrefix = 'ShopDB' // '<facility> ShopDB', resolved once and cached
|
|
|
|
function compose(page) {
|
|
return page && page !== 'Home' ? `${sitePrefix} - ${page}` : sitePrefix
|
|
}
|
|
|
|
async function resolveSitePrefix() {
|
|
try {
|
|
const facility = await getFacilityName()
|
|
if (facility && facility !== 'ShopDB') sitePrefix = `${facility} ShopDB`
|
|
} catch (settingsError) {
|
|
// settings unavailable: plain ShopDB is a fine tab title
|
|
}
|
|
return sitePrefix
|
|
}
|
|
|
|
// Called by the router on every navigation, BEFORE it awaits anything, so a
|
|
// view that resolves first is not treated as belonging to the previous route.
|
|
export function beginNavigation(key) {
|
|
activeKey = key
|
|
recordTitle = null
|
|
}
|
|
|
|
// Called by the router once it knows the site prefix. Skipped when a view has
|
|
// already named the record for this route.
|
|
export async function applyGenericTitle(key, page) {
|
|
await resolveSitePrefix()
|
|
if (key !== activeKey) return // navigated on while we awaited
|
|
// A view that named the record before the facility name arrived composed its
|
|
// title against the placeholder prefix, so rewrite it rather than leaving
|
|
// 'ShopDB - CSF16' without the facility name in front of it.
|
|
document.title = compose(recordTitle !== null ? recordTitle : page)
|
|
}
|
|
|
|
// A tab shows very little, and the browser truncates mid-word wherever it runs
|
|
// out. Cut at a word boundary instead, so what survives is readable. Knowledge
|
|
// base articles are the case that forces this: their name is a whole sentence
|
|
// of description, not a short label.
|
|
const MAXLENGTH = 60
|
|
|
|
function shorten(text) {
|
|
if (text.length <= MAXLENGTH) return text
|
|
const cut = text.slice(0, MAXLENGTH)
|
|
const space = cut.lastIndexOf(' ')
|
|
return `${(space > MAXLENGTH / 2 ? cut.slice(0, space) : cut).trimEnd()}...`
|
|
}
|
|
|
|
// Called by a detail view once its record has loaded. An empty or missing name
|
|
// leaves the generic title in place rather than showing a bare id: "Machine"
|
|
// beats "Machine 4192" for somebody scanning a row of tabs, and a record that
|
|
// failed to load has no name to show anyway.
|
|
export function setRecordTitle(name) {
|
|
const text = (name === null || name === undefined) ? '' : String(name).trim()
|
|
if (!text) return
|
|
recordTitle = shorten(text)
|
|
document.title = compose(recordTitle)
|
|
}
|
|
|
|
// Exposed for tests.
|
|
export function _state() {
|
|
return { activeKey, recordTitle, sitePrefix }
|
|
}
|
|
|
|
export function _reset() {
|
|
activeKey = null
|
|
recordTitle = null
|
|
sitePrefix = 'ShopDB'
|
|
}
|