Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions web/src/app/community/d/[id]/DeckDetailClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
} from "@/lib/community/visibility";
import { DESCRIPTION_MAX, TITLE_MAX } from "@/lib/community/validate";
import { useDeviceHost } from "@/lib/community/deviceHost";
import { useBackdropClose } from "@/lib/hooks/useBackdropClose";
import { useZipDownload, zipStatusUrl } from "@/lib/community/zipDownload";
import type { DeckPageItem } from "@/lib/community/server/serialize";
import { captureEvent } from "@/lib/posthogEvents";
Expand Down Expand Up @@ -607,6 +608,7 @@ function PerformanceModal({
const [json, setJson] = useState(initialJson ?? "");
const [error, setError] = useState<string | null>(null);
const [busy, setBusy] = useState(false);
const closeOnBackdrop = useBackdropClose(onClose);

const send = async (value: string | null) => {
setBusy(true);
Expand Down Expand Up @@ -643,7 +645,7 @@ function PerformanceModal({
};

return (
<div className={styles.modalOverlay} role="dialog" aria-modal="true" onClick={onClose}>
<div className={styles.modalOverlay} role="dialog" aria-modal="true" {...closeOnBackdrop}>
<div className={styles.modalCard} onClick={(event) => event.stopPropagation()}>
<div className={styles.modalHeader}>
<span>Deck performance</span>
Expand Down Expand Up @@ -715,6 +717,7 @@ function EditDeckModal({
const [description, setDescription] = useState(initialDescription ?? "");
const [error, setError] = useState<string | null>(null);
const [busy, setBusy] = useState(false);
const closeOnBackdrop = useBackdropClose(onClose);

const save = async () => {
const trimmed = title.trim();
Expand Down Expand Up @@ -745,7 +748,7 @@ function EditDeckModal({
};

return (
<div className={styles.modalOverlay} role="dialog" aria-modal="true" onClick={onClose}>
<div className={styles.modalOverlay} role="dialog" aria-modal="true" {...closeOnBackdrop}>
<div className={styles.modalCard} onClick={(event) => event.stopPropagation()}>
<div className={styles.modalHeader}>
<span>Edit deck details</span>
Expand Down
4 changes: 3 additions & 1 deletion web/src/components/community/AddHeaderModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { authClient } from "@/lib/community/auth-client";
import { COMMUNITY_FETCH_INIT, communityApiUrl } from "@/lib/community/apiBase";
import { useSubmitLatch } from "@/lib/community/submitLatch";
import { useDialog } from "@/lib/hooks/useDialog";
import { useBackdropClose } from "@/lib/hooks/useBackdropClose";
import AuthModal from "./AuthModal";
import styles from "./Community.module.css";

Expand Down Expand Up @@ -56,6 +57,7 @@ export default function AddHeaderModal({
// One request per press, decided in the click's own tick (#455).
const latch = useSubmitLatch();
const cardRef = useDialog<HTMLDivElement>(onClose);
const closeOnBackdrop = useBackdropClose(onClose);

const moderating = mode === "moderate" || mode === "moderate-port";

Expand Down Expand Up @@ -126,7 +128,7 @@ export default function AddHeaderModal({
: "Add firmware header (.h)";

return (
<div className={styles.modalOverlay} role="dialog" aria-modal="true" onClick={onClose}>
<div className={styles.modalOverlay} role="dialog" aria-modal="true" {...closeOnBackdrop}>
<div
ref={cardRef}
tabIndex={-1}
Expand Down
4 changes: 3 additions & 1 deletion web/src/components/community/AddPerformanceModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { authClient } from "@/lib/community/auth-client";
import { COMMUNITY_FETCH_INIT, communityApiUrl } from "@/lib/community/apiBase";
import { useSubmitLatch } from "@/lib/community/submitLatch";
import { useDialog } from "@/lib/hooks/useDialog";
import { useBackdropClose } from "@/lib/hooks/useBackdropClose";
import { readPerformanceFile } from "@/lib/community/performanceFile";
import AuthModal from "./AuthModal";
import styles from "./Community.module.css";
Expand Down Expand Up @@ -34,6 +35,7 @@ export default function AddPerformanceModal({
// One request per press, decided in the click's own tick (#455).
const latch = useSubmitLatch();
const cardRef = useDialog<HTMLDivElement>(onClose);
const closeOnBackdrop = useBackdropClose(onClose);

// The Director saves .pfs and the panel plays it, so that is what people
// have. It is decoded here into the canonical form the site stores, which
Expand Down Expand Up @@ -79,7 +81,7 @@ export default function AddPerformanceModal({
};

return (
<div className={styles.modalOverlay} role="dialog" aria-modal="true" onClick={onClose}>
<div className={styles.modalOverlay} role="dialog" aria-modal="true" {...closeOnBackdrop}>
<div
ref={cardRef}
tabIndex={-1}
Expand Down
4 changes: 3 additions & 1 deletion web/src/components/community/AuthModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useState, type ReactNode } from "react";
import { authClient, dummyEmailFor } from "@/lib/community/auth-client";
import { PASSWORD_MIN, USERNAME_RE } from "@/lib/community/validate";
import { useDialog } from "@/lib/hooks/useDialog";
import { useBackdropClose } from "@/lib/hooks/useBackdropClose";
import styles from "./Community.module.css";

// Username + password auth. Email is an optional recovery field only — when
Expand Down Expand Up @@ -139,8 +140,9 @@ export default function AuthModal({ onClose, onAuthed, embedded = false }: Props
// around the form owns both.
function AuthWindow({ onClose, children }: { onClose: () => void; children: ReactNode }) {
const cardRef = useDialog<HTMLDivElement>(onClose);
const closeOnBackdrop = useBackdropClose(onClose);
return (
<div className={styles.modalOverlay} role="dialog" aria-modal="true" onClick={onClose}>
<div className={styles.modalOverlay} role="dialog" aria-modal="true" {...closeOnBackdrop}>
<div
ref={cardRef}
tabIndex={-1}
Expand Down
4 changes: 3 additions & 1 deletion web/src/components/community/DeckPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from "@/lib/community/deck";
import { useDeviceHost } from "@/lib/community/deviceHost";
import { useModuleBuild } from "@/lib/community/useModuleBuild";
import { useBackdropClose } from "@/lib/hooks/useBackdropClose";
import AuthModal from "./AuthModal";
import ShareDeckModal from "./ShareDeckModal";
import styles from "./Community.module.css";
Expand Down Expand Up @@ -92,6 +93,7 @@ export default function DeckPanel() {
setNeedsAuth(false);
setOpen(false);
};
const closeOnBackdrop = useBackdropClose(close);

// No builds configured (e.g. the Vercel mirror) → nothing here works.
if (!buildsConfigured()) return null;
Expand All @@ -111,7 +113,7 @@ export default function DeckPanel() {
</button>

{open && (
<div className={styles.modalOverlay} role="dialog" aria-modal="true" onClick={close}>
<div className={styles.modalOverlay} role="dialog" aria-modal="true" {...closeOnBackdrop}>
<div
className={`${styles.modalCard} ${styles.modalCardWide}`}
onClick={(event) => event.stopPropagation()}
Expand Down
4 changes: 3 additions & 1 deletion web/src/components/community/EditDetailsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from "@/lib/community/validate";
import { LICENSE_OPTIONS, forkLicenseOptions, licenseBySpdx } from "@/lib/pattern/share";
import { useDialog } from "@/lib/hooks/useDialog";
import { useBackdropClose } from "@/lib/hooks/useBackdropClose";
import { captureEvent } from "@/lib/posthogEvents";
import { COMMUNITY_FETCH_INIT, communityApiUrl } from "@/lib/community/apiBase";
import {
Expand Down Expand Up @@ -68,6 +69,7 @@ export default function EditDetailsModal({
// before the hook could see which button opened the window, so the focus
// had nowhere to go back to.
const titleRef = useDialog<HTMLInputElement>(onClose);
const closeOnBackdrop = useBackdropClose(onClose);

// What this pattern may be relicensed to. The pattern's CURRENT licence is
// always included even when it is no longer offered (MIT, CC0): a retired
Expand Down Expand Up @@ -113,7 +115,7 @@ export default function EditDetailsModal({
};

return (
<div className={styles.modalOverlay} role="dialog" aria-modal="true" onClick={onClose}>
<div className={styles.modalOverlay} role="dialog" aria-modal="true" {...closeOnBackdrop}>
<div className={styles.modalCard} onClick={(event) => event.stopPropagation()}>
<div className={styles.modalHeader}>
<span>Edit pattern details</span>
Expand Down
4 changes: 3 additions & 1 deletion web/src/components/community/PublishModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useEffect, useRef, useState } from "react";
import { useRouter } from "next/navigation";
import { useDialog } from "@/lib/hooks/useDialog";
import { useBackdropClose } from "@/lib/hooks/useBackdropClose";
import { authClient } from "@/lib/community/auth-client";
import { COMMUNITY_FETCH_INIT, communityApiUrl } from "@/lib/community/apiBase";
import { useSubmitLatch } from "@/lib/community/submitLatch";
Expand Down Expand Up @@ -127,6 +128,7 @@ export default function PublishModal({
// field's autoFocus used to move it before the hook could look, and the
// field is not there yet while the session is being checked.
const cardRef = useDialog<HTMLDivElement>(onClose);
const closeOnBackdrop = useBackdropClose(onClose);
const titleRef = useRef<HTMLInputElement>(null);
const formShown = !isPending && Boolean(session);
useEffect(() => {
Expand Down Expand Up @@ -263,7 +265,7 @@ export default function PublishModal({
};

return (
<div className={styles.modalOverlay} role="dialog" aria-modal="true" onClick={onClose}>
<div className={styles.modalOverlay} role="dialog" aria-modal="true" {...closeOnBackdrop}>
<div
ref={cardRef}
tabIndex={-1}
Expand Down
4 changes: 3 additions & 1 deletion web/src/components/community/ReportModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
type ReportTargetType,
} from "@/lib/community/validate";
import { useDialog } from "@/lib/hooks/useDialog";
import { useBackdropClose } from "@/lib/hooks/useBackdropClose";
import { captureEvent } from "@/lib/posthogEvents";
import AuthModal from "./AuthModal";
import styles from "./Community.module.css";
Expand Down Expand Up @@ -41,6 +42,7 @@ export default function ReportModal({
const latch = useSubmitLatch();
const [sent, setSent] = useState(false);
const cardRef = useDialog<HTMLDivElement>(onClose);
const closeOnBackdrop = useBackdropClose(onClose);

const submit = async () => {
if (!latch.take()) return;
Expand Down Expand Up @@ -69,7 +71,7 @@ export default function ReportModal({
};

return (
<div className={styles.modalOverlay} role="dialog" aria-modal="true" onClick={onClose}>
<div className={styles.modalOverlay} role="dialog" aria-modal="true" {...closeOnBackdrop}>
<div
ref={cardRef}
tabIndex={-1}
Expand Down
4 changes: 3 additions & 1 deletion web/src/components/community/SendModuleModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useDeviceHost } from "@/lib/community/deviceHost";
import { captureEvent } from "@/lib/posthogEvents";
import { useModuleBuild } from "@/lib/community/useModuleBuild";
import { useZipDownload } from "@/lib/community/zipDownload";
import { useBackdropClose } from "@/lib/hooks/useBackdropClose";
import AuthModal from "./AuthModal";
import { ZipInstallNote, ZipProgress } from "./ZipDownload";
import styles from "./Community.module.css";
Expand Down Expand Up @@ -48,8 +49,9 @@ export default function SendModuleModal(
}
),
) {
const closeOnBackdrop = useBackdropClose(props.onClose);
return (
<div className={styles.modalOverlay} role="dialog" aria-modal="true" onClick={props.onClose}>
<div className={styles.modalOverlay} role="dialog" aria-modal="true" {...closeOnBackdrop}>
<div
className={`${styles.modalCard} ${styles.modalCardWide}`}
onClick={(event) => event.stopPropagation()}
Expand Down
4 changes: 3 additions & 1 deletion web/src/components/community/ShareDeckModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { COMMUNITY_FETCH_INIT, communityApiUrl } from "@/lib/community/apiBase";
import { useSubmitLatch } from "@/lib/community/submitLatch";
import { PUBLIC_DECKS_MAX, type CollectedPattern } from "@/lib/community/deck";
import { DESCRIPTION_MAX, TITLE_MAX } from "@/lib/community/validate";
import { useBackdropClose } from "@/lib/hooks/useBackdropClose";
import {
VISIBILITY_HINTS,
VISIBILITY_LABELS,
Expand Down Expand Up @@ -40,6 +41,7 @@ export default function ShareDeckModal({
const [busy, setBusy] = useState(false);
// One request per press, decided in the click's own tick (#455).
const latch = useSubmitLatch();
const closeOnBackdrop = useBackdropClose(onClose);

const share = async () => {
const trimmed = title.trim();
Expand Down Expand Up @@ -83,7 +85,7 @@ export default function ShareDeckModal({
};

return (
<div className={styles.modalOverlay} role="dialog" aria-modal="true" onClick={onClose}>
<div className={styles.modalOverlay} role="dialog" aria-modal="true" {...closeOnBackdrop}>
<div className={styles.modalCard} onClick={(event) => event.stopPropagation()}>
<div className={styles.modalHeader}>
<span>Share deck</span>
Expand Down
4 changes: 3 additions & 1 deletion web/src/components/community/ShareDeckPackModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import { useEffect, useRef, useState } from "react";
import { useBackdropClose } from "@/lib/hooks/useBackdropClose";
import styles from "./Community.module.css";

// Handing a deck to somebody.
Expand Down Expand Up @@ -39,6 +40,7 @@ export default function ShareDeckPackModal({
}) {
const [copied, setCopied] = useState(false);
const inputRef = useRef<HTMLInputElement | null>(null);
const closeOnBackdrop = useBackdropClose(onClose);

// Escape closes it. This one is a thing you glance at and leave, so it had
// the key before the form windows took it from lib/hooks/useDialog.
Expand All @@ -62,7 +64,7 @@ export default function ShareDeckPackModal({
};

return (
<div className={styles.modalOverlay} role="dialog" aria-modal="true" onClick={onClose}>
<div className={styles.modalOverlay} role="dialog" aria-modal="true" {...closeOnBackdrop}>
<div className={styles.modalCard} onClick={(event) => event.stopPropagation()}>
<div className={styles.modalHeader}>
<span className={styles.modalKicker}>Share</span>
Expand Down
Loading
Loading