Something went wrong. Try again.
Browser extension: detect and subscribe to standard.site publications on ATProto
Something went wrong. Try again.
6.1 kB · 161 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162// The popup's status pills: the one place that turns a tab's detection state// into words. A page can be several things at once (offline, unverified,// session expired), so every applicable message is returned, weighted by// severity — errors first, then alerts, then warnings, then informational// lines. Pure so every state -> messages mapping can be unit-tested; the// popup only decides where to render the result.
import { bskyProfileUrl } from './atproto'import type { PageState } from './types'
/** * How loud a pill is. `error` is the extension failing at its job; `alert` is * the extension working and the answer being serious — a moderation decision * the user themselves made, or one a labeler they listen to made. Both are * red, and each keeps its own glyph (see TONE_ICONS in the popup), so the * distinction survives without color vision. */export type StatusTone = 'info' | 'warn' | 'alert' | 'error'
export interface StatusMessage { text: string tone: StatusTone /** A substring of `text` to render as a link pointing at `href`. */ link?: { text: string; href: string } /** * This line is not an answer, it is the absence of one: the worker has not * said anything about this page yet. The popup marks the status region * `aria-busy` for it, the same way the publication card does while its own * lookups are out — so "still working" is one question anything outside the * popup can ask, including the capture (scripts/capture-status-docs.mjs). */ pending?: boolean}
export interface StatusInput { /** Detection state for the tab; undefined while unknown or inapplicable. */ state: PageState | undefined /** Whether the active tab is an http(s) page at all. */ httpPage: boolean /** Whether the popup has a signed-in session. */ signedIn: boolean /** The popup's request to the background worker itself failed. */ requestFailed?: boolean /** A stored session existed but could not be restored (token expired or revoked). */ sessionExpired?: boolean /** The signed-out visitor clicked the inert Subscribe button. */ signinNudge?: boolean}
const TONE_WEIGHT: Record<StatusTone, number> = { error: 0, alert: 1, warn: 2, info: 3 }
/** Host of the publication's canonical URL, for pointing at its real home. */function pubHost(url: string): string | undefined { try { return new URL(url).hostname } catch { return undefined }}
/** * Every status message that applies to a state, most severe first. Empty when * everything is nominal and the publication card speaks for itself. */export function statusMessagesFor(input: StatusInput): StatusMessage[] { const { state, httpPage, signedIn, requestFailed, sessionExpired, signinNudge } = input const messages: StatusMessage[] = []
if (!httpPage) { return [ { text: 'Publication detection only runs on regular web pages, not on browser or extension pages.', tone: 'info', }, ] }
if (sessionExpired) { messages.push({ text: 'Your session expired — sign in again to see your subscription state.', tone: 'info', }) }
if (requestFailed) { messages.push({ text: 'Something went wrong while checking this page. Use Refresh to try again.', tone: 'error', }) } else if (!state) { messages.push({ text: 'Checking this page…', tone: 'info', pending: true }) } else { if (state.error === 'offline') { messages.push({ text: 'You appear to be offline. Publication lookups need a connection — reconnect, then use Refresh.', tone: 'error', }) } if (state.error === 'fetch-failed') { messages.push({ text: 'Could not fetch publication records for this page — the publisher’s data server may be unreachable. Use Refresh to retry.', tone: 'error', }) }
const pub = state.pub if (!pub && !state.error) { messages.push({ text: 'No standard.site publication on this page.', tone: 'info' }) }
// Before the verification warning: who is behind the page outranks whether // this page is really their home. The handle links to the profile, which // is where the block can be reviewed — nothing here changes it. if (pub && state.blocked) { const who = pub.handle ? `@${pub.handle.replace(/^@/, '')}` : pub.did messages.push({ // Red, like the badge for this state: a block the user made is the // most serious thing the popup can say about a page it understood. text: `You have blocked ${who}, the account this publication belongs to.`, tone: 'alert', link: { text: who, href: bskyProfileUrl(pub.handle ?? pub.did) }, }) }
if (pub && !pub.verified) { const host = pubHost(pub.record.url) messages.push({ text: `This site isn’t the verified publisher for “${pub.record.name}”.` + (host ? ` The publication’s own home is ${host}.` : ''), tone: 'warn', ...(host ? { link: { text: host, href: pub.record.url } } : {}), }) }
// Signed out, and the visitor clicked the inert Subscribe: answer the // click with a real pill instead of a standing hint. Shown even next to // the expired-session line — a click deserves a direct response. if (pub && !signedIn && signinNudge) { messages.push({ text: 'Sign in with your Atmosphere account to subscribe.', tone: 'warn', link: { text: 'Atmosphere account', href: 'https://atmosphereaccount.com/hosts' }, }) }
// Signed in, but the subscription list could not be fetched. if (pub && signedIn && state.subscriptionRkey === undefined) { messages.push({ text: 'Could not load your subscriptions, so the subscribe state shown here may be stale. Use Refresh to retry.', tone: 'warn', }) } }
// Stable sort: severity first, insertion order within a tone. return messages .map((m, i) => ({ m, i })) .sort((a, b) => TONE_WEIGHT[a.m.tone] - TONE_WEIGHT[b.m.tone] || a.i - b.i) .map(({ m }) => m)}