From 7e4b64b831eca9698732ffc3b500411f2e608c2c Mon Sep 17 00:00:00 2001 From: padreug Date: Sat, 6 Sep 2025 10:55:10 +0200 Subject: [PATCH] Remove LEGACY Nostr client services and related components to streamline the codebase - Delete Nostr client hub and associated files, including the Nostr store, to eliminate unused functionality. - Update service tokens and dependency injections to reflect the removal of Nostr client services. - Adjust notification settings in NotificationSettings.vue to prepare for future implementation of notifications. --- CLAUDE.md | 1 - .../notifications/NotificationSettings.vue | 14 +- src/core/base/BaseService.ts | 6 - src/core/di-container.ts | 1 - src/modules/base/index.ts | 8 - src/modules/base/nostr/index.ts | 8 +- src/modules/base/nostr/nostrclient-hub.ts | 442 ------------------ src/stores/nostr.ts | 188 -------- 8 files changed, 7 insertions(+), 661 deletions(-) delete mode 100644 src/modules/base/nostr/nostrclient-hub.ts delete mode 100644 src/stores/nostr.ts diff --git a/CLAUDE.md b/CLAUDE.md index 6c6c348..0902790 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -78,7 +78,6 @@ const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB) **Available Services:** - `SERVICE_TOKENS.RELAY_HUB` - Centralized Nostr relay management -- `SERVICE_TOKENS.NOSTR_CLIENT_HUB` - Nostr client services - `SERVICE_TOKENS.AUTH_SERVICE` - Authentication services - `SERVICE_TOKENS.VISIBILITY_SERVICE` - App visibility and connection management diff --git a/src/components/notifications/NotificationSettings.vue b/src/components/notifications/NotificationSettings.vue index 1a70cfb..e72d360 100644 --- a/src/components/notifications/NotificationSettings.vue +++ b/src/components/notifications/NotificationSettings.vue @@ -151,15 +151,13 @@ import { Button } from '@/components/ui/button' import { Label } from '@/components/ui/label' import { Badge } from '@/components/ui/badge' import { Bell, BellOff, TestTube, AlertCircle } from 'lucide-vue-next' -import { useNostrStore } from '@/stores/nostr' import { notificationManager } from '@/lib/notifications/manager' import { pushService } from '@/lib/notifications/push' import { configUtils } from '@/lib/config' import { toast } from 'vue-sonner' -import { storeToRefs } from 'pinia' -const nostrStore = useNostrStore() -const { notificationsEnabled } = storeToRefs(nostrStore) +// Placeholder for notifications - will be implemented later +const notificationsEnabled = ref(false) const isLoading = ref(false) const isTestLoading = ref(false) @@ -174,14 +172,14 @@ onMounted(async () => { async function checkStatus() { isBlocked.value = pushService.getPermission() === 'denied' - await nostrStore.checkPushNotificationStatus() + // TODO: Implement push notification status checking when notifications are re-added } async function enableNotifications() { try { isLoading.value = true - await nostrStore.enablePushNotifications() - toast.success('Push notifications enabled!') + // TODO: Implement push notification enabling when notifications are re-added + toast.success('Push notifications will be implemented later') await checkStatus() } catch (error) { const message = error instanceof Error ? error.message : 'Failed to enable notifications' @@ -193,7 +191,7 @@ async function enableNotifications() { async function disableNotifications() { try { - await nostrStore.disablePushNotifications() + // TODO: Implement push notification disabling when notifications are re-added toast.success('Push notifications disabled') await checkStatus() } catch (error) { diff --git a/src/core/base/BaseService.ts b/src/core/base/BaseService.ts index 46cfde8..82016b8 100644 --- a/src/core/base/BaseService.ts +++ b/src/core/base/BaseService.ts @@ -44,7 +44,6 @@ export abstract class BaseService { // Core dependencies with proper typing protected relayHub: any = null protected authService: any = null - protected nostrClientHub: any = null protected visibilityService: any = null // Service state @@ -134,7 +133,6 @@ export abstract class BaseService { // Try to inject core dependencies this.relayHub = tryInjectService(SERVICE_TOKENS.RELAY_HUB) this.authService = tryInjectService(SERVICE_TOKENS.AUTH_SERVICE) - this.nostrClientHub = tryInjectService(SERVICE_TOKENS.NOSTR_CLIENT_HUB) this.visibilityService = tryInjectService(SERVICE_TOKENS.VISIBILITY_SERVICE) // Check if all required dependencies are available @@ -180,9 +178,6 @@ export abstract class BaseService { if (deps.includes('AuthService') && !this.authService) { missing.push('AuthService') } - if (deps.includes('NostrClientHub') && !this.nostrClientHub) { - missing.push('NostrClientHub') - } if (deps.includes('VisibilityService') && !this.visibilityService) { missing.push('VisibilityService') } @@ -269,7 +264,6 @@ export abstract class BaseService { this.isInitialized.value = false this.relayHub = null this.authService = null - this.nostrClientHub = null console.log(`♻️ ${this.metadata.name} disposed`) diff --git a/src/core/di-container.ts b/src/core/di-container.ts index 4fb8677..d3be71c 100644 --- a/src/core/di-container.ts +++ b/src/core/di-container.ts @@ -109,7 +109,6 @@ export const SERVICE_TOKENS = { // Nostr services RELAY_HUB: Symbol('relayHub'), - NOSTR_CLIENT_HUB: Symbol('nostrClientHub'), // Auth services AUTH_SERVICE: Symbol('authService'), diff --git a/src/modules/base/index.ts b/src/modules/base/index.ts index 56f15b1..ddcfc02 100644 --- a/src/modules/base/index.ts +++ b/src/modules/base/index.ts @@ -2,7 +2,6 @@ import type { App } from 'vue' import type { ModulePlugin } from '@/core/types' import { container, SERVICE_TOKENS } from '@/core/di-container' import { relayHub } from './nostr/relay-hub' -import { nostrclientHub } from './nostr/nostrclient-hub' // Import auth services import { auth } from './auth/auth-service' @@ -27,7 +26,6 @@ export const baseModule: ModulePlugin = { // Register core Nostr services container.provide(SERVICE_TOKENS.RELAY_HUB, relayHub) - container.provide(SERVICE_TOKENS.NOSTR_CLIENT_HUB, nostrclientHub) // Register auth service container.provide(SERVICE_TOKENS.AUTH_SERVICE, auth) @@ -56,10 +54,6 @@ export const baseModule: ModulePlugin = { waitForDependencies: false, // VisibilityService has no dependencies maxRetries: 1 }) - await nostrclientHub.initialize({ - waitForDependencies: true, // NostrClientHub depends on VisibilityService - maxRetries: 3 - }) console.log('✅ Base module installed successfully') }, @@ -69,7 +63,6 @@ export const baseModule: ModulePlugin = { // Cleanup services await relayHub.dispose() - await nostrclientHub.dispose() await auth.dispose() await paymentService.dispose() await visibilityService.dispose() @@ -79,7 +72,6 @@ export const baseModule: ModulePlugin = { services: { relayHub, - nostrclientHub, auth, paymentService, pwaService diff --git a/src/modules/base/nostr/index.ts b/src/modules/base/nostr/index.ts index 2cd4bdf..a62a132 100644 --- a/src/modules/base/nostr/index.ts +++ b/src/modules/base/nostr/index.ts @@ -1,16 +1,10 @@ // Re-export Nostr infrastructure from base module export { RelayHub } from './relay-hub' -export { NostrclientHub } from './nostrclient-hub' export { relayHub } from './relay-hub' -export { nostrclientHub } from './nostrclient-hub' // Re-export types export type { RelayConfig, SubscriptionConfig, RelayStatus -} from './relay-hub' - -export type { - NostrclientConfig -} from './nostrclient-hub' \ No newline at end of file +} from './relay-hub' \ No newline at end of file diff --git a/src/modules/base/nostr/nostrclient-hub.ts b/src/modules/base/nostr/nostrclient-hub.ts deleted file mode 100644 index 80e7f37..0000000 --- a/src/modules/base/nostr/nostrclient-hub.ts +++ /dev/null @@ -1,442 +0,0 @@ -import type { Filter, Event } from 'nostr-tools' -import { BaseService } from '@/core/base/BaseService' - - -export interface NostrclientConfig { - url: string - privateKey?: string // For private WebSocket endpoint -} - -export interface SubscriptionConfig { - id: string - filters: Filter[] - onEvent?: (event: Event) => void - onEose?: () => void - onClose?: () => void -} - -export interface RelayStatus { - url: string - connected: boolean - lastSeen: number - error?: string -} - -export class NostrclientHub extends BaseService { - // Service metadata - protected readonly metadata = { - name: 'NostrclientHub', - version: '1.0.0', - dependencies: ['VisibilityService'] - } - - // EventEmitter functionality - private events: { [key: string]: Function[] } = {} - - // Service state - private ws: WebSocket | null = null - private config: NostrclientConfig - private subscriptions: Map = new Map() - private reconnectInterval?: number - private reconnectAttempts = 0 - private readonly maxReconnectAttempts = 5 - private readonly reconnectDelay = 5000 - private visibilityUnsubscribe?: () => void - - // Connection state - private _isConnected = false - private _isConnecting = false - - constructor(config: NostrclientConfig) { - super() - this.config = config - } - - // EventEmitter methods - on(event: string, listener: Function) { - if (!this.events[event]) { - this.events[event] = [] - } - this.events[event].push(listener) - } - - emit(event: string, ...args: any[]) { - if (this.events[event]) { - this.events[event].forEach(listener => listener(...args)) - } - } - - removeAllListeners(event?: string) { - if (event) { - delete this.events[event] - } else { - this.events = {} - } - } - - /** - * Service-specific initialization (called by BaseService) - */ - protected async onInitialize(): Promise { - // Connect to WebSocket - console.log('🔧 NostrclientHub: Initializing connection to', this.config.url) - await this.connect() - - // Register with visibility service - this.registerWithVisibilityService() - - this.debug('NostrclientHub initialized') - } - - get isConnected(): boolean { - return this._isConnected - } - - get isConnecting(): boolean { - return this._isConnecting - } - - get totalSubscriptionCount(): number { - return this.subscriptions.size - } - - get subscriptionDetails(): Array<{ id: string; filters: Filter[] }> { - return Array.from(this.subscriptions.values()).map(sub => ({ - id: sub.id, - filters: sub.filters - })) - } - - - /** - * Connect to the nostrclient WebSocket - */ - async connect(): Promise { - if (this._isConnecting || this._isConnected) { - return - } - - this._isConnecting = true - this.reconnectAttempts++ - - try { - console.log('🔧 NostrclientHub: Connecting to nostrclient WebSocket') - - // Determine WebSocket endpoint - const wsUrl = this.config.privateKey - ? `${this.config.url}/${this.config.privateKey}` // Private endpoint - : `${this.config.url}/relay` // Public endpoint - - this.ws = new WebSocket(wsUrl) - - this.ws.onopen = () => { - console.log('🔧 NostrclientHub: WebSocket connected') - this._isConnected = true - this._isConnecting = false - this.reconnectAttempts = 0 - this.emit('connected') - - // Resubscribe to existing subscriptions - this.resubscribeAll() - } - - this.ws.onmessage = (event) => { - this.handleMessage(event.data) - } - - this.ws.onclose = (event) => { - console.log('🔧 NostrclientHub: WebSocket closed:', event.code, event.reason) - this._isConnected = false - this._isConnecting = false - this.emit('disconnected', event) - - // Schedule reconnection - if (this.reconnectAttempts < this.maxReconnectAttempts) { - this.scheduleReconnect() - } else { - this.emit('maxReconnectionAttemptsReached') - } - } - - this.ws.onerror = (error) => { - console.error('🔧 NostrclientHub: WebSocket error:', error) - this.emit('error', error) - } - - } catch (error) { - this._isConnecting = false - console.error('🔧 NostrclientHub: Connection failed:', error) - this.emit('connectionError', error) - - if (this.reconnectAttempts < this.maxReconnectAttempts) { - this.scheduleReconnect() - } - } - } - - /** - * Disconnect from the WebSocket - */ - disconnect(): void { - if (this.reconnectInterval) { - clearTimeout(this.reconnectInterval) - this.reconnectInterval = undefined - } - - if (this.ws) { - this.ws.close() - this.ws = null - } - - this._isConnected = false - this._isConnecting = false - this.subscriptions.clear() - this.emit('disconnected') - } - - /** - * Subscribe to events - */ - subscribe(config: SubscriptionConfig): () => void { - if (!this._isConnected) { - throw new Error('Not connected to nostrclient') - } - - // Store subscription - this.subscriptions.set(config.id, config) - - // Send REQ message - const reqMessage = JSON.stringify([ - 'REQ', - config.id, - ...config.filters - ]) - - this.ws?.send(reqMessage) - console.log('🔧 NostrclientHub: Subscribed to', config.id) - - // Return unsubscribe function - return () => { - this.unsubscribe(config.id) - } - } - - /** - * Unsubscribe from events - */ - unsubscribe(subscriptionId: string): void { - if (!this._isConnected) { - return - } - - // Send CLOSE message - const closeMessage = JSON.stringify(['CLOSE', subscriptionId]) - this.ws?.send(closeMessage) - - // Remove from subscriptions - this.subscriptions.delete(subscriptionId) - console.log('🔧 NostrclientHub: Unsubscribed from', subscriptionId) - } - - /** - * Publish an event - */ - async publishEvent(event: Event): Promise { - if (!this._isConnected) { - throw new Error('Not connected to nostrclient') - } - - const eventMessage = JSON.stringify(['EVENT', event]) - this.ws?.send(eventMessage) - - console.log('🔧 NostrclientHub: Published event', event.id) - this.emit('eventPublished', { eventId: event.id }) - } - - /** - * Query events (one-time fetch) - */ - async queryEvents(filters: Filter[]): Promise { - return new Promise((resolve, reject) => { - if (!this._isConnected) { - reject(new Error('Not connected to nostrclient')) - return - } - - const queryId = `query-${Date.now()}` - const events: Event[] = [] - let eoseReceived = false - - // Create temporary subscription for query - const tempSubscription = this.subscribe({ - id: queryId, - filters, - onEvent: (event) => { - events.push(event) - }, - onEose: () => { - eoseReceived = true - this.unsubscribe(queryId) - resolve(events) - }, - onClose: () => { - if (!eoseReceived) { - reject(new Error('Query subscription closed unexpectedly')) - } - } - }) - - // Timeout after 30 seconds - setTimeout(() => { - if (!eoseReceived) { - tempSubscription() - reject(new Error('Query timeout')) - } - }, 30000) - }) - } - - /** - * Handle incoming WebSocket messages - */ - private handleMessage(data: string): void { - try { - const message = JSON.parse(data) - - if (Array.isArray(message) && message.length >= 2) { - const [type, subscriptionId, ...rest] = message - - switch (type) { - case 'EVENT': - const event = rest[0] as Event - const subscription = this.subscriptions.get(subscriptionId) - if (subscription?.onEvent) { - subscription.onEvent(event) - } - this.emit('event', { subscriptionId, event }) - break - - case 'EOSE': - const eoseSubscription = this.subscriptions.get(subscriptionId) - if (eoseSubscription?.onEose) { - eoseSubscription.onEose() - } - this.emit('eose', { subscriptionId }) - break - - case 'NOTICE': - console.log('🔧 NostrclientHub: Notice from relay:', rest[0]) - this.emit('notice', { message: rest[0] }) - break - - default: - console.log('🔧 NostrclientHub: Unknown message type:', type) - } - } - } catch (error) { - console.error('🔧 NostrclientHub: Failed to parse message:', error) - } - } - - /** - * Resubscribe to all existing subscriptions after reconnection - */ - private resubscribeAll(): void { - for (const [id, config] of this.subscriptions) { - const reqMessage = JSON.stringify([ - 'REQ', - id, - ...config.filters - ]) - this.ws?.send(reqMessage) - } - console.log('🔧 NostrclientHub: Resubscribed to', this.subscriptions.size, 'subscriptions') - } - - /** - * Schedule automatic reconnection - */ - private scheduleReconnect(): void { - if (this.reconnectInterval) { - clearTimeout(this.reconnectInterval) - } - - const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1) - console.log(`🔧 NostrclientHub: Scheduling reconnection in ${delay}ms`) - - this.reconnectInterval = setTimeout(async () => { - await this.connect() - }, delay) as unknown as number - } - - /** - * Register with VisibilityService for connection management - */ - private registerWithVisibilityService(): void { - if (!this.visibilityService) { - this.debug('VisibilityService not available') - return - } - - this.visibilityUnsubscribe = this.visibilityService.registerService( - this.metadata.name, - async () => this.handleAppResume(), - async () => this.handleAppPause() - ) - - this.debug('Registered with VisibilityService') - } - - /** - * Handle app resuming from visibility change - */ - private async handleAppResume(): Promise { - this.debug('App resumed - checking nostrclient WebSocket connection') - - // Check if we need to reconnect - if (!this.isConnected && !this._isConnecting) { - this.debug('WebSocket disconnected, attempting to reconnect...') - await this.connect() - } else if (this.isConnected) { - // Connection is alive, resubscribe to ensure all subscriptions are active - this.resubscribeAll() - } - } - - /** - * Handle app pausing from visibility change - */ - private async handleAppPause(): Promise { - this.debug('App paused - WebSocket connection will be maintained for quick resume') - - // Don't immediately disconnect - WebSocket will be checked on resume - // This allows for quick resume without full reconnection overhead - } - - /** - * Cleanup when service is disposed (overrides BaseService) - */ - protected async onDispose(): Promise { - // Unregister from visibility service - if (this.visibilityUnsubscribe) { - this.visibilityUnsubscribe() - this.visibilityUnsubscribe = undefined - } - - // Disconnect WebSocket - this.disconnect() - - // Clear all event listeners - this.removeAllListeners() - - this.debug('NostrclientHub disposed') - } -} - -// Export singleton instance -export const nostrclientHub = new NostrclientHub({ - url: import.meta.env.VITE_NOSTRCLIENT_URL || 'wss://localhost:5000/nostrclient/api/v1' -}) - -// Ensure global export -;(globalThis as any).nostrclientHub = nostrclientHub diff --git a/src/stores/nostr.ts b/src/stores/nostr.ts deleted file mode 100644 index 8092c6f..0000000 --- a/src/stores/nostr.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { defineStore } from 'pinia' -import { ref } from 'vue' -import { relayHub } from '@/lib/nostr/relayHub' -import { config } from '@/lib/config' -import { pushService, type PushSubscriptionData } from '@/lib/notifications/push' - -// Define an interface for the account object -interface NostrAccount { - privkey: string - pubkey: string -} - -export const useNostrStore = defineStore('nostr', () => { - // Connection state - const isConnected = ref(false) - const isConnecting = ref(false) - const error = ref(null) - - // Configuration - const relayUrls = ref(config.nostr.relays) - const account = ref(null) - - // Push notifications - const pushSubscription = ref(null) - const notificationsEnabled = ref(false) - - // Connection management - async function connect(): Promise { - try { - error.value = null - isConnecting.value = true - - // Initialize and connect using the centralized relay hub - await relayHub.initialize(relayUrls.value) - await relayHub.connect() - - isConnected.value = relayHub.isConnected - } catch (err) { - error.value = err instanceof Error ? err : new Error('Failed to connect') - isConnected.value = false - throw err - } finally { - isConnecting.value = false - } - } - - function disconnect(): void { - // Don't disconnect the relay hub as it's managed centrally - // Just update our local state - isConnected.value = false - isConnecting.value = false - error.value = null - } - - // Configuration setters - function setConnected(value: boolean) { - isConnected.value = value - } - - function setRelayUrls(urls: string[]) { - relayUrls.value = urls - // The relay hub will handle reconnection with new relays if needed - } - - function setAccount(nostrAccount: NostrAccount | null) { - account.value = nostrAccount - } - - // Push notification management - async function enablePushNotifications(): Promise { - try { - const subscription = await pushService.subscribe() - pushSubscription.value = subscription - notificationsEnabled.value = true - - // Store subscription in localStorage for persistence - localStorage.setItem('push-subscription', JSON.stringify(subscription)) - localStorage.setItem('notifications-enabled', 'true') - - return subscription - } catch (error) { - console.error('Failed to enable push notifications:', error) - throw error - } - } - - async function disablePushNotifications(): Promise { - try { - await pushService.unsubscribe() - pushSubscription.value = null - notificationsEnabled.value = false - - // Remove from localStorage - localStorage.removeItem('push-subscription') - localStorage.removeItem('notifications-enabled') - } catch (error) { - console.error('Failed to disable push notifications:', error) - } - } - - async function checkPushNotificationStatus(): Promise { - try { - // Check localStorage first - const storedEnabled = localStorage.getItem('notifications-enabled') === 'true' - const storedSubscription = localStorage.getItem('push-subscription') - - if (storedEnabled && storedSubscription) { - pushSubscription.value = JSON.parse(storedSubscription) - notificationsEnabled.value = true - } - - // Verify with push service - const currentSubscription = await pushService.getSubscription() - if (currentSubscription) { - pushSubscription.value = currentSubscription - notificationsEnabled.value = true - } else if (storedEnabled) { - // Stored state says enabled but no actual subscription - clear stored state - await disablePushNotifications() - } - } catch (error) { - console.error('Failed to check push notification status:', error) - } - } - - // Send test notification - async function sendTestNotification(): Promise { - await pushService.showLocalNotification({ - title: '🚨 Test Admin Announcement', - body: 'This is a test notification to verify push notifications are working correctly.', - icon: '/pwa-192x192.png', - tag: 'test-notification', - data: { - url: '/', - type: 'admin-announcement' - } - }) - } - - // Setup relay hub event listeners to keep store state in sync - function setupRelayHubListeners(): void { - relayHub.on('connected', () => { - isConnected.value = true - isConnecting.value = false - error.value = null - }) - - relayHub.on('disconnected', () => { - isConnected.value = false - isConnecting.value = false - }) - - relayHub.on('error', (err: Error) => { - error.value = err - isConnected.value = false - isConnecting.value = false - }) - - relayHub.on('connecting', () => { - isConnecting.value = true - }) - } - - // Initialize relay hub listeners - setupRelayHubListeners() - - return { - // State - isConnected, - isConnecting, - error, - relayUrls, - account, - pushSubscription, - notificationsEnabled, - - // Actions - connect, - disconnect, - setConnected, - setRelayUrls, - setAccount, - enablePushNotifications, - disablePushNotifications, - checkPushNotificationStatus, - sendTestNotification, - } -}) \ No newline at end of file