Replace generic slate/indigo dark theme with a custom editorial direction: warm paper neutrals, oxblood signal, Fraunces serif display, Inter body, JetBrains Mono for codes. SVG paper-texture noise overlay and thin rules across the app. Refactored: Home, Settings, JoinRoom, Pair, Share, plus every modal and panel (DropZone, DevicePairingPanel, PublicRoomPanel, ProfileSetup, TextShareModal, ReceiveDialog, TransferProgress, PeerList, PeerAvatar). Also drops three pre-existing Uint8Array/BlobPart strictness errors so the production build is green again. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
122 lines
3.4 KiB
TypeScript
122 lines
3.4 KiB
TypeScript
import { useState } from "react";
|
|
import { QRCodeSVG } from "qrcode.react";
|
|
import { useStore } from "../stores/useStore";
|
|
|
|
interface PublicRoomPanelProps {
|
|
onCreateRoom: () => void;
|
|
}
|
|
|
|
export default function PublicRoomPanel({ onCreateRoom }: PublicRoomPanelProps) {
|
|
const publicRoomCode = useStore((s) => s.publicRoomCode);
|
|
const publicRoomUrl = useStore((s) => s.publicRoomUrl);
|
|
const [showModal, setShowModal] = useState(false);
|
|
|
|
const handleClick = () => {
|
|
if (publicRoomCode && publicRoomUrl) {
|
|
setShowModal(true);
|
|
} else {
|
|
onCreateRoom();
|
|
setShowModal(true);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<button
|
|
onClick={handleClick}
|
|
className="paper-panel px-4 py-4 flex flex-col items-start gap-1
|
|
hover:border-ink transition-colors duration-fast ease-crisp
|
|
text-left"
|
|
>
|
|
<span className="font-mono text-[10px] uppercase tracking-[0.22em] text-ink-muted">
|
|
Public link
|
|
</span>
|
|
<span className="text-sm text-ink">Send to anyone →</span>
|
|
</button>
|
|
|
|
{showModal && (
|
|
<PublicRoomModal
|
|
code={publicRoomCode}
|
|
url={publicRoomUrl}
|
|
onClose={() => setShowModal(false)}
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
|
|
function PublicRoomModal({
|
|
code,
|
|
url,
|
|
onClose,
|
|
}: {
|
|
code: string | null;
|
|
url: string | null;
|
|
onClose: () => void;
|
|
}) {
|
|
const [copied, setCopied] = useState(false);
|
|
|
|
const copyToClipboard = () => {
|
|
if (!url) return;
|
|
navigator.clipboard.writeText(url);
|
|
setCopied(true);
|
|
setTimeout(() => setCopied(false), 1500);
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className="fixed inset-0 z-50 bg-ink/40 flex items-center justify-center p-4"
|
|
onClick={onClose}
|
|
>
|
|
<div
|
|
className="paper-panel shadow-lift rounded-sm p-6 max-w-sm w-full"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<div className="text-xs uppercase tracking-[0.2em] text-ink-muted">
|
|
Public link
|
|
</div>
|
|
<h3 className="font-display text-2xl text-ink mt-1 mb-5">
|
|
Receive from anyone
|
|
</h3>
|
|
|
|
{!code || !url ? (
|
|
<p className="text-sm text-ink-muted">Generating…</p>
|
|
) : (
|
|
<>
|
|
<div className="flex justify-center mb-5">
|
|
<div className="bg-paper p-3 border border-paper-edge rounded-sm">
|
|
<QRCodeSVG value={url} size={180} bgColor="#F5F0E6" fgColor="#1A1714" />
|
|
</div>
|
|
</div>
|
|
|
|
<div className="text-center space-y-2">
|
|
<p className="font-mono text-3xl font-medium text-signal tracking-[0.3em]">
|
|
{code.toUpperCase()}
|
|
</p>
|
|
<button
|
|
onClick={copyToClipboard}
|
|
className="text-xs text-ink-muted hover:text-ink transition-colors
|
|
font-mono break-all"
|
|
>
|
|
{copied ? "Copied ✓" : url}
|
|
</button>
|
|
</div>
|
|
|
|
<p className="text-xs text-ink-muted mt-5 leading-relaxed">
|
|
Anyone with this code or link can send you files. Expires in 10 minutes.
|
|
</p>
|
|
</>
|
|
)}
|
|
|
|
<button
|
|
onClick={onClose}
|
|
className="mt-6 w-full py-2.5 border border-paper-edge hover:border-ink
|
|
text-sm text-ink rounded-sm transition-colors duration-fast ease-crisp"
|
|
>
|
|
Close
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|