refactor: remove cross-network device pairing
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 48s
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 48s
Remove dead pair-code flow end-to-end (client, server, protocol) and the orphaned PublicRoomPanel/DevicePairingPanel/Pair components that were no longer reachable from the UI. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
54021f88d8
commit
35aca309c9
@ -139,19 +139,6 @@ function validateAvatar(avatar: unknown): string | undefined {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
// ── Pair codes (temporary mapping: short code → groupId, 5 min TTL) ──
|
||||
|
||||
import { customAlphabet } from "nanoid";
|
||||
const generatePairCode = customAlphabet("ABCDEFGHJKLMNPQRSTUVWXYZ23456789", 6);
|
||||
const pairCodes = new Map<string, { groupId: string; expiresAt: number }>();
|
||||
|
||||
setInterval(() => {
|
||||
const now = Date.now();
|
||||
for (const [code, entry] of pairCodes) {
|
||||
if (now > entry.expiresAt) pairCodes.delete(code);
|
||||
}
|
||||
}, 60_000);
|
||||
|
||||
// ── HTTP server (Hono) ──
|
||||
|
||||
const honoApp = buildApp();
|
||||
@ -224,12 +211,6 @@ wss.on("connection", (ws, req) => {
|
||||
case "wake-peer":
|
||||
handleWakePeer(client, msg);
|
||||
break;
|
||||
case "create-pair-code":
|
||||
handleCreatePairCode(client, msg);
|
||||
break;
|
||||
case "resolve-pair-code":
|
||||
handleResolvePairCode(client, msg);
|
||||
break;
|
||||
case "leave":
|
||||
handleLeave(client);
|
||||
break;
|
||||
@ -429,38 +410,6 @@ function handleSignal(client: Client, to: string, data: unknown): void {
|
||||
}
|
||||
}
|
||||
|
||||
function handleCreatePairCode(client: Client, msg: ClientMessage & { type: "create-pair-code" }): void {
|
||||
if (typeof msg.groupId !== "string" || msg.groupId.length === 0) return;
|
||||
|
||||
// Check if this groupId already has an active code
|
||||
for (const [code, entry] of pairCodes) {
|
||||
if (entry.groupId === msg.groupId && Date.now() < entry.expiresAt) {
|
||||
send(client.ws, { type: "pair-code-created", code });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const code = generatePairCode();
|
||||
pairCodes.set(code, { groupId: msg.groupId, expiresAt: Date.now() + 5 * 60 * 1000 });
|
||||
console.log(`[pair] created code ${code} for group ${msg.groupId.slice(0, 8)}...`);
|
||||
send(client.ws, { type: "pair-code-created", code });
|
||||
}
|
||||
|
||||
function handleResolvePairCode(client: Client, msg: ClientMessage & { type: "resolve-pair-code" }): void {
|
||||
if (typeof msg.code !== "string" || msg.code.length !== 6) {
|
||||
send(client.ws, { type: "error", code: "pair-code-not-found", message: "Code d'appairage invalide" });
|
||||
return;
|
||||
}
|
||||
const entry = pairCodes.get(msg.code.toUpperCase());
|
||||
if (!entry || Date.now() > entry.expiresAt) {
|
||||
send(client.ws, { type: "error", code: "pair-code-not-found", message: "Code d'appairage invalide ou expiré" });
|
||||
return;
|
||||
}
|
||||
pairCodes.delete(msg.code.toUpperCase());
|
||||
console.log(`[pair] resolved code ${msg.code} → group ${entry.groupId.slice(0, 8)}...`);
|
||||
send(client.ws, { type: "pair-code-resolved", groupId: entry.groupId });
|
||||
}
|
||||
|
||||
function handleLeave(client: Client): void {
|
||||
const lanRoom = roomManager.getRoomById(client.lanRoomId);
|
||||
if (!lanRoom) return;
|
||||
|
||||
@ -40,25 +40,13 @@ export interface WakePeerMessage {
|
||||
deviceId: string;
|
||||
}
|
||||
|
||||
export interface CreatePairCodeMessage {
|
||||
type: "create-pair-code";
|
||||
groupId: string;
|
||||
}
|
||||
|
||||
export interface ResolvePairCodeMessage {
|
||||
type: "resolve-pair-code";
|
||||
code: string;
|
||||
}
|
||||
|
||||
export type ClientMessage =
|
||||
| HelloMessage
|
||||
| CreatePublicRoomMessage
|
||||
| SignalMessage
|
||||
| LeaveMessage
|
||||
| SubscribePushMessage
|
||||
| WakePeerMessage
|
||||
| CreatePairCodeMessage
|
||||
| ResolvePairCodeMessage;
|
||||
| WakePeerMessage;
|
||||
|
||||
// ── Server → Client messages ──
|
||||
|
||||
@ -105,17 +93,7 @@ export interface SignalRelayMessage {
|
||||
data: unknown;
|
||||
}
|
||||
|
||||
export interface PairCodeCreatedMessage {
|
||||
type: "pair-code-created";
|
||||
code: string;
|
||||
}
|
||||
|
||||
export interface PairCodeResolvedMessage {
|
||||
type: "pair-code-resolved";
|
||||
groupId: string;
|
||||
}
|
||||
|
||||
export type ErrorCode = "room-not-found" | "room-expired" | "rate-limit" | "pair-code-not-found";
|
||||
export type ErrorCode = "room-not-found" | "room-expired" | "rate-limit";
|
||||
|
||||
export interface ErrorMessage {
|
||||
type: "error";
|
||||
@ -129,8 +107,6 @@ export type ServerMessage =
|
||||
| PeerJoinedMessage
|
||||
| PeerLeftMessage
|
||||
| SignalRelayMessage
|
||||
| PairCodeCreatedMessage
|
||||
| PairCodeResolvedMessage
|
||||
| ErrorMessage;
|
||||
|
||||
// ── Data channel messages (peer-to-peer) ──
|
||||
|
||||
@ -2,7 +2,6 @@ import { Routes, Route } from "react-router-dom";
|
||||
import Home from "./pages/Home";
|
||||
import JoinRoom from "./pages/JoinRoom";
|
||||
import Share from "./pages/Share";
|
||||
import Pair from "./pages/Pair";
|
||||
import Settings from "./pages/Settings";
|
||||
import Receive from "./pages/Receive";
|
||||
import Inbox from "./pages/Inbox";
|
||||
@ -12,7 +11,6 @@ export default function App() {
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/share" element={<Share />} />
|
||||
<Route path="/pair" element={<Pair />} />
|
||||
<Route path="/settings" element={<Settings />} />
|
||||
<Route path="/inbox" element={<Inbox />} />
|
||||
<Route path="/r/:id" element={<Receive />} />
|
||||
|
||||
@ -1,187 +0,0 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useProfileStore } from "../stores/useProfileStore";
|
||||
import { useStore } from "../stores/useStore";
|
||||
|
||||
interface DevicePairingPanelProps {
|
||||
onRequestCode: (groupId: string) => void;
|
||||
onResolveCode: (code: string) => void;
|
||||
}
|
||||
|
||||
export default function DevicePairingPanel({
|
||||
onRequestCode,
|
||||
onResolveCode,
|
||||
}: DevicePairingPanelProps) {
|
||||
const { groupId, setGroupId } = useProfileStore();
|
||||
const pairCode = useStore((s) => s.pairCode);
|
||||
const error = useStore((s) => s.error);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [mode, setMode] = useState<"show" | "enter">("show");
|
||||
const [inputCode, setInputCode] = useState("");
|
||||
const [pairError, setPairError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (showModal && mode === "show") {
|
||||
let gid = groupId;
|
||||
if (!gid) {
|
||||
gid = crypto.randomUUID();
|
||||
setGroupId(gid);
|
||||
}
|
||||
useStore.getState().setPairCode(null);
|
||||
onRequestCode(gid);
|
||||
}
|
||||
}, [showModal, mode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (error && error.includes("appairage")) {
|
||||
setPairError(error);
|
||||
useStore.getState().setError(null);
|
||||
}
|
||||
}, [error]);
|
||||
|
||||
const handleClose = () => {
|
||||
setShowModal(false);
|
||||
useStore.getState().setPairCode(null);
|
||||
setPairError(null);
|
||||
setInputCode("");
|
||||
};
|
||||
|
||||
const handleSubmitCode = () => {
|
||||
if (inputCode.length >= 6) {
|
||||
setPairError(null);
|
||||
onResolveCode(inputCode);
|
||||
}
|
||||
};
|
||||
|
||||
const currentGroupId = useProfileStore((s) => s.groupId);
|
||||
useEffect(() => {
|
||||
if (mode === "enter" && showModal && currentGroupId && currentGroupId !== groupId) {
|
||||
handleClose();
|
||||
}
|
||||
}, [currentGroupId]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => { setMode("show"); setShowModal(true); }}
|
||||
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">
|
||||
Pair device
|
||||
</span>
|
||||
<span className="text-sm text-ink">Link your own →</span>
|
||||
</button>
|
||||
|
||||
{showModal && (
|
||||
<div
|
||||
className="fixed inset-0 z-50 bg-ink/40 flex items-center justify-center p-4"
|
||||
onClick={handleClose}
|
||||
>
|
||||
<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">
|
||||
Pair device
|
||||
</div>
|
||||
<h3 className="font-display text-2xl text-ink mt-1 mb-5">
|
||||
Link your devices
|
||||
</h3>
|
||||
|
||||
{/* Tab switcher */}
|
||||
<div className="flex border border-paper-edge rounded-sm overflow-hidden mb-5">
|
||||
<button
|
||||
onClick={() => setMode("show")}
|
||||
className={`flex-1 py-2 text-xs uppercase tracking-[0.15em] transition-colors
|
||||
${mode === "show"
|
||||
? "bg-ink text-paper"
|
||||
: "bg-paper text-ink-muted hover:text-ink"}`}
|
||||
>
|
||||
Show code
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { setMode("enter"); setPairError(null); }}
|
||||
className={`flex-1 py-2 text-xs uppercase tracking-[0.15em] transition-colors
|
||||
${mode === "enter"
|
||||
? "bg-ink text-paper"
|
||||
: "bg-paper text-ink-muted hover:text-ink"}`}
|
||||
>
|
||||
Enter code
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{mode === "show" ? (
|
||||
<>
|
||||
<p className="text-sm text-ink-muted mb-5">
|
||||
Enter this code on your other device.
|
||||
</p>
|
||||
|
||||
{pairCode ? (
|
||||
<p className="font-mono text-4xl text-signal tracking-[0.3em] text-center">
|
||||
{pairCode}
|
||||
</p>
|
||||
) : (
|
||||
<div className="flex justify-center py-3">
|
||||
<div className="w-5 h-5 border border-signal border-t-transparent rounded-full animate-spin" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="text-xs text-ink-muted mt-5 leading-relaxed text-center">
|
||||
Code expires in 5 minutes. Pairing is permanent.
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p className="text-sm text-ink-muted mb-4">
|
||||
Enter the code shown on the other device.
|
||||
</p>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
value={inputCode}
|
||||
onChange={(e) => {
|
||||
setInputCode(e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, ""));
|
||||
setPairError(null);
|
||||
}}
|
||||
placeholder="ABC123"
|
||||
maxLength={6}
|
||||
autoFocus
|
||||
className="w-full px-4 py-3 bg-paper border border-paper-edge
|
||||
rounded-sm text-ink text-2xl text-center font-mono tracking-[0.3em]
|
||||
placeholder:text-ink-faint focus:outline-none focus:border-ink
|
||||
transition-colors duration-fast ease-crisp"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") handleSubmitCode();
|
||||
}}
|
||||
/>
|
||||
|
||||
{pairError && (
|
||||
<p className="text-sm text-fail mt-3">{pairError}</p>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={handleSubmitCode}
|
||||
disabled={inputCode.length < 6}
|
||||
className="mt-5 w-full py-2.5 bg-ink text-paper rounded-sm text-sm
|
||||
font-medium hover:bg-signal transition-colors duration-fast
|
||||
ease-crisp disabled:opacity-30 disabled:cursor-not-allowed"
|
||||
>
|
||||
Pair
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={handleClose}
|
||||
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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,121 +0,0 @@
|
||||
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="#2563EB" />
|
||||
</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>
|
||||
);
|
||||
}
|
||||
@ -240,18 +240,6 @@ export function useSignaling(joinCode?: string) {
|
||||
case "public-room-created":
|
||||
setPublicRoom(msg.code, msg.url, msg.expiresAt);
|
||||
break;
|
||||
case "pair-code-created":
|
||||
useStore.getState().setPairCode(msg.code);
|
||||
break;
|
||||
case "pair-code-resolved": {
|
||||
// Store the groupId and reconnect to join the group room
|
||||
useProfileStore.getState().setGroupId(msg.groupId);
|
||||
// Update the signaling client's profile so reconnect sends the new groupId
|
||||
signalingRef.current?.updateProfile({ groupId: msg.groupId });
|
||||
signalingRef.current?.disconnect();
|
||||
setTimeout(() => signalingRef.current?.connect(), 500);
|
||||
break;
|
||||
}
|
||||
case "error":
|
||||
setError(msg.message);
|
||||
break;
|
||||
@ -556,14 +544,6 @@ export function useSignaling(joinCode?: string) {
|
||||
signalingRef.current?.send({ type: "wake-peer", deviceId });
|
||||
}, []);
|
||||
|
||||
const requestPairCode = useCallback((groupId: string) => {
|
||||
signalingRef.current?.send({ type: "create-pair-code", groupId });
|
||||
}, []);
|
||||
|
||||
const resolvePairCode = useCallback((code: string) => {
|
||||
signalingRef.current?.send({ type: "resolve-pair-code", code: code.toUpperCase() });
|
||||
}, []);
|
||||
|
||||
return {
|
||||
sendFiles,
|
||||
sendText,
|
||||
@ -571,7 +551,5 @@ export function useSignaling(joinCode?: string) {
|
||||
rejectTransfer,
|
||||
createPublicRoom,
|
||||
wakePeer,
|
||||
requestPairCode,
|
||||
resolvePairCode,
|
||||
};
|
||||
}
|
||||
|
||||
@ -145,16 +145,11 @@ function HomeConnected() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer className="pt-6 mt-10 sm:pt-8 sm:mt-14 rule flex flex-col sm:flex-row sm:items-center gap-3 sm:justify-between text-xs text-ink-muted">
|
||||
<footer className="pt-6 mt-10 sm:pt-8 sm:mt-14 rule flex items-center justify-between text-xs text-ink-muted">
|
||||
<span>Nothing transits the server</span>
|
||||
<div className="flex items-center gap-4">
|
||||
<Link to="/pair" className="text-ink-muted hover:text-ink transition-colors duration-fast">
|
||||
Pair across networks →
|
||||
</Link>
|
||||
<Link to="/settings" className="text-ink hover:text-signal transition-colors duration-fast">
|
||||
Account →
|
||||
</Link>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useSearchParams, useNavigate } from "react-router-dom";
|
||||
import { useProfileStore } from "../stores/useProfileStore";
|
||||
|
||||
export default function Pair() {
|
||||
const [params] = useSearchParams();
|
||||
const navigate = useNavigate();
|
||||
const { setGroupId } = useProfileStore();
|
||||
|
||||
const groupId = params.get("g");
|
||||
|
||||
useEffect(() => {
|
||||
if (groupId) {
|
||||
setGroupId(groupId);
|
||||
const t = setTimeout(() => navigate("/", { replace: true }), 1500);
|
||||
return () => clearTimeout(t);
|
||||
}
|
||||
}, [groupId, setGroupId, navigate]);
|
||||
|
||||
if (!groupId) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center px-4">
|
||||
<div className="paper-panel px-6 py-5 text-center">
|
||||
<div className="text-xs uppercase tracking-[0.22em] text-fail">Invalid</div>
|
||||
<p className="mt-2 text-sm text-ink">This pairing link is malformed.</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center px-4">
|
||||
<div className="paper-panel px-8 py-8 max-w-sm w-full text-center">
|
||||
<div className="text-xs uppercase tracking-[0.22em] text-ok">Paired</div>
|
||||
<h1 className="font-display text-2xl text-ink mt-2 tracking-tight">
|
||||
Device linked
|
||||
</h1>
|
||||
<p className="text-sm text-ink-muted mt-3 leading-relaxed">
|
||||
Your devices will recognize each other automatically from now on.
|
||||
</p>
|
||||
<p className="text-xs text-ink-faint mt-6 font-mono uppercase tracking-widest">
|
||||
Redirecting…
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -37,9 +37,6 @@ interface AppState {
|
||||
transfers: TransferInfo[];
|
||||
incomingRequest: IncomingRequest | null;
|
||||
|
||||
// Pairing
|
||||
pairCode: string | null;
|
||||
|
||||
// UI
|
||||
showTextModal: boolean;
|
||||
selectedPeerId: string | null;
|
||||
@ -57,7 +54,6 @@ interface AppState {
|
||||
updateTransfer: (id: string, updates: Partial<TransferInfo>) => void;
|
||||
removeTransfer: (id: string) => void;
|
||||
setIncomingRequest: (request: IncomingRequest | null) => void;
|
||||
setPairCode: (code: string | null) => void;
|
||||
setShowTextModal: (show: boolean) => void;
|
||||
setSelectedPeerId: (peerId: string | null) => void;
|
||||
setError: (error: string | null) => void;
|
||||
@ -74,7 +70,6 @@ const initialState = {
|
||||
publicRoomExpiresAt: null,
|
||||
transfers: [],
|
||||
incomingRequest: null,
|
||||
pairCode: null,
|
||||
showTextModal: false,
|
||||
selectedPeerId: null,
|
||||
error: null,
|
||||
@ -119,8 +114,6 @@ export const useStore = create<AppState>((set) => ({
|
||||
|
||||
setIncomingRequest: (request) => set({ incomingRequest: request }),
|
||||
|
||||
setPairCode: (code) => set({ pairCode: code }),
|
||||
|
||||
setShowTextModal: (show) => set({ showTextModal: show }),
|
||||
|
||||
setSelectedPeerId: (peerId) => set({ selectedPeerId: peerId }),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user