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
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">
import { onMounted, onUnmounted } from 'vue'
import { onMounted, onUnmounted, ref } from 'vue'
import Navbar from '@/components/layout/Navbar.vue'
import Footer from '@/components/layout/Footer.vue'
import ConnectionStatus from '@/components/nostr/ConnectionStatus.vue'
import { useNostr } from '@/composables/useNostr'
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 () => {
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">
<nav class="container flex h-14 items-center justify-between">
<Navbar />
<ConnectionStatus :is-connected="isConnected" :error="error" />
<ConnectionStatus
:is-connected="isConnected"
:is-connecting="isConnecting"
:error="error"
/>
</nav>
</header>

View file

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

View file

@ -15,6 +15,8 @@ export const badgeVariants = cva(
'border-transparent bg-destructive/10 text-destructive hover:bg-destructive/20',
success:
'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',
},
},

View file

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