feat(nostr): Add connecting state to Nostr connection management

- Introduce `isConnecting` state in useNostr composable
- Update ConnectionStatus component to handle connecting state
- Add warning variant to Badge for connecting status
- Implement dynamic status text, color, and animation for connection states
- Modify App.vue to pass new isConnecting prop to ConnectionStatus
This commit is contained in:
padreug 2025-03-09 15:21:31 +01:00
parent 0923731ee9
commit 18ece1e3e7
5 changed files with 52 additions and 12 deletions

3
.env
View file

@ -1,5 +1,4 @@
# Support agent's public key in npub format # Support agent's public key in npub format
VITE_SUPPORT_NPUB=npub1tm42jkmdn54zncjcylp34e85jagmgndr0skw4v0rsg8rucmu7r5swayth3 VITE_SUPPORT_NPUB=npub1tm42jkmdn54zncjcylp34e85jagmgndr0skw4v0rsg8rucmu7r5swayth3
VITE_NOSTR_RELAYS=["wss://relay.damus.io","wss://relay.nostr.info"] VITE_NOSTR_RELAYS=["wss://nostr.atitlan.io"]

View file

@ -1,12 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, onUnmounted } from 'vue' import { onMounted, onUnmounted, ref } from 'vue'
import Navbar from '@/components/layout/Navbar.vue' import Navbar from '@/components/layout/Navbar.vue'
import Footer from '@/components/layout/Footer.vue' import Footer from '@/components/layout/Footer.vue'
import ConnectionStatus from '@/components/nostr/ConnectionStatus.vue' import ConnectionStatus from '@/components/nostr/ConnectionStatus.vue'
import { useNostr } from '@/composables/useNostr' import { useNostr } from '@/composables/useNostr'
const relays = JSON.parse(import.meta.env.VITE_NOSTR_RELAYS as string) const relays = JSON.parse(import.meta.env.VITE_NOSTR_RELAYS as string)
const { isConnected, error, connect, disconnect } = useNostr({ relays }) const { isConnected, isConnecting, error, connect, disconnect } = useNostr({ relays })
onMounted(async () => { onMounted(async () => {
await connect() await connect()
@ -25,7 +25,11 @@ onUnmounted(() => {
class="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60"> class="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<nav class="container flex h-14 items-center justify-between"> <nav class="container flex h-14 items-center justify-between">
<Navbar /> <Navbar />
<ConnectionStatus :is-connected="isConnected" :error="error" /> <ConnectionStatus
:is-connected="isConnected"
:is-connecting="isConnecting"
:error="error"
/>
</nav> </nav>
</header> </header>

View file

@ -2,22 +2,51 @@
<script setup lang="ts"> <script setup lang="ts">
import { Badge } from '@/components/ui/badge' import { Badge } from '@/components/ui/badge'
defineProps<{ const props = defineProps<{
isConnected: boolean isConnected: boolean
isConnecting: boolean
error?: Error | null error?: Error | null
}>() }>()
function getStatusVariant() {
if (props.isConnecting) return 'warning'
return props.isConnected ? 'success' : 'destructive'
}
function getStatusText() {
if (props.isConnecting) return 'Connecting'
return props.isConnected ? 'Online' : 'Offline'
}
function getStatusColor() {
if (props.isConnecting) return 'bg-yellow-400'
return props.isConnected ? 'bg-green-400' : 'bg-red-400'
}
function getStatusColorSolid() {
if (props.isConnecting) return 'bg-yellow-500'
return props.isConnected ? 'bg-green-500' : 'bg-red-500'
}
</script> </script>
<template> <template>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Badge :variant="isConnected ? 'success' : 'destructive'" class="flex items-center gap-1"> <Badge :variant="getStatusVariant()" class="flex items-center gap-1">
<span class="relative flex h-2 w-2"> <span class="relative flex h-2 w-2">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full opacity-75" <span
:class="isConnected ? 'bg-green-400' : 'bg-red-400'" /> class="absolute inline-flex h-full w-full rounded-full opacity-75"
<span class="relative inline-flex h-2 w-2 rounded-full" :class="[
:class="isConnected ? 'bg-green-500' : 'bg-red-500'" /> getStatusColor(),
{ 'animate-ping': !props.isConnecting },
{ 'animate-pulse': props.isConnecting }
]"
/>
<span
class="relative inline-flex h-2 w-2 rounded-full"
:class="getStatusColorSolid()"
/>
</span> </span>
<span class="text-[10px]">{{ isConnected ? 'Online' : 'Offline' }}</span> <span class="text-[10px]">{{ getStatusText() }}</span>
</Badge> </Badge>
<p v-if="error" class="text-xs text-destructive"> <p v-if="error" class="text-xs text-destructive">
{{ error.message }} {{ error.message }}

View file

@ -15,6 +15,8 @@ export const badgeVariants = cva(
'border-transparent bg-destructive/10 text-destructive hover:bg-destructive/20', 'border-transparent bg-destructive/10 text-destructive hover:bg-destructive/20',
success: success:
'border-transparent bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400', 'border-transparent bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400',
warning:
'border-transparent bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400',
outline: 'border-border text-foreground hover:bg-accent hover:text-accent-foreground', outline: 'border-border text-foreground hover:bg-accent hover:text-accent-foreground',
}, },
}, },

View file

@ -4,27 +4,33 @@ import { NostrClient, type NostrClientConfig } from '@/lib/nostr/client'
export function useNostr(config: NostrClientConfig) { export function useNostr(config: NostrClientConfig) {
const client = new NostrClient(config) const client = new NostrClient(config)
const isConnected = ref(false) const isConnected = ref(false)
const isConnecting = ref(false)
const error = ref<Error | null>(null) const error = ref<Error | null>(null)
async function connect() { async function connect() {
try { try {
error.value = null error.value = null
isConnecting.value = true
await client.connect() await client.connect()
isConnected.value = client.isConnected isConnected.value = client.isConnected
} catch (err) { } catch (err) {
error.value = err instanceof Error ? err : new Error('Failed to connect') error.value = err instanceof Error ? err : new Error('Failed to connect')
isConnected.value = false isConnected.value = false
} finally {
isConnecting.value = false
} }
} }
function disconnect() { function disconnect() {
client.disconnect() client.disconnect()
isConnected.value = false isConnected.value = false
isConnecting.value = false
error.value = null error.value = null
} }
return { return {
isConnected, isConnected,
isConnecting,
error, error,
connect, connect,
disconnect disconnect