style: Improve RelayHubStatus and RelayHubDemo components for better dark mode support and UI consistency

- Update styles in RelayHubStatus.vue to enhance dark mode appearance, including adjustments to colors, borders, and font weights.
- Refactor RelayHubDemo.vue to improve layout and responsiveness, ensuring better visibility and usability in both light and dark themes.
- Add dark mode styles for various elements, including headers, buttons, and informational texts, to provide a cohesive user experience across the application.
This commit is contained in:
padreug 2025-08-10 18:48:55 +02:00
parent 48761e8035
commit 13f6f44a89
2 changed files with 202 additions and 91 deletions

View file

@ -121,29 +121,37 @@ const {
<style scoped> <style scoped>
.relay-hub-status { .relay-hub-status {
padding: 1rem; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
border: 1px solid #e2e8f0;
border-radius: 0.5rem;
background-color: #f8fafc;
max-width: 600px;
} }
.status-header { .status-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 1rem; margin-bottom: 1.5rem;
padding-bottom: 1rem;
border-bottom: 1px solid #e2e8f0;
} }
.status-header h3 { .status-header h3 {
margin: 0; margin: 0;
font-size: 1.125rem;
font-weight: 600;
color: #1e293b; color: #1e293b;
} }
.dark .status-header h3 {
color: #f1f5f9;
}
.dark .status-header {
border-bottom-color: #475569;
}
.connection-indicator { .connection-indicator {
padding: 0.25rem 0.75rem; padding: 0.25rem 0.75rem;
border-radius: 9999px; border-radius: 9999px;
font-size: 0.875rem; font-size: 0.75rem;
font-weight: 500; font-weight: 500;
text-transform: capitalize; text-transform: capitalize;
} }
@ -165,7 +173,7 @@ const {
.connection-indicator.error { .connection-indicator.error {
background-color: #fecaca; background-color: #fecaca;
color: #7f1d1d; color: #dc2626;
} }
.connection-info { .connection-info {
@ -175,8 +183,13 @@ const {
.info-row { .info-row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center;
padding: 0.5rem 0; padding: 0.5rem 0;
border-bottom: 1px solid #e2e8f0; border-bottom: 1px solid #f1f5f9;
}
.dark .info-row {
border-bottom-color: #334155;
} }
.info-row:last-child { .info-row:last-child {
@ -185,17 +198,30 @@ const {
.label { .label {
font-weight: 500; font-weight: 500;
color: #475569; color: #64748b;
}
.dark .label {
color: #94a3b8;
} }
.value { .value {
font-weight: 600;
color: #1e293b; color: #1e293b;
} }
.dark .value {
color: #f1f5f9;
}
.value.error { .value.error {
color: #dc2626; color: #dc2626;
} }
.dark .value.error {
color: #fca5a5;
}
.relay-list { .relay-list {
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
} }
@ -205,6 +231,10 @@ const {
color: #1e293b; color: #1e293b;
} }
.dark .relay-list h4 {
color: #f1f5f9;
}
.relay-item { .relay-item {
display: grid; display: grid;
grid-template-columns: 1fr auto auto auto; grid-template-columns: 1fr auto auto auto;
@ -217,6 +247,11 @@ const {
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
.dark .relay-item {
background-color: #1e293b;
border-color: #475569;
}
.relay-url { .relay-url {
font-family: monospace; font-family: monospace;
font-size: 0.875rem; font-size: 0.875rem;
@ -224,6 +259,10 @@ const {
word-break: break-all; word-break: break-all;
} }
.dark .relay-url {
color: #94a3b8;
}
.relay-status { .relay-status {
padding: 0.25rem 0.5rem; padding: 0.25rem 0.5rem;
border-radius: 0.25rem; border-radius: 0.25rem;
@ -245,12 +284,20 @@ const {
text-align: center; text-align: center;
} }
.dark .relay-latency {
color: #94a3b8;
}
.relay-error { .relay-error {
font-size: 0.75rem; font-size: 0.75rem;
color: #dc2626; color: #dc2626;
text-align: center; text-align: center;
} }
.dark .relay-error {
color: #fca5a5;
}
.actions { .actions {
display: flex; display: flex;
gap: 0.5rem; gap: 0.5rem;
@ -268,26 +315,50 @@ const {
transition: all 0.2s; transition: all 0.2s;
} }
.dark .actions button {
background-color: #374151;
color: #f9fafb;
border-color: #6b7280;
}
.actions button:hover:not(:disabled) { .actions button:hover:not(:disabled) {
background-color: #f9fafb; background-color: #f9fafb;
border-color: #9ca3af; border-color: #9ca3af;
} }
.dark .actions button:hover:not(:disabled) {
background-color: #4b5563;
border-color: #9ca3af;
}
.actions button:disabled { .actions button:disabled {
opacity: 0.5; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
} }
.dark .actions button:disabled {
background-color: #6b7280;
color: #9ca3af;
}
.subscription-info { .subscription-info {
border-top: 1px solid #e2e8f0; border-top: 1px solid #e2e8f0;
padding-top: 1rem; padding-top: 1rem;
} }
.dark .subscription-info {
border-top-color: #475569;
}
.subscription-info h4 { .subscription-info h4 {
margin: 0 0 0.5rem 0; margin: 0 0 0.5rem 0;
color: #1e293b; color: #1e293b;
} }
.dark .subscription-info h4 {
color: #f1f5f9;
}
.subscription-count { .subscription-count {
font-size: 1.5rem; font-size: 1.5rem;
font-weight: bold; font-weight: bold;
@ -295,6 +366,10 @@ const {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.dark .subscription-count {
color: #60a5fa;
}
.subscription-details { .subscription-details {
margin-top: 1rem; margin-top: 1rem;
padding: 1rem; padding: 1rem;
@ -302,6 +377,10 @@ const {
border-radius: 0.375rem; border-radius: 0.375rem;
} }
.dark .subscription-details {
background-color: #334155;
}
.subscription-details h5 { .subscription-details h5 {
margin: 0 0 0.75rem 0; margin: 0 0 0.75rem 0;
color: #475569; color: #475569;
@ -309,6 +388,10 @@ const {
font-weight: 600; font-weight: 600;
} }
.dark .subscription-details h5 {
color: #94a3b8;
}
.subscription-list { .subscription-list {
/* Individual subscription items handle their own spacing */ /* Individual subscription items handle their own spacing */
} }
@ -321,6 +404,11 @@ const {
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
} }
.dark .subscription-item {
background-color: #1e293b;
border-color: #475569;
}
.subscription-item:last-child { .subscription-item:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
@ -329,10 +417,20 @@ const {
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
font-family: monospace; font-family: monospace;
font-size: 0.875rem; font-size: 0.875rem;
color: #1e293b;
}
.dark .subscription-id {
color: #f1f5f9;
} }
.subscription-filters { .subscription-filters {
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
color: #64748b;
}
.dark .subscription-filters {
color: #94a3b8;
} }
.filter-details { .filter-details {
@ -352,15 +450,28 @@ const {
color: #475569; color: #475569;
} }
.dark .filter-item code {
background-color: #475569;
color: #e2e8f0;
}
.subscription-relays { .subscription-relays {
font-size: 0.875rem; font-size: 0.875rem;
color: #64748b; color: #64748b;
} }
.dark .subscription-relays {
color: #94a3b8;
}
.no-subscriptions { .no-subscriptions {
text-align: center; text-align: center;
color: #94a3b8; color: #94a3b8;
font-style: italic; font-style: italic;
padding: 1rem; padding: 1rem;
} }
.dark .no-subscriptions {
color: #64748b;
}
</style> </style>

View file

@ -1,33 +1,32 @@
<template> <template>
<div class="container mx-auto px-4 py-8"> <div class="min-h-screen bg-gray-50 dark:bg-gray-900 py-8">
<div class="max-w-4xl mx-auto"> <div class="max-w-6xl mx-auto px-4">
<div class="mb-8"> <div class="mb-8">
<h1 class="text-3xl font-bold text-gray-900 mb-4">Nostr Relay Hub Demo</h1> <h1 class="text-3xl font-bold text-gray-900 dark:text-white mb-2">Relay Hub Demo</h1>
<p class="text-gray-600"> <p class="text-gray-600 dark:text-gray-300">
This page demonstrates the centralized relay hub that manages all Nostr WebSocket connections. Test and monitor the centralized Nostr relay hub functionality
The hub ensures connections stay active, especially important for mobile devices.
</p> </p>
</div> </div>
<!-- Relay Hub Status --> <!-- Relay Hub Status -->
<div class="bg-white rounded-lg shadow p-6"> <div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6 mb-6">
<h2 class="text-xl font-semibold mb-4">Relay Hub Status</h2> <h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">Relay Hub Status</h2>
<RelayHubStatus /> <RelayHubStatus />
</div> </div>
<!-- Subscription Details --> <!-- Subscription Details -->
<div class="bg-white rounded-lg shadow p-6"> <div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6 mb-6">
<h2 class="text-xl font-semibold mb-4">Subscription Details</h2> <h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">Subscription Details</h2>
<div v-if="subscriptionDetails.length > 0" class="space-y-4"> <div v-if="subscriptionDetails.length > 0" class="space-y-4">
<div <div
v-for="sub in subscriptionDetails" v-for="sub in subscriptionDetails"
:key="sub.id" :key="sub.id"
class="p-4 border border-gray-200 rounded-lg" class="p-4 border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-700"
> >
<div class="font-mono text-sm font-semibold text-blue-600 mb-2"> <div class="font-mono text-sm font-semibold text-blue-600 dark:text-blue-400 mb-2">
{{ sub.id }} {{ sub.id }}
</div> </div>
<div class="text-sm text-gray-600"> <div class="text-sm text-gray-600 dark:text-gray-300">
<div><strong>Filters:</strong> {{ sub.filters.length }} filter(s)</div> <div><strong>Filters:</strong> {{ sub.filters.length }} filter(s)</div>
<div v-if="sub.relays && sub.relays.length > 0"> <div v-if="sub.relays && sub.relays.length > 0">
<strong>Relays:</strong> {{ sub.relays.join(', ') }} <strong>Relays:</strong> {{ sub.relays.join(', ') }}
@ -35,67 +34,65 @@
</div> </div>
</div> </div>
</div> </div>
<div v-else class="text-gray-500 text-center py-4"> <div v-else class="text-gray-500 dark:text-gray-400 text-center py-4">
No active subscriptions No active subscriptions
</div> </div>
</div> </div>
<!-- Connection Test --> <!-- Connection Test -->
<div class="bg-white rounded-lg shadow p-6 mb-8"> <div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6 mb-6">
<h2 class="text-xl font-semibold mb-4">Connection Test</h2> <h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">Connection Test</h2>
<div class="space-y-4"> <div class="space-y-4">
<div class="flex items-center space-x-4">
<button <button
@click="testConnection" @click="testConnection"
:disabled="isTesting" :disabled="isTesting || isConnected"
class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50" class="px-4 py-2 bg-blue-600 dark:bg-blue-500 text-white rounded-lg hover:bg-blue-700 dark:hover:bg-blue-600 disabled:bg-gray-400 dark:disabled:bg-gray-600 disabled:cursor-not-allowed transition-colors"
> >
{{ isTesting ? 'Testing...' : 'Test Connection' }} {{ isTesting ? 'Testing...' : 'Test Connection' }}
</button> </button>
<span v-if="testResult" class="text-sm" :class="testResult.success ? 'text-green-600' : 'text-red-600'">
{{ testResult.message }}
</span>
</div>
<div v-if="testResult && testResult.success" class="text-sm text-gray-600"> <div v-if="testResult" class="p-4 rounded-lg" :class="{
<p>Successfully connected to {{ testResult.connectedCount }} relays</p> 'bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-700 text-green-800 dark:text-green-200': testResult.success,
<p>Connection health: {{ testResult.health }}%</p> 'bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-700 text-red-800 dark:text-red-200': !testResult.success
}">
<p class="font-medium">{{ testResult.message }}</p>
<div v-if="testResult.success" class="mt-2 text-sm">
<p>Connected Relays: {{ testResult.connectedCount }}</p>
<p>Connection Health: {{ testResult.health?.toFixed(1) }}%</p>
</div>
</div> </div>
</div> </div>
</div> </div>
<!-- Subscription Test --> <!-- Subscription Test -->
<div class="bg-white rounded-lg shadow p-6 mb-8"> <div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6 mb-6">
<h2 class="text-xl font-semibold mb-4">Subscription Test</h2> <h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">Subscription Test</h2>
<div class="space-y-4"> <div class="space-y-4">
<div class="flex items-center space-x-4">
<button <button
@click="testSubscription" @click="testSubscription"
:disabled="!isConnected || isSubscribing" :disabled="isSubscribing"
class="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700 disabled:opacity-50" class="px-4 py-2 bg-blue-600 dark:bg-blue-500 text-white rounded-lg hover:bg-blue-700 dark:hover:bg-blue-600 disabled:bg-gray-400 dark:disabled:bg-gray-600 disabled:cursor-not-allowed transition-colors"
> >
{{ isSubscribing ? 'Subscribing...' : 'Test Subscription' }} {{ isSubscribing ? 'Subscribing...' : 'Test Subscription' }}
</button> </button>
<button <button
v-if="activeTestSubscription" v-if="activeTestSubscription"
@click="unsubscribeTest" @click="unsubscribeTest"
class="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700" class="px-4 py-2 bg-red-600 dark:bg-red-500 text-white rounded-lg hover:bg-red-700 dark:hover:bg-red-600 transition-colors"
> >
Unsubscribe Unsubscribe
</button> </button>
</div>
<div v-if="subscriptionEvents.length > 0" class="text-sm"> <div v-if="subscriptionEvents.length > 0" class="text-sm">
<p class="font-medium mb-2">Received Events ({{ subscriptionEvents.length }}):</p> <p class="font-medium text-gray-900 dark:text-white mb-2">Received Events ({{ subscriptionEvents.length }}):</p>
<div class="space-y-2 max-h-40 overflow-y-auto"> <div class="space-y-2 max-h-40 overflow-y-auto">
<div <div
v-for="event in subscriptionEvents.slice(-5)" v-for="event in subscriptionEvents.slice(-5)"
:key="event.id" :key="event.id"
class="p-2 bg-gray-50 rounded text-xs" class="p-2 bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded text-xs font-mono text-gray-700 dark:text-gray-300"
> >
<div class="font-mono text-gray-700">{{ event.id.substring(0, 8) }}...</div> {{ event.id }}
<div class="text-gray-600">Kind: {{ event.kind }}</div>
<div class="text-gray-600">Author: {{ event.pubkey.substring(0, 8) }}...</div>
</div> </div>
</div> </div>
</div> </div>
@ -103,47 +100,50 @@
</div> </div>
<!-- Mobile Visibility Test --> <!-- Mobile Visibility Test -->
<div class="bg-white rounded-lg shadow p-6 mb-8"> <div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6 mb-6">
<h2 class="text-xl font-semibold mb-4">Mobile Visibility Test</h2> <h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">Mobile Visibility Test</h2>
<p class="text-gray-600 mb-4"> <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
Test how the relay hub handles mobile app lifecycle events (visibility changes, network changes). <div class="p-4 border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-700">
<div class="flex items-center space-x-2 mb-2">
<div class="w-3 h-3 rounded-full" :class="pageVisible ? 'bg-green-500' : 'bg-red-500'"></div>
<span class="font-medium text-gray-900 dark:text-white">Page Visibility</span>
</div>
<p class="text-sm text-gray-600 dark:text-gray-300">
{{ pageVisible ? 'Page is visible' : 'Page is hidden (backgrounded)' }}
</p> </p>
<div class="space-y-2 text-sm">
<div class="flex items-center space-x-2">
<span class="w-4 h-4 rounded-full" :class="pageVisible ? 'bg-green-500' : 'bg-red-500'"></span>
<span>Page Visible: {{ pageVisible ? 'Yes' : 'No' }}</span>
</div> </div>
<div class="flex items-center space-x-2">
<span class="w-4 h-4 rounded-full" :class="networkOnline ? 'bg-green-500' : 'bg-red-500'"></span> <div class="p-4 border border-gray-200 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-700">
<span>Network Online: {{ networkOnline ? 'Yes' : 'No' }}</span> <div class="flex items-center space-x-2 mb-2">
<div class="w-3 h-3 rounded-full" :class="networkOnline ? 'bg-green-500' : 'bg-red-500'"></div>
<span class="font-medium text-gray-900 dark:text-white">Network Status</span>
</div> </div>
<p class="text-sm text-gray-600 dark:text-gray-300">
{{ networkOnline ? 'Network is online' : 'Network is offline' }}
</p>
</div> </div>
<div class="mt-4 text-xs text-gray-500">
<p> Try switching tabs or minimizing the browser window</p>
<p> Disconnect your network to test offline handling</p>
<p> The relay hub will maintain connections and reconnect automatically</p>
</div> </div>
</div> </div>
<!-- Configuration Info --> <!-- Configuration Info -->
<div class="bg-white rounded-lg shadow p-6"> <div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6">
<h2 class="text-xl font-semibold mb-4">Configuration</h2> <h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">Configuration</h2>
<div class="space-y-2 text-sm"> <div class="space-y-2 text-sm">
<div class="flex justify-between"> <div class="flex justify-between">
<span class="font-medium">Configured Relays:</span> <span class="font-medium text-gray-700 dark:text-gray-300">Configured Relays:</span>
<span>{{ config.nostr.relays.length }}</span> <span class="text-gray-900 dark:text-white">{{ config.nostr.relays.length }}</span>
</div> </div>
<div class="flex justify-between"> <div class="flex justify-between">
<span class="font-medium">Market Relays:</span> <span class="font-medium text-gray-700 dark:text-gray-300">Market Relays:</span>
<span>{{ config.market.supportedRelays.length }}</span> <span class="text-gray-900 dark:text-white">{{ config.market.supportedRelays.length }}</span>
</div> </div>
<div class="mt-4"> <div class="mt-4">
<p class="font-medium mb-2">Relay URLs:</p> <p class="font-medium text-gray-700 dark:text-gray-300 mb-2">Relay URLs:</p>
<div class="space-y-1"> <div class="space-y-1">
<div <div
v-for="relay in config.nostr.relays" v-for="relay in config.nostr.relays"
:key="relay" :key="relay"
class="font-mono text-xs bg-gray-50 p-2 rounded" class="font-mono text-xs bg-gray-50 dark:bg-gray-700 p-2 rounded border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-300"
> >
{{ relay }} {{ relay }}
</div> </div>