feat: larger UI

This commit is contained in:
kieran 2024-12-16 15:49:09 +00:00
parent 623bb8f0b5
commit d5904d4ee0
No known key found for this signature in database
GPG Key ID: DE71CEB3925BE941
5 changed files with 43 additions and 17 deletions

View File

@ -28,7 +28,7 @@ async fn main() -> Result<(), anyhow::Error> {
let args: ProgramArgs = ProgramArgs::parse(); let args: ProgramArgs = ProgramArgs::parse();
let mut report: HashMap<String, HashSet<Uuid>> = HashMap::new(); let mut report: HashMap<String, HashSet<String>> = HashMap::new();
let mut binding = NostrCursor::new(args.archive); let mut binding = NostrCursor::new(args.archive);
let mut cursor = Box::pin(binding.walk()); let mut cursor = Box::pin(binding.walk());
@ -44,7 +44,7 @@ async fn main() -> Result<(), anyhow::Error> {
warn!("Invalid base58 id {}", g); warn!("Invalid base58 id {}", g);
continue; continue;
}; };
let uuid = if let Ok(u) = Uuid::from_slice_le(base58.as_slice()) { let _uuid = if let Ok(u) = Uuid::from_slice_le(base58.as_slice()) {
u u
} else { } else {
warn!("Invalid uuid {}", g); warn!("Invalid uuid {}", g);
@ -52,9 +52,9 @@ async fn main() -> Result<(), anyhow::Error> {
}; };
info!("Got link: {} => {}", g, e.pubkey); info!("Got link: {} => {}", g, e.pubkey);
if let Some(ur) = report.get_mut(&e.pubkey) { if let Some(ur) = report.get_mut(&e.pubkey) {
ur.insert(uuid); ur.insert(g.to_string());
} else { } else {
report.insert(e.pubkey.clone(), HashSet::from([uuid])); report.insert(e.pubkey.clone(), HashSet::from([g.to_string()]));
} }
} }
} }

View File

