fix: code-review round 1 — 4 findings

- og: guard safe_abs_path's None (missing/gc'd poster) before _jpeg_size, else the public /watch OG
  render crashed with an uncaught AttributeError
- channels: hoist the priority step button to module scope so it no longer remounts on every
  hold-repeat tick (which dropped the pointer capture and could let a hold run away on pointer drift)
- watch: only route audio through Web Audio on iOS (where a backgrounded <video> is suspended);
  other platforms keep their reliable native audio, avoiding a foreground-silence risk if the
  AudioContext can't resume
- share: drop the unstable `create` mutation object from the auto-create effect deps (it changes
  every render); the links.data trigger + autoTried ref already fire it exactly once

The clipboard-on-focus finding is intended (the user explicitly asked for auto-paste on focus; the
read is best-effort and browsers reject it without a gesture, so no real prompt fires).
This commit is contained in:
2026-07-28 02:49:34 +02:00
parent d5c9fb9918
commit 7765d19813
4 changed files with 73 additions and 34 deletions
+57 -32
View File
@@ -819,6 +819,46 @@ function SyncBadge({ ok, label, hint }: { ok: boolean; label: string; hint?: str
);
}
// Module-level (NOT defined inside PriorityCell) so its identity is stable across PriorityCell
// re-renders — otherwise every hold-repeat tick would remount the button and drop the pointer
// capture set on pointerdown, letting a hold run away if the pointer drifts off the arrow.
function PriorityStepButton({
delta,
Icon,
label,
onHoldStart,
onStop,
onTap,
}: {
delta: number;
Icon: typeof ArrowUp;
label: string;
onHoldStart: (delta: number) => void;
onStop: () => void;
onTap: (delta: number) => void;
}) {
return (
<button
onPointerDown={(e) => {
// Keep receiving pointer events even if the pointer drifts off the small button mid-hold.
try {
e.currentTarget.setPointerCapture(e.pointerId);
} catch {
/* invalid pointer id (e.g. synthetic events) — capture is a nicety, not required */
}
onHoldStart(delta);
}}
onPointerUp={onStop}
onPointerCancel={onStop}
onClick={() => onTap(delta)}
className="text-muted hover:text-accent leading-none"
aria-label={label}
>
<Icon className="w-3.5 h-3.5" />
</button>
);
}
function PriorityCell({
c,
onPriority,
@@ -858,7 +898,7 @@ function PriorityCell({
tick();
}, 350);
};
const onClick = (delta: number) => {
const onTap = (delta: number) => {
if (suppressClick.current) {
suppressClick.current = false;
return;
@@ -866,35 +906,6 @@ function PriorityCell({
onPriority(delta);
};
const StepBtn = ({
delta,
Icon,
label,
}: {
delta: number;
Icon: typeof ArrowUp;
label: string;
}) => (
<button
onPointerDown={(e) => {
// Keep receiving pointer events even if the pointer drifts off the small button mid-hold.
try {
e.currentTarget.setPointerCapture(e.pointerId);
} catch {
/* invalid pointer id (e.g. synthetic events) — capture is a nicety, not required */
}
start(delta);
}}
onPointerUp={stop}
onPointerCancel={stop}
onClick={() => onClick(delta)}
className="text-muted hover:text-accent leading-none"
aria-label={label}
>
<Icon className="w-3.5 h-3.5" />
</button>
);
const commitEdit = () => {
const parsed = parseInt(draft, 10);
if (!Number.isNaN(parsed) && parsed !== c.priority) onSet(parsed);
@@ -941,8 +952,22 @@ function PriorityCell({
)}
</span>
<span className="inline-flex flex-col">
<StepBtn delta={1} Icon={ArrowUp} label={t("channels.row.raisePriority")} />
<StepBtn delta={-1} Icon={ArrowDown} label={t("channels.row.lowerPriority")} />
<PriorityStepButton
delta={1}
Icon={ArrowUp}
label={t("channels.row.raisePriority")}
onHoldStart={start}
onStop={stop}
onTap={onTap}
/>
<PriorityStepButton
delta={-1}
Icon={ArrowDown}
label={t("channels.row.lowerPriority")}
onHoldStart={start}
onStop={stop}
onTap={onTap}
/>
</span>
</div>
</Tooltip>