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:
co-authored by
Claude Opus 4.6
parent
e49735002c
commit
4a35e1e0b5
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user