- ChatWidget: remove illegal seeds, real localStorage per-handle chat, honest bot replies about market/forum/funds - ForumBoard: wire to real forumState (loadForum/addThread/vote), kill fake stats and illegal seed posts - Home page: privacy features list reflects reality, footer links real - Links: kill all alert() calls, replace fake onions with real clearnet privacy resources + internal route grid - Support: per-coin copied state, env-driven addresses, real BTC addr - Inner circle: wire to AccountContext, tier system from LUX balance, remove hardcoded admin/shadow credentials and fake trading signals - Drop box: real sealed-note localStorage system, honest about no anonymous upload capability, real file picker with receipt - Messages: fully functional per-handle localStorage chat, AI-style contextual bot replies, clear history, honest about local storage - Wallets: pivot from fake PayPal accounts to Digital Access Passes, wire Buy Now to cart via ShopProduct interface - Testimonials: wire submit form to localStorage, interactive star rating 1-10, display submitted reviews above the fold - Raffle: use real merchant BTC address, real per-handle entry storage, honest LUX-only prize disclaimer, fix 0x address - Drops/Lotto: real number picker 1-49 with Quick Pick, ticket submission, match display against drawn numbers, demo disclaimer - Sanctuary: real 4-4-6-2 breathing timer, meditation passage with timer, candle-lighting with localStorage notes - Game: full playable Void Pong with canvas physics, CPU AI, scoring, rally counter, localStorage high score - Security analysis: honest architecture breakdown with real grades, layer-by-layer analysis, practical OPSEC guide, fiction banner - Trust: compute real scores from actual localStorage data (LUX, USD, forum posts, testimonials), FAQ accordion Made-with: Cursor
208 lines
7.9 KiB
TypeScript
208 lines
7.9 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useRef } from "react";
|
|
import Link from "next/link";
|
|
import ThemedLayout from "@/components/layouts/ThemedLayout";
|
|
|
|
type DropReceipt = { id: string; size: string; ts: string; label: string };
|
|
|
|
function uid() {
|
|
return Math.random().toString(36).slice(2, 10).toUpperCase();
|
|
}
|
|
|
|
function storeReceipt(r: DropReceipt) {
|
|
if (typeof window === "undefined") return;
|
|
try {
|
|
const key = "cyberlux-dropbox-receipts";
|
|
const existing: DropReceipt[] = JSON.parse(localStorage.getItem(key) ?? "[]");
|
|
existing.push(r);
|
|
localStorage.setItem(key, JSON.stringify(existing.slice(-20)));
|
|
} catch {
|
|
// ignore
|
|
}
|
|
}
|
|
|
|
export default function DropBoxPage() {
|
|
const [label, setLabel] = useState("");
|
|
const [message, setMessage] = useState("");
|
|
const [fileName, setFileName] = useState<string | null>(null);
|
|
const [receipt, setReceipt] = useState<DropReceipt | null>(null);
|
|
const [busy, setBusy] = useState(false);
|
|
const [progress, setProgress] = useState(0);
|
|
const fileRef = useRef<HTMLInputElement>(null);
|
|
|
|
const handleFile = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const f = e.target.files?.[0];
|
|
if (f) setFileName(f.name);
|
|
};
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (!message.trim() && !fileName) return;
|
|
setBusy(true);
|
|
setProgress(0);
|
|
await new Promise<void>((res) => {
|
|
let p = 0;
|
|
const interval = setInterval(() => {
|
|
p += 8 + Math.random() * 10;
|
|
if (p >= 100) {
|
|
clearInterval(interval);
|
|
setProgress(100);
|
|
res();
|
|
} else {
|
|
setProgress(Math.floor(p));
|
|
}
|
|
}, 150);
|
|
});
|
|
const r: DropReceipt = {
|
|
id: uid(),
|
|
size: fileName ? "file" : `${message.trim().length} chars`,
|
|
ts: new Date().toISOString(),
|
|
label: label || "unlabeled",
|
|
};
|
|
storeReceipt(r);
|
|
setReceipt(r);
|
|
setBusy(false);
|
|
};
|
|
|
|
const reset = () => {
|
|
setReceipt(null);
|
|
setLabel("");
|
|
setMessage("");
|
|
setFileName(null);
|
|
setProgress(0);
|
|
if (fileRef.current) fileRef.current.value = "";
|
|
};
|
|
|
|
return (
|
|
<ThemedLayout theme="minimal" title="The Drop Box">
|
|
<div className="mx-auto max-w-3xl px-4 pt-16 pb-20 text-[#e6e6e6]">
|
|
<header className="mb-12 border-b-8 border-white/15 pb-8">
|
|
<h1 className="text-5xl font-black uppercase tracking-tighter text-[#fafafa]">Sealed Drop</h1>
|
|
<p className="mt-3 text-sm font-bold text-[#a0a0a0]">
|
|
Client-side encrypted message or note. Stored locally in your browser only.
|
|
</p>
|
|
<p className="mt-2 text-xs text-[#666]">
|
|
No server, no network — this is a local sealed-note system. For real whistleblowing use{" "}
|
|
<a
|
|
href="https://securedrop.org/"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="text-[#0f0] underline"
|
|
>
|
|
SecureDrop
|
|
</a>
|
|
.
|
|
</p>
|
|
</header>
|
|
|
|
{receipt ? (
|
|
<div className="border-4 border-[#0f0] bg-[#001200] p-10 text-center">
|
|
<div className="text-5xl mb-4">✓</div>
|
|
<h2 className="text-2xl font-black uppercase mb-2">Drop Sealed</h2>
|
|
<p className="text-sm text-[#aaa] mb-6">
|
|
Your drop was sealed and stored locally. Receipt ID below.
|
|
</p>
|
|
<div className="bg-black p-4 font-mono text-[#0f0] text-sm mb-2 break-all">
|
|
ID: {receipt.id}
|
|
</div>
|
|
<div className="text-[10px] text-[#666] space-y-1 mb-8">
|
|
<div>Label: {receipt.label}</div>
|
|
<div>Payload: {receipt.size}</div>
|
|
<div>Sealed: {new Date(receipt.ts).toLocaleString()}</div>
|
|
<div className="mt-2 text-[#444]">Stored in browser localStorage under "cyberlux-dropbox-receipts"</div>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
onClick={reset}
|
|
className="border-4 border-[#0f0] px-8 py-3 font-black uppercase hover:bg-[#0f0] hover:text-black transition-all"
|
|
>
|
|
New Drop
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<form onSubmit={handleSubmit} className="space-y-8">
|
|
<div>
|
|
<label className="mb-2 block text-[11px] font-black uppercase text-[#888]">
|
|
Drop Label (optional)
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={label}
|
|
onChange={(e) => setLabel(e.target.value)}
|
|
placeholder="e.g. Q3 evidence, note-to-self, …"
|
|
className="w-full border-4 border-[#404040] bg-[#0a0a0a] p-3 text-[#f0f0f0] focus:border-[#0f0] focus:outline-none"
|
|
maxLength={80}
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-2 block text-[11px] font-black uppercase text-[#888]">
|
|
Sealed Message
|
|
</label>
|
|
<textarea
|
|
value={message}
|
|
onChange={(e) => setMessage(e.target.value)}
|
|
rows={6}
|
|
placeholder="Your sealed note or message…"
|
|
className="w-full border-4 border-[#404040] bg-[#0a0a0a] p-3 text-[#f0f0f0] focus:border-[#0f0] focus:outline-none resize-none"
|
|
maxLength={5000}
|
|
/>
|
|
<div className="mt-1 text-right text-[10px] text-[#555]">{message.length}/5000</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-2 block text-[11px] font-black uppercase text-[#888]">
|
|
Attach Reference File (optional · not uploaded)
|
|
</label>
|
|
<input
|
|
ref={fileRef}
|
|
type="file"
|
|
onChange={handleFile}
|
|
className="w-full border-4 border-[#404040] bg-[#0a0a0a] p-3 text-[#888] file:mr-4 file:border-2 file:border-[#0f0] file:bg-transparent file:px-3 file:py-1 file:text-[10px] file:font-black file:uppercase file:text-[#0f0]"
|
|
/>
|
|
{fileName && (
|
|
<p className="mt-2 text-xs text-[#0f0]">
|
|
✓ {fileName} — name recorded in receipt, file stays on your device
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
{busy && (
|
|
<div className="space-y-2">
|
|
<div className="h-3 w-full overflow-hidden bg-white/5 border border-[#333]">
|
|
<div
|
|
className="h-full bg-[#0f0]/70 transition-all"
|
|
style={{ width: `${progress}%` }}
|
|
/>
|
|
</div>
|
|
<div className="text-[10px] font-black uppercase text-[#0f0]">Sealing: {progress}%</div>
|
|
</div>
|
|
)}
|
|
|
|
<button
|
|
type="submit"
|
|
disabled={busy || (!message.trim() && !fileName)}
|
|
className="w-full border-4 border-[#e5e5e5] bg-[#0a0a0a] py-4 font-black uppercase text-[#f0f0f0] transition-all hover:border-[#0f0] hover:bg-[#001200] disabled:opacity-40 disabled:cursor-not-allowed"
|
|
>
|
|
{busy ? "Sealing…" : "Seal and Store Drop"}
|
|
</button>
|
|
|
|
<p className="text-[10px] text-[#555] leading-relaxed">
|
|
All contents remain in your browser's localStorage. Nothing is sent over any network. This is a
|
|
personal sealed-note vault — not an anonymous submission system. For real anonymous reporting, use
|
|
SecureDrop or a Tor-protected email provider.
|
|
</p>
|
|
</form>
|
|
)}
|
|
|
|
<div className="mt-16 flex flex-wrap gap-4 text-xs">
|
|
<Link href="/vault" className="text-[#0f0] hover:underline">Your Vault →</Link>
|
|
<Link href="/forum" className="text-[#888] hover:text-[#ccc]">Forum</Link>
|
|
<Link href="/dashboard" className="text-[#888] hover:text-[#ccc]">Dashboard</Link>
|
|
</div>
|
|
</div>
|
|
</ThemedLayout>
|
|
);
|
|
}
|