feat: profile page character display, persistent auth, cleanup

- Large animated sprite on BotProfilePage with tier-aware rendering
- Dynamic status titles (UNSTOPPABLE, ON FIRE, FRESH MEAT, etc.)
- Nostr auth persisted to localStorage — survives navigation and HMR
- Remove grotesque close-up overlays (eyeballs, tongues, teeth, drool)
- Remove crowd cheering signs (too small to look good)
- Add archetype to bot stats API response
- SpritePreview now accepts tier prop

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-07 08:21:20 +00:00
co-authored by Claude Opus 4.6
parent e49735002c
commit 4a35e1e0b5
5 changed files with 81 additions and 307 deletions
+36 -4
View File
@@ -26,15 +26,41 @@ declare global {
}
}
const pubkey = ref<string | null>(null)
const bot = ref<BotData | null>(null)
const profilePicUrl = ref<string | null>(null)
// Persist auth state across page navigations and HMR reloads
function loadStored<T>(key: string): T | null {
try {
const raw = localStorage.getItem(key)
return raw ? JSON.parse(raw) : null
} catch { return null }
}
function store(key: string, value: unknown) {
if (value == null) localStorage.removeItem(key)
else localStorage.setItem(key, JSON.stringify(value))
}
const pubkey = ref<string | null>(loadStored('bf_pubkey'))
const bot = ref<BotData | null>(loadStored('bf_bot'))
const profilePicUrl = ref<string | null>(loadStored('bf_pic'))
const isLoading = ref(false)
export function useNostr() {
const isLoggedIn = computed(() => !!pubkey.value && !!bot.value)
const hasExtension = computed(() => !!window.nostr)
// Restore session on first load — re-verify with server
if (pubkey.value && !bot.value) {
fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ pubkey: pubkey.value }),
}).then(r => r.json()).then(data => {
if (data.exists) {
bot.value = data.bot
store('bf_bot', data.bot)
}
}).catch(() => {})
}
async function login(): Promise<{ pubkey: string; bot: BotData | null }> {
if (!window.nostr) {
throw new Error('No Nostr extension found. Install nos2x, Alby, or another NIP-07 extension.')
@@ -44,10 +70,11 @@ export function useNostr() {
try {
const pk = await window.nostr.getPublicKey()
pubkey.value = pk
store('bf_pubkey', pk)
// Fetch Nostr profile pic from relay
const pic = await fetchNostrProfilePic(pk)
if (pic) profilePicUrl.value = pic
if (pic) { profilePicUrl.value = pic; store('bf_pic', pic) }
// Check if this pubkey has a bot
const res = await fetch('/api/auth/login', {
@@ -60,6 +87,7 @@ export function useNostr() {
const data = await res.json()
if (data.exists) {
bot.value = data.bot
store('bf_bot', data.bot)
return { pubkey: pk, bot: data.bot }
}
}
@@ -101,6 +129,7 @@ export function useNostr() {
bestStreak: 0,
tier: 0,
}
store('bf_bot', bot.value)
return bot.value
}
@@ -109,6 +138,9 @@ export function useNostr() {
pubkey.value = null
bot.value = null
profilePicUrl.value = null
store('bf_pubkey', null)
store('bf_bot', null)
store('bf_pic', null)
}
return {