amanpay / web /src /components /d2.tsx
MHamdan's picture
CI deploy 7dac85a (part 2)
134ecb7 verified
Raw
History Blame Contribute Delete
3.08 kB
import { useI18n } from '../i18n'
import type { MessageKey } from '../i18n/en'
import { isWebAuthnSupported } from '../auth/passkey'
import { Callout } from './ui'
type T = (key: MessageKey, vars?: Record<string, string | number>) => string
/** True when the app is rendered inside another frame — passkeys need the top-level origin. */
export function inEmbeddedFrame(): boolean {
try {
return window.self !== window.top
} catch {
return true
}
}
export function statusLabel(t: T, status: string): string {
const key = `d2.status.${status}` as MessageKey
const label = t(key)
return label === key ? status : label
}
export function roleLabel(t: T, role: string): string {
const key = `d2.role.${role}` as MessageKey
const label = t(key)
return label === key ? role : label
}
/** Shows unsupported-browser and embedded-frame warnings for passkey flows. */
export function PasskeyEnvWarnings() {
const { t } = useI18n()
return (
<>
{!isWebAuthnSupported() && <Callout tone="warn">{t('d2.unsupported')}</Callout>}
{inEmbeddedFrame() && <Callout tone="warn">{t('d2.iframeWarn')}</Callout>}
</>
)
}
/**
* When the app is embedded in another site's iframe (e.g. the huggingface.co Spaces page),
* browsers may block the session cookie and passkeys. Offer a one-tap break-out to the app's own
* first-party tab, where everything works. Renders nothing when not embedded.
*/
export function OpenInNewTabBanner() {
const { t } = useI18n()
if (!inEmbeddedFrame()) return null
const href = typeof window !== 'undefined' ? window.location.href : '#'
return (
<div className="callout tone-warn" role="note">
<strong>{t('d2.openNewTab.title')}</strong>
<p className="hint">{t('d2.openNewTab.body')}</p>
<a className="btn" href={href} target="_blank" rel="noopener noreferrer">
{t('d2.openNewTab.button')} ↗
</a>
</div>
)
}
/** Accessible destructive-action confirmation. Focus lands on Cancel; Esc cancels. */
export function ConfirmDialog({
message, confirmLabel, danger = true, busy = false, onConfirm, onCancel,
}: {
message: string
confirmLabel: string
danger?: boolean
busy?: boolean
onConfirm: () => void
onCancel: () => void
}) {
const { t } = useI18n()
return (
<div className="d2-modal-backdrop" onClick={onCancel}>
<div
className="d2-modal card"
role="alertdialog"
aria-modal="true"
aria-label={message}
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => {
if (e.key === 'Escape') onCancel()
}}
>
<p>{message}</p>
<div className="row">
<button type="button" className="btn" autoFocus onClick={onCancel} disabled={busy}>
{t('d2.cancel')}
</button>
<button
type="button"
className={danger ? 'btn danger' : 'btn'}
onClick={onConfirm}
disabled={busy}
>
{confirmLabel}
</button>
</div>
</div>
</div>
)
}