153 lines
6.8 KiB
JavaScript
153 lines
6.8 KiB
JavaScript
/**
|
|
* This plugin's configuration, rendered on its own page in the plugin
|
|
* manager (`plugins.bundle.config`, keyed by this package's name).
|
|
*
|
|
* The owner asks for two views: `summary` (a one-liner that stands in for the
|
|
* row's description) and `page` (the whole form). Everything visible here
|
|
* belongs to this package, including the platform-specific permission hint.
|
|
*
|
|
* The page is laid out as three groups — what to notify about, the system
|
|
* channel's own state, and what the plugin is doing right now. Each group keeps
|
|
* its controls in one place: a list whose rows carry the subject on the left and
|
|
* the control on the right, and a fact list for the readings, so the status
|
|
* block is scannable instead of being four sentences in one cell.
|
|
*
|
|
* @module @dsh-plugin/session-notify/client/ui/ConfigSection
|
|
*/
|
|
|
|
import * as React from 'react'
|
|
import { DISMISS_CHOICES, KINDS } from '../constants.js'
|
|
import { windowIsAway } from '../platform.js'
|
|
import { DismissRow } from './DismissRow.js'
|
|
import { KindRow } from './KindRow.js'
|
|
import { seatTranslator, useOwnStore } from './hooks.js'
|
|
import { CSS } from './styles.js'
|
|
|
|
const h = React.createElement
|
|
|
|
export function ConfigSection(props) {
|
|
const snapshot = useOwnStore(props.store)
|
|
const tr = seatTranslator(props, props.fallbackT)
|
|
if (props.view === 'summary') return h(React.Fragment, null, tr('settings.description'))
|
|
|
|
const permission = snapshot.permission
|
|
const supported = permission !== 'unsupported'
|
|
const failed = permission === 'denied' || snapshot.promptError !== ''
|
|
const statusKey = permission === 'granted'
|
|
? 'settings.permission.granted'
|
|
: permission === 'denied'
|
|
? 'settings.permission.denied'
|
|
: supported
|
|
? 'settings.permission.default'
|
|
: 'settings.permission.unsupported'
|
|
const promptFailed = snapshot.promptError !== ''
|
|
const permissionText = promptFailed ? snapshot.promptError : tr(statusKey)
|
|
const dotClass = permission === 'granted'
|
|
? ''
|
|
: failed ? ' is-error' : supported ? ' is-warn' : ' is-muted'
|
|
const permissionNote = promptFailed
|
|
? ''
|
|
: permission === 'denied' || !supported
|
|
? tr(`settings.permission.hint.${props.platform}`)
|
|
: permission === 'granted'
|
|
? tr('config.testHint')
|
|
: tr('settings.permission.hint.plain')
|
|
|
|
const health = snapshot.health
|
|
const healthText = health.state === 'watching'
|
|
? tr('settings.health.watching')
|
|
: health.state === 'noHooks'
|
|
? tr('settings.health.noHooks')
|
|
: health.state === 'error'
|
|
? tr('settings.health.error', { message: health.message })
|
|
: tr('settings.health.starting')
|
|
const healthFailed = health.state !== 'watching' && health.state !== 'starting'
|
|
|
|
const delivery = props.copy.deliveryFacts(snapshot.lastDelivery, tr)
|
|
|
|
return h(React.Fragment, null,
|
|
h('style', null, CSS),
|
|
h('div', { className: 'dsn-page' },
|
|
// The page header already shows this bundle's title and description, so this
|
|
// lede says what the header cannot: how an alert travels, and that the
|
|
// channel is chosen at delivery time.
|
|
h('p', { className: 'dsn-lede' }, tr('config.intro')),
|
|
|
|
h('section', { className: 'dsn-group' },
|
|
h('h3', { className: 'dsn-group-title' }, tr('config.section.kinds')),
|
|
h('div', { className: 'dsn-list' },
|
|
KINDS.map((kind) => h(KindRow, {
|
|
key: kind,
|
|
label: tr(`settings.kind.${kind}`),
|
|
checked: snapshot.kinds[kind] === true,
|
|
disabled: false,
|
|
onToggle: () => props.setKind(kind, snapshot.kinds[kind] !== true),
|
|
}))),
|
|
h('p', { className: 'dsn-group-note' }, tr('config.saveNote'))),
|
|
|
|
h('section', { className: 'dsn-group' },
|
|
h('h3', { className: 'dsn-group-title' }, tr('config.section.popup')),
|
|
h('div', { className: 'dsn-list' },
|
|
h(DismissRow, {
|
|
label: tr('settings.dismiss.label'),
|
|
options: DISMISS_CHOICES.map((value) => ({ value, label: tr(`settings.dismiss.${value}`) })),
|
|
value: snapshot.dismissAfterMs,
|
|
onChange: props.setDismiss,
|
|
})),
|
|
h('p', { className: 'dsn-group-note' }, tr('config.dismiss.note'))),
|
|
|
|
h('section', { className: 'dsn-group' },
|
|
h('h3', { className: 'dsn-group-title' }, tr('config.section.permission')),
|
|
h('div', { className: 'dsn-list' },
|
|
h('div', { className: 'dsn-row' },
|
|
h('div', { className: 'dsn-row-text' },
|
|
h('span', {
|
|
className: 'dsn-state',
|
|
role: failed ? 'alert' : 'status',
|
|
},
|
|
h('span', { className: `dsn-dot${dotClass}` }),
|
|
permissionText),
|
|
h('p', { className: 'dsn-row-sub' }, permissionNote)),
|
|
h('button', {
|
|
type: 'button',
|
|
className: `dsn-button${supported && permission !== 'granted' ? ' is-primary' : ''}`,
|
|
disabled: !supported,
|
|
onClick: permission === 'granted' ? props.testSystem : props.ask,
|
|
}, tr(!supported
|
|
? 'settings.action.unsupported'
|
|
: permission === 'granted' ? 'settings.action.testSystem' : 'settings.action.allow'))))),
|
|
|
|
h('section', { className: 'dsn-group' },
|
|
h('h3', { className: 'dsn-group-title' }, tr('config.section.status')),
|
|
h('dl', { className: 'dsn-facts' },
|
|
h('dt', { className: 'dsn-fact-key' }, tr('config.diag.window')),
|
|
h('dd', { className: 'dsn-fact' },
|
|
h('p', { className: 'dsn-fact-value' }, tr(windowIsAway() ? 'config.diag.away' : 'config.diag.inFront'))),
|
|
h('dt', { className: 'dsn-fact-key' }, tr('config.diag.watch')),
|
|
h('dd', { className: 'dsn-fact' },
|
|
h('p', {
|
|
className: `dsn-fact-value${healthFailed ? ' is-error' : ''}`,
|
|
role: healthFailed ? 'alert' : 'status',
|
|
}, healthText)),
|
|
h('dt', { className: 'dsn-fact-key' }, tr('config.diag.last')),
|
|
h('dd', { className: 'dsn-fact' },
|
|
delivery === null
|
|
? h('p', { className: 'dsn-fact-value' }, tr('config.diag.none'))
|
|
: [
|
|
h('p', { key: 'line', className: 'dsn-fact-value' }, `${delivery.at} · ${delivery.outcome}`),
|
|
delivery.title === ''
|
|
? null
|
|
: h('p', { key: 'title', className: 'dsn-fact-title' }, delivery.title),
|
|
]),
|
|
h('dt', { className: 'dsn-fact-key' }, tr('config.diag.replay')),
|
|
h('dd', { className: 'dsn-fact' },
|
|
h('p', { className: 'dsn-fact-value' }, props.copy.replayValue(props.replayCount(), tr)))),
|
|
h('div', { className: 'dsn-list' },
|
|
h('div', { className: 'dsn-row is-action' },
|
|
h('button', {
|
|
type: 'button',
|
|
className: 'dsn-button',
|
|
onClick: props.testAny,
|
|
}, tr('settings.action.testAny')))))))
|
|
}
|