From 1c48a23fabc1b9af8bc0bd5a080eb48442b76ef4 Mon Sep 17 00:00:00 2001 From: lyonbot Date: Fri, 3 Jan 2025 21:53:44 +0800 Subject: [PATCH] feat: delete record and copy message --- src/database.ts | 27 +++++++++++++++ src/index.ts | 8 +++++ web/src/App.tsx | 43 +++++++++++++++++------ web/src/components/UploadRecords.tsx | 52 ++++++++++++++++++++++++---- 4 files changed, 113 insertions(+), 17 deletions(-) diff --git a/src/database.ts b/src/database.ts index 892532f..1a3c40c 100644 --- a/src/database.ts +++ b/src/database.ts @@ -111,3 +111,30 @@ export async function purgeRecordsBeforeId( return; } + +export async function deleteRecord( + db: D1Database, + id: number, + deleteFiles: (path: string[]) => Promise +) { + // 1. get record + const record = await db + .prepare(`SELECT * FROM upload_record WHERE id = ?`) + .bind(id) + .first(); + if (!record) return; + + // 2. delete files + if (!record.files) return; + try { + const files = JSON.parse(record.files) as { + name: string; + path: string; + size: number; + }[]; + await deleteFiles(files.map((file) => file.path)); + } catch {} + + // 3. delete record + await db.prepare(`DELETE FROM upload_record WHERE id = ?`).bind(id).run(); +} diff --git a/src/index.ts b/src/index.ts index cec4804..33a4cc0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ import { Hono } from "hono"; import { createUploadRecord, + deleteRecord, getUploadRecords, migrateTables, purgeRecordsBeforeId, @@ -92,6 +93,13 @@ app.get("/api/download", async (c) => { return new Response(r.body, { headers }); }); +app.post("/api/delete", async (c) => { + const body = await c.req.json(); + const id = +body.id; + await deleteRecord(c.env.DB, id, (paths) => c.env.MY_BUCKET.delete(paths)); + return c.json({ ok: true }); +}); + app.post("/api/purge", async (c) => { const beforeId = 9999999; await purgeRecordsBeforeId(c.env.DB, beforeId, (paths) => diff --git a/web/src/App.tsx b/web/src/App.tsx index 4ff89d8..75dd202 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,32 +1,53 @@ +import { useState } from 'react'; import './App.css'; import { ContentInput } from './components/ContentInput'; import { UploadRecords } from './components/UploadRecords'; const App = () => { + const [progress, setProgress] = useState(0); function startUpload(text: string, files: File[]) { const body = new FormData(); body.append('message', text); files.forEach((file) => body.append('files', file)); - fetch('/api/upload', { - method: 'POST', - headers: { - 'x-uploader': 'yon', - }, - body, - }) - .then((res) => res.json()) - .then((data) => { - console.log('data', data); - }); + const xhr = new XMLHttpRequest(); + xhr.open('POST', '/api/upload'); + xhr.setRequestHeader('x-uploader', 'yon'); + + xhr.upload.onprogress = (e) => { + if (e.lengthComputable) { + const percentComplete = (e.loaded / e.total) * 100; + setProgress(percentComplete); + } + }; + + xhr.onload = () => { + if (xhr.status === 200) { + const data = JSON.parse(xhr.responseText); + console.log('response', data); + setProgress(0); + window.location.reload(); + } + }; + + xhr.send(body); } return (
+
); }; +function SimpleProgressBar(props: { progress: number }) { + return ( +
+
+
+ ); +} + export default App; diff --git a/web/src/components/UploadRecords.tsx b/web/src/components/UploadRecords.tsx index 58d0e7d..4fbc001 100644 --- a/web/src/components/UploadRecords.tsx +++ b/web/src/components/UploadRecords.tsx @@ -33,9 +33,6 @@ export const UploadRecords = memo((props) => { }); const UploadRecordItem = memo((props: { record: UploadRecord }) => { - const [open, setOpen] = useState(false); - const toggleOpen = () => setOpen(!open); - const files = useMemo(() => { if (!props.record.files) return []; return JSON.parse(props.record.files) as { name: string; path: string; size: number }[]; @@ -58,12 +55,26 @@ const UploadRecordItem = memo((props: { record: UploadRecord }) => { {toReadableSize(props.record.size)} )} + +
+
+
+ + {!!props.record.message && ( + copyToClipboard(props.record.message)}> + + Copy Message + + )} + + deleteRecord(props.record.id)}> + + Delete +
{props.record.thumbnail} -
-        {props.record.message}
-      
+
{props.record.message}
{files.length > 0 && (
@@ -94,3 +105,32 @@ function toReadableSize(size: number) { } return `${size.toFixed(1)} ${units[unit]}`; } + +function copyToClipboard(text: string) { + try { + navigator.clipboard.writeText(text); + } catch (err) { + const textarea = document.createElement('textarea'); + textarea.value = text; + document.body.appendChild(textarea); + textarea.select(); + document.execCommand('copy'); + document.body.removeChild(textarea); + } +} + +function deleteRecord(id: number) { + if (!confirm('Are you sure you want to delete this record?')) return; + fetch('/api/delete', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ id }), + }) + .then((res) => res.json()) + .then((data) => { + console.log('deleted data', data); + window.document.dispatchEvent(new FocusEvent('focus')); + }); +}