personal website
1/// <reference path="../../guestbook.d.ts" />
2import { useEffect, useRef } from "react"
3import { configureGuestbook } from "cutebook/register"
4import { GuestbookEntries } from "../GuestbookEntries"
5
6// Configure guestbook once
7let configured = false
8if (!configured) {
9 const isDev = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'
10 const port = window.location.port || '3000'
11
12 // For dev, use loopback client format matching the demo
13 // Client ID uses http://localhost, redirect_uri uses 127.0.0.1
14 const scope = 'atproto transition:generic'
15 const redirectUri = isDev ? `http://127.0.0.1:${port}/guestbook` : 'https://nekomimi.pet/guestbook'
16 const clientId = isDev
17 ? `http://localhost?redirect_uri=${encodeURIComponent(redirectUri)}&scope=${encodeURIComponent(scope)}`
18 : 'https://nekomimi.pet/client-metadata.json'
19
20 configureGuestbook({
21 oauth: {
22 clientId,
23 redirectUri,
24 scope,
25 },
26 })
27 configured = true
28}
29
30export function GuestbookPage() {
31 const refreshRef = useRef<(() => void) | null>(null)
32
33 const handleSignCreated = () => {
34 refreshRef.current?.()
35 }
36
37 useEffect(() => {
38 const signElement = document.querySelector('guestbook-sign')
39 if (signElement) {
40 signElement.addEventListener('sign-created', handleSignCreated)
41 return () => signElement.removeEventListener('sign-created', handleSignCreated)
42 }
43 }, [])
44
45 return (
46 <div className="min-h-screen bg-gradient-to-b from-gray-50 to-gray-100 py-12 px-6">
47 <div className="max-w-xl mx-auto">
48 {/* Header */}
49 <header className="mb-12 text-center">
50 <div className="inline-block mb-4">
51 <span className="text-5xl">📖</span>
52 </div>
53 <h1 className="text-3xl font-light tracking-tight text-gray-900 mb-3">
54 Ana's Guestbook
55 </h1>
56 <p className="text-gray-500 font-mono text-sm">
57 Leave a message, say hello
58 </p>
59 </header>
60
61 {/* Sign Form */}
62 <div className="mb-12 bg-white rounded-2xl shadow-sm border border-gray-200/50 p-6">
63 <guestbook-sign did="did:plc:ttdrpj45ibqunmfhdsb4zdwq"></guestbook-sign>
64 </div>
65
66 {/* Entries Header */}
67 <div className="flex items-center gap-3 mb-6">
68 <div className="h-px flex-1 bg-gradient-to-r from-transparent via-gray-300 to-transparent"></div>
69 <span className="text-xs font-mono text-gray-400 uppercase tracking-widest">
70 Messages
71 </span>
72 <div className="h-px flex-1 bg-gradient-to-r from-transparent via-gray-300 to-transparent"></div>
73 </div>
74
75 <GuestbookEntries
76 did="did:plc:ttdrpj45ibqunmfhdsb4zdwq"
77 limit={50}
78 onRefresh={(refresh) => { refreshRef.current = refresh }}
79 />
80 </div>
81 </div>
82 )
83}
84