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

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')))))))
}