@ -3,7 +3,7 @@ import Upload from "./views/upload";
function App() { function App() {
return ( return (
<div className="flex flex-col gap-4 w-[700px] mx-auto mt-4"> <div className="flex flex-col gap-4 mx-auto mt-4 max-w-[1920px] px-10">
<Header /> <Header />
<Upload /> <Upload />
</div> </div>

View File

@ -5,7 +5,7 @@ export default function Button({
className, className,
onClick, onClick,
...props ...props
}: { onClick?: (e: React.MouseEvent) => Promise<void> } & Omit< }: { onClick?: (e: React.MouseEvent) => Promise<void> | void } & Omit<
HTMLProps<HTMLButtonElement>, HTMLProps<HTMLButtonElement>,
"type" | "onClick" "type" | "onClick"
>) { >) {

View File

@ -18,7 +18,7 @@ export default function FileList({
onPage, onPage,
onDelete, onDelete,
}: { }: {
files: Array<File | NostrEvent>; files: Array<File | NostrEvent | FileInfo>;
pages?: number; pages?: number;
page?: number; page?: number;
onPage?: (n: number) => void; onPage?: (n: number) => void;
@ -30,9 +30,9 @@ export default function FileList({
} }
function renderInner(f: FileInfo) { function renderInner(f: FileInfo) {
if (f.type?.startsWith("image/")) { if (f.type?.startsWith("image/") || !f.type) {
return ( return (
<img src={f.url} className="w-full h-full object-cover object-center" /> <img src={f.url} className="w-full h-full object-contain object-center" loading="lazy" />
); );
} else if (f.type?.startsWith("video/")) { } else if (f.type?.startsWith("video/")) {
return ( return (
@ -43,7 +43,10 @@ export default function FileList({
} }
} }
function getInfo(f: File | NostrEvent): FileInfo { function getInfo(f: File | NostrEvent | FileInfo): FileInfo {
if ("url" in f) {
return f;
}
if ("created_at" in f) { if ("created_at" in f) {
return { return {
id: f.tags.find((a) => a[0] === "x")![1], id: f.tags.find((a) => a[0] === "x")![1],
@ -88,7 +91,7 @@ export default function FileList({
function showGrid() { function showGrid() {
return ( return (
<div className="grid grid-cols-4 gap-2"> <div className="grid gap-2 grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 2xl:grid-cols-10">
{files.map((a) => { {files.map((a) => {
const info = getInfo(a); const info = getInfo(a);
@ -110,12 +113,12 @@ export default function FileList({
<a href={info.url} className="underline" target="_blank"> <a href={info.url} className="underline" target="_blank">
Link Link
</a> </a>
<a href="#" onClick={e => { {onDelete && <a href="#" onClick={e => {
e.preventDefault(); e.preventDefault();
onDelete?.(info.id) onDelete?.(info.id)
}} className="underline"> }} className="underline">
Delete Delete
</a> </a>}
</div> </div>
</div> </div>
{renderInner(info)} {renderInner(info)}
@ -166,12 +169,12 @@ export default function FileList({
<a href={info.url} className="underline" target="_blank"> <a href={info.url} className="underline" target="_blank">
Link Link
</a> </a>
<a href="#" onClick={e => { {onDelete && <a href="#" onClick={e => {
e.preventDefault(); e.preventDefault();
onDelete?.(info.id) onDelete?.(info.id)
}} className="underline"> }} className="underline">
Delete Delete
</a> </a>}
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -8,10 +8,12 @@ import usePublisher from "../hooks/publisher";
import { Nip96, Nip96FileList } from "../upload/nip96"; import { Nip96, Nip96FileList } from "../upload/nip96";
import { AdminSelf, Route96 } from "../upload/admin"; import { AdminSelf, Route96 } from "../upload/admin";
import { FormatBytes } from "../const"; import { FormatBytes } from "../const";
import Report from "../report.json";
export default function Upload() { export default function Upload() {
const [type, setType] = useState<"blossom" | "nip96">("blossom"); const [type, setType] = useState<"blossom" | "nip96">("blossom");
const [noCompress, setNoCompress] = useState(false); const [noCompress, setNoCompress] = useState(false);
const [showLegacy, setShowLegacy] = useState(false);
const [toUpload, setToUpload] = useState<File>(); const [toUpload, setToUpload] = useState<File>();
const [self, setSelf] = useState<AdminSelf>(); const [self, setSelf] = useState<AdminSelf>();
const [error, setError] = useState<string>(); const [error, setError] = useState<string>();
@ -24,6 +26,8 @@ export default function Upload() {
const login = useLogin(); const login = useLogin();
const pub = usePublisher(); const pub = usePublisher();
const myLegacyFiles = login ? (Report as Record<string, Array<string>>)[login.pubkey] : [];
const url = import.meta.env.VITE_API_URL || `${location.protocol}//${location.host}`; const url = import.meta.env.VITE_API_URL || `${location.protocol}//${location.host}`;
async function doUpload() { async function doUpload() {
if (!pub) return; if (!pub) return;
@ -58,7 +62,7 @@ export default function Upload() {
setError(undefined); setError(undefined);
const uploader = new Nip96(url, pub); const uploader = new Nip96(url, pub);
await uploader.loadInfo(); await uploader.loadInfo();
const result = await uploader.listFiles(n, 12); const result = await uploader.listFiles(n, 50);
setListedFiles(result); setListedFiles(result);
} catch (e) { } catch (e) {
if (e instanceof Error) { if (e instanceof Error) {
@ -76,7 +80,7 @@ export default function Upload() {
try { try {
setError(undefined); setError(undefined);
const uploader = new Route96(url, pub); const uploader = new Route96(url, pub);
const result = await uploader.listFiles(n, 12); const result = await uploader.listFiles(n, 50);
setAdminListedFiles(result); setAdminListedFiles(result);
} catch (e) { } catch (e) {
if (e instanceof Error) { if (e instanceof Error) {
@ -176,11 +180,30 @@ export default function Upload() {
List Uploads List Uploads
</Button>} </Button>}
{self && <div className="flex justify-between font-medium"> {self && <div className="flex justify-between font-medium">
<div>Uploads: {self.file_count.toLocaleString()}</div> <div>Uploads: {self.file_count.toLocaleString()}</div>
<div>Total Size: {FormatBytes(self.total_size)}</div> <div>Total Size: {FormatBytes(self.total_size)}</div>
</div>} </div>}
{login && myLegacyFiles.length > 0 && (
<div className="flex flex-col gap-4 font-bold">
You have {myLegacyFiles.length.toLocaleString()} files which can be migrated from void.cat
<div className="flex gap-2">
<Button>
Migrate Files
</Button>
<Button onClick={() => setShowLegacy(true)}>
Show Files
</Button>
</div>
</div>
)}
{showLegacy && (
<FileList
files={myLegacyFiles.map(f => ({ id: f, url: `https://void.cat/d/${f}` }))}
/>
)}
{listedFiles && ( {listedFiles && (
<FileList <FileList
files={listedFiles.files} files={listedFiles.files}