diff --git a/app/globals.css b/app/globals.css index ec33d56..7bcf05f 100644 --- a/app/globals.css +++ b/app/globals.css @@ -37,7 +37,7 @@ body { --sidebar-foreground: 240 5.3% 26.1%; --sidebar-primary: 240 5.9% 10%; --sidebar-primary-foreground: 0 0% 98%; - --sidebar-accent: 240 4.8% 95.9%; + --sidebar-accent: 240 4.8% 88.9%; --sidebar-accent-foreground: 240 5.9% 10%; --sidebar-border: 220 13% 91%; --sidebar-ring: 217.2 91.2% 59.8%; diff --git a/app/hook/[id]/page.tsx b/app/hook/[id]/page.tsx index 00438e2..31a0b0a 100644 --- a/app/hook/[id]/page.tsx +++ b/app/hook/[id]/page.tsx @@ -2,6 +2,7 @@ import { AppSidebar } from "@/components/app-sidebar"; import CopyButton from "@/components/copy-button"; +import RequestMethodBadge from "@/components/request-method-badge"; import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"; import { Table, @@ -74,9 +75,7 @@ export default function Page({ params }: { params: Promise<{ id: string }> }) { - - {selected.method} - + {selected.path} diff --git a/app/page.tsx b/app/page.tsx index ce447c5..cb4781c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -5,8 +5,19 @@ import { useRouter } from "next/navigation"; export default function Home() { const router = useRouter(); + const getOrCreateId = () => { + const storedId = localStorage.getItem("id"); + if (storedId) { + return storedId; + } + const newId = Math.random().toString(36).substring(2, 10); + localStorage.setItem("id", newId); + return newId; + }; + useEffect(() => { - router.push(`/hook/${Math.random().toString(36).substring(2, 10)}`); + const id = getOrCreateId(); + router.push(`/hook/${id}`); }, [router]); return null; diff --git a/components/app-sidebar.tsx b/components/app-sidebar.tsx index 8b34289..bfb6da1 100644 --- a/components/app-sidebar.tsx +++ b/components/app-sidebar.tsx @@ -3,126 +3,154 @@ import { useEffect, useState } from "react"; import { - Sidebar, - SidebarContent, - SidebarGroup, - SidebarGroupContent, - SidebarHeader, + Sidebar, + SidebarContent, + SidebarGroup, + SidebarGroupContent, + SidebarHeader, } from "@/components/ui/sidebar"; import { formatTimestamp } from "@/lib/datetime"; import { LoaderCircle } from "lucide-react"; -import { FaGithub } from "react-icons/fa"; +import { FaGithub, FaTrash } from "react-icons/fa"; import Link from "next/link"; +import RequestMethodBadge from "./request-method-badge"; export function AppSidebar({ - hookId, - onSelected, - selected, - ...props + hookId, + onSelected, + selected, + ...props }: React.ComponentProps & { - hookId: string; - selected?: WebRequest; - onSelected: (request: WebRequest) => void; + hookId: string; + selected?: WebRequest; + onSelected: (request: WebRequest) => void; }) { - const [requests, setRequests] = useState([]); - const [totalRequests, setTotalRequests] = useState(0); + const [requests, setRequests] = useState([]); + const [totalRequests, setTotalRequests] = useState(0); - useEffect(() => { - fetch(`/api/hook/${hookId}`, { - headers: { - "Content-Type": "text/event-stream", - }, - }).then((res) => { - const reader = res.body?.getReader(); - if (!reader) return; - reader.read().then(function processText({ done, value }) { - if (done) { - return; - } - const text = new TextDecoder().decode(value); - const lines = text.split("\n").filter((line) => line.trim()); - lines.forEach((line) => { - const req: WebRequest = JSON.parse(line); - setTotalRequests(req.serial); - setRequests((prev) => { - const newRequests = [req, ...prev]; - return newRequests.slice(0, 100); - }); - }); - reader.read().then(processText); - }); - }); - }, [hookId]); + useEffect(() => { + const storedRequests = localStorage.getItem(`requests_${hookId}`); + const parsedRequests = JSON.parse(storedRequests || "[]") as WebRequest[]; + if (!!parsedRequests?.length) { + setRequests(parsedRequests); + setTotalRequests(parsedRequests.length); + } - useEffect(() => { - if (!selected && requests.length > 0) { - onSelected(requests[0]); - } - }, [selected, requests]); + fetch(`/api/hook/${hookId}`, { + headers: { + "Content-Type": "text/event-stream", + }, + }).then((res) => { + const reader = res.body?.getReader(); + if (!reader) return; + reader.read().then(function processText({ done, value }) { + if (done) { + return; + } + const text = new TextDecoder().decode(value); + const lines = text.split("\n").filter((line) => line.trim()); + lines.forEach((line) => { + const req: WebRequest = JSON.parse(line); + setTotalRequests((prev) => { + return prev + 1; + }); + setRequests((prev) => { + const newRequests = [req, ...prev]; + return newRequests.slice(0, 100); + }); + }); + reader.read().then(processText); + }); + }); + }, [hookId]); - return ( - - - -
- -
- - W - - irehook -
- - - - -
- {/* */} - - REQUESTS ({totalRequests}) - -
- - - - {requests.length === 0 && ( -
- Waiting for - your first request -
- )} - {requests.map((req) => ( -
onSelected(req)} - className={`flex hover:cursor-pointer flex-col items-start gap-2 whitespace-nowrap border-b p-4 text-sm leading-tight last:border-b-0 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground ${ - selected?.id === req.id - ? "bg-sidebar-accent text-sidebar-accent-foreground" - : "" - }`} - > -
- - {req.method} - - - {req.path.substring(0, 30)} - {req.path.length > 30 ? "..." : ""} - -
- - {formatTimestamp(req.timestamp)} - -
- ))} -
-
-
-
-
- ); + useEffect(() => { + localStorage.setItem(`requests_${hookId}`, JSON.stringify(requests)); + }, [hookId, requests]); + + useEffect(() => { + if (!selected && requests.length > 0) { + onSelected(requests[0]); + } + }, [selected, requests]); + + return ( + + + +
+ +
+ + W + + irehook +
+ + + + +
+ {/* */} + + REQUESTS ({totalRequests}) + +
+ + + + {requests.length === 0 && ( +
+ Waiting for + your first request +
+ )} + {requests.map((req) => ( +
onSelected(req)} + className={`group/request flex hover:cursor-pointer flex-col items-start gap-2 whitespace-nowrap border-b p-4 text-sm leading-tight last:border-b-0 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground ${ + selected?.id === req.id + ? "bg-sidebar-accent text-sidebar-accent-foreground" + : "" + }`} + > +
+ + + {req.path.substring(0, 30)} + {req.path.length > 30 ? "..." : ""} + +
+
+ + {formatTimestamp(req.timestamp)} + + + + +
+
+ ))} +
+
+
+
+
+ ); } diff --git a/components/request-method-badge.tsx b/components/request-method-badge.tsx new file mode 100644 index 0000000..8280ade --- /dev/null +++ b/components/request-method-badge.tsx @@ -0,0 +1,31 @@ +"use client"; + +export default function RequestMethodBadge({ method }: { method: string }) { + return ( + + {method} + + ); +} diff --git a/package-lock.json b/package-lock.json index 5112e3e..18feeaf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,7 +37,7 @@ "eslint": "^9", "eslint-config-next": "15.1.2", "postcss": "^8", - "tailwindcss": "^3.4.1", + "tailwindcss": "^3.4.17", "typescript": "^5" } }, diff --git a/package.json b/package.json index c38ca81..95aaaed 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "eslint": "^9", "eslint-config-next": "15.1.2", "postcss": "^8", - "tailwindcss": "^3.4.1", + "tailwindcss": "^3.4.17", "typescript": "^5" } }