forked from unom/punktfunk
Pairing: - Refresh the paired-devices list after a native PIN pairing (the happy path never invalidated it, so a newly paired device stayed hidden until remount). - Moonlight PIN: a 204 means "PIN delivered to the waiting handshake", NOT paired, so it now reads "PIN sent" instead of a false "Paired successfully". - Hide the Moonlight pairing card on native-only hosts (HostInfo.gamestream) — it could never receive a PIN there. - Per-row pending on unpair/approve/deny; PIN input maxLength 16 (was 8). Displays / Library: - "Arrange displays" save refreshes the settings card (it rewrites the policy), without clobbering unsaved Custom edits (re-seed only when the draft still matches the server). - Live-display list wrapped in QueryState so errors don't read as "no displays". - "Forever" keep-alive option in the custom editor; edit-game form round-trips the logo artwork (was dropped on save); per-card delete pending. Stats: - Distinct colour for the native "queue" latency stage (it collided with "capture"). - "Not measured on this path" note on the GameStream health chart; configured-bitrate target line on throughput; host-authoritative elapsed timer; LiveCard surfaces non-404 errors. Shell / auth / i18n: - SSR-stable locale: first client render matches the base-locale SSR (no hydration mismatch), then adopts the persisted/browser locale post-hydration. - BFF proxy maps an upstream (mgmt-token) 401 to 502 so a logged-in user isn't bounced into a post-login redirect loop. - Logout checks the POST result before navigating; logs dedup by seq (StrictMode); login "next" keeps query/hash; Dashboard shows the active-session count. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
30 lines
1.4 KiB
TypeScript
30 lines
1.4 KiB
TypeScript
import type { FC } from "react";
|
|
import { useGetHostInfo } from "@/api/gen/host/host";
|
|
import { useLocale } from "@/lib/i18n";
|
|
import { MoonlightPairingSection } from "./MoonlightPairingCard";
|
|
import { NativePairingSection } from "./NativePairingCard";
|
|
import { PairedDevicesSection } from "./PairedDevices";
|
|
import { PendingDevicesSection } from "./PendingDevices";
|
|
import { PairingView } from "./view";
|
|
|
|
// Pairing composes four independent, self-contained sub-cards. Each subsection owns its own
|
|
// queries + mutations (in its own file, next to its presentational card). The arrangement lives in
|
|
// PairingView so the live page (these containers) and the Storybook story (pure cards + mock state)
|
|
// fill the same slots — the layout is defined once and can't drift.
|
|
export const SectionPairing: FC = () => {
|
|
useLocale();
|
|
// Moonlight/GameStream pairing only works when the host runs the compat planes (`--gamestream`,
|
|
// off by default). Otherwise a Moonlight PIN can never arrive, so the card is dead UI — hide it
|
|
// (and until host info loads, to avoid a flash of an un-actionable card).
|
|
const host = useGetHostInfo();
|
|
const gamestream = host.data?.gamestream === true;
|
|
return (
|
|
<PairingView
|
|
pending={<PendingDevicesSection />}
|
|
native={<NativePairingSection />}
|
|
moonlight={gamestream ? <MoonlightPairingSection /> : null}
|
|
paired={<PairedDevicesSection />}
|
|
/>
|
|
);
|
|
};
|