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.
This commit is contained in:
parent
92c33aa0a3
commit
7e4b64b831
8 changed files with 7 additions and 661 deletions
|
|
@ -78,7 +78,6 @@ const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
|
||||||
|
|
||||||
**Available Services:**
|
**Available Services:**
|
||||||
- `SERVICE_TOKENS.RELAY_HUB` - Centralized Nostr relay management
|
- `SERVICE_TOKENS.RELAY_HUB` - Centralized Nostr relay management
|
||||||
- `SERVICE_TOKENS.NOSTR_CLIENT_HUB` - Nostr client services
|
|
||||||
- `SERVICE_TOKENS.AUTH_SERVICE` - Authentication services
|
- `SERVICE_TOKENS.AUTH_SERVICE` - Authentication services
|
||||||
- `SERVICE_TOKENS.VISIBILITY_SERVICE` - App visibility and connection management
|
- `SERVICE_TOKENS.VISIBILITY_SERVICE` - App visibility and connection management
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -151,15 +151,13 @@ import { Button } from '@/components/ui/button'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { Bell, BellOff, TestTube, AlertCircle } from 'lucide-vue-next'
|
import { Bell, BellOff, TestTube, AlertCircle } from 'lucide-vue-next'
|
||||||
import { useNostrStore } from '@/stores/nostr'
|
|
||||||
import { notificationManager } from '@/lib/notifications/manager'
|
import { notificationManager } from '@/lib/notifications/manager'
|
||||||
import { pushService } from '@/lib/notifications/push'
|
import { pushService } from '@/lib/notifications/push'
|
||||||
import { configUtils } from '@/lib/config'
|
import { configUtils } from '@/lib/config'
|
||||||
import { toast } from 'vue-sonner'
|
import { toast } from 'vue-sonner'
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
|
|
||||||
const nostrStore = useNostrStore()
|
// Placeholder for notifications - will be implemented later
|
||||||
const { notificationsEnabled } = storeToRefs(nostrStore)
|
const notificationsEnabled = ref(false)
|
||||||
|
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
const isTestLoading = ref(false)
|
const isTestLoading = ref(false)
|
||||||
|
|
@ -174,14 +172,14 @@ onMounted(async () => {
|
||||||
|
|
||||||
async function checkStatus() {
|
async function checkStatus() {
|
||||||
isBlocked.value = pushService.getPermission() === 'denied'
|
isBlocked.value = pushService.getPermission() === 'denied'
|
||||||
await nostrStore.checkPushNotificationStatus()
|
// TODO: Implement push notification status checking when notifications are re-added
|
||||||
}
|
}
|
||||||
|
|
||||||
async function enableNotifications() {
|
async function enableNotifications() {
|
||||||
try {
|
try {
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
await nostrStore.enablePushNotifications()
|
// TODO: Implement push notification enabling when notifications are re-added
|
||||||
toast.success('Push notifications enabled!')
|
toast.success('Push notifications will be implemented later')
|
||||||
await checkStatus()
|
await checkStatus()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : 'Failed to enable notifications'
|
const message = error instanceof Error ? error.message : 'Failed to enable notifications'
|
||||||
|
|
@ -193,7 +191,7 @@ async function enableNotifications() {
|
||||||
|
|
||||||
async function disableNotifications() {
|
async function disableNotifications() {
|
||||||
try {
|
try {
|
||||||
await nostrStore.disablePushNotifications()
|
// TODO: Implement push notification disabling when notifications are re-added
|
||||||
toast.success('Push notifications disabled')
|
toast.success('Push notifications disabled')
|
||||||
await checkStatus()
|
await checkStatus()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,6 @@ export abstract class BaseService {
|
||||||
// Core dependencies with proper typing
|
// Core dependencies with proper typing
|
||||||
protected relayHub: any = null
|
protected relayHub: any = null
|
||||||
protected authService: any = null
|
protected authService: any = null
|
||||||
protected nostrClientHub: any = null
|
|
||||||
protected visibilityService: any = null
|
protected visibilityService: any = null
|
||||||
|
|
||||||
// Service state
|
// Service state
|
||||||
|
|
@ -134,7 +133,6 @@ export abstract class BaseService {
|
||||||
// Try to inject core dependencies
|
// Try to inject core dependencies
|
||||||
this.relayHub = tryInjectService(SERVICE_TOKENS.RELAY_HUB)
|
this.relayHub = tryInjectService(SERVICE_TOKENS.RELAY_HUB)
|
||||||
this.authService = tryInjectService(SERVICE_TOKENS.AUTH_SERVICE)
|
this.authService = tryInjectService(SERVICE_TOKENS.AUTH_SERVICE)
|
||||||
this.nostrClientHub = tryInjectService(SERVICE_TOKENS.NOSTR_CLIENT_HUB)
|
|
||||||
this.visibilityService = tryInjectService(SERVICE_TOKENS.VISIBILITY_SERVICE)
|
this.visibilityService = tryInjectService(SERVICE_TOKENS.VISIBILITY_SERVICE)
|
||||||
|
|
||||||
// Check if all required dependencies are available
|
// Check if all required dependencies are available
|
||||||
|
|
@ -180,9 +178,6 @@ export abstract class BaseService {
|
||||||
if (deps.includes('AuthService') && !this.authService) {
|
if (deps.includes('AuthService') && !this.authService) {
|
||||||
missing.push('AuthService')
|
missing.push('AuthService')
|
||||||
}
|
}
|
||||||
if (deps.includes('NostrClientHub') && !this.nostrClientHub) {
|
|
||||||
missing.push('NostrClientHub')
|
|
||||||
}
|
|
||||||
if (deps.includes('VisibilityService') && !this.visibilityService) {
|
if (deps.includes('VisibilityService') && !this.visibilityService) {
|
||||||
missing.push('VisibilityService')
|
missing.push('VisibilityService')
|
||||||
}
|
}
|
||||||
|
|
@ -269,7 +264,6 @@ export abstract class BaseService {
|
||||||
this.isInitialized.value = false
|
this.isInitialized.value = false
|
||||||
this.relayHub = null
|
this.relayHub = null
|
||||||
this.authService = null
|
this.authService = null
|
||||||
this.nostrClientHub = null
|
|
||||||
|
|
||||||
console.log(`♻️ ${this.metadata.name} disposed`)
|
console.log(`♻️ ${this.metadata.name} disposed`)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,6 @@ export const SERVICE_TOKENS = {
|
||||||
|
|
||||||
// Nostr services
|
// Nostr services
|
||||||
RELAY_HUB: Symbol('relayHub'),
|
RELAY_HUB: Symbol('relayHub'),
|
||||||
NOSTR_CLIENT_HUB: Symbol('nostrClientHub'),
|
|
||||||
|
|
||||||
// Auth services
|
// Auth services
|
||||||
AUTH_SERVICE: Symbol('authService'),
|
AUTH_SERVICE: Symbol('authService'),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import type { App } from 'vue'
|
||||||
import type { ModulePlugin } from '@/core/types'
|
import type { ModulePlugin } from '@/core/types'
|
||||||
import { container, SERVICE_TOKENS } from '@/core/di-container'
|
import { container, SERVICE_TOKENS } from '@/core/di-container'
|
||||||
import { relayHub } from './nostr/relay-hub'
|
import { relayHub } from './nostr/relay-hub'
|
||||||
import { nostrclientHub } from './nostr/nostrclient-hub'
|
|
||||||
|
|
||||||
// Import auth services
|
// Import auth services
|
||||||
import { auth } from './auth/auth-service'
|
import { auth } from './auth/auth-service'
|
||||||
|
|
@ -27,7 +26,6 @@ export const baseModule: ModulePlugin = {
|
||||||
|
|
||||||
// Register core Nostr services
|
// Register core Nostr services
|
||||||
container.provide(SERVICE_TOKENS.RELAY_HUB, relayHub)
|
container.provide(SERVICE_TOKENS.RELAY_HUB, relayHub)
|
||||||
container.provide(SERVICE_TOKENS.NOSTR_CLIENT_HUB, nostrclientHub)
|
|
||||||
|
|
||||||
// Register auth service
|
// Register auth service
|
||||||
container.provide(SERVICE_TOKENS.AUTH_SERVICE, auth)
|
container.provide(SERVICE_TOKENS.AUTH_SERVICE, auth)
|
||||||
|
|
@ -56,10 +54,6 @@ export const baseModule: ModulePlugin = {
|
||||||
waitForDependencies: false, // VisibilityService has no dependencies
|
waitForDependencies: false, // VisibilityService has no dependencies
|
||||||
maxRetries: 1
|
maxRetries: 1
|
||||||
})
|
})
|
||||||
await nostrclientHub.initialize({
|
|
||||||
waitForDependencies: true, // NostrClientHub depends on VisibilityService
|
|
||||||
maxRetries: 3
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log('✅ Base module installed successfully')
|
console.log('✅ Base module installed successfully')
|
||||||
},
|
},
|
||||||
|
|
@ -69,7 +63,6 @@ export const baseModule: ModulePlugin = {
|
||||||
|
|
||||||
// Cleanup services
|
// Cleanup services
|
||||||
await relayHub.dispose()
|
await relayHub.dispose()
|
||||||
await nostrclientHub.dispose()
|
|
||||||
await auth.dispose()
|
await auth.dispose()
|
||||||
await paymentService.dispose()
|
await paymentService.dispose()
|
||||||
await visibilityService.dispose()
|
await visibilityService.dispose()
|
||||||
|
|
@ -79,7 +72,6 @@ export const baseModule: ModulePlugin = {
|
||||||
|
|
||||||
services: {
|
services: {
|
||||||
relayHub,
|
relayHub,
|
||||||
nostrclientHub,
|
|
||||||
auth,
|
auth,
|
||||||
paymentService,
|
paymentService,
|
||||||
pwaService
|
pwaService
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
// Re-export Nostr infrastructure from base module
|
// Re-export Nostr infrastructure from base module
|
||||||
export { RelayHub } from './relay-hub'
|
export { RelayHub } from './relay-hub'
|
||||||
export { NostrclientHub } from './nostrclient-hub'
|
|
||||||
export { relayHub } from './relay-hub'
|
export { relayHub } from './relay-hub'
|
||||||
export { nostrclientHub } from './nostrclient-hub'
|
|
||||||
|
|
||||||
// Re-export types
|
// Re-export types
|
||||||
export type {
|
export type {
|
||||||
|
|
@ -10,7 +8,3 @@ export type {
|
||||||
SubscriptionConfig,
|
SubscriptionConfig,
|
||||||
RelayStatus
|
RelayStatus
|
||||||
} from './relay-hub'
|
} from './relay-hub'
|
||||||
|
|
||||||
export type {
|
|
||||||
NostrclientConfig
|
|
||||||
} from './nostrclient-hub'
|
|
||||||
|
|
@ -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<string, SubscriptionConfig> = 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<void> {
|
|
||||||
// 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<void> {
|
|
||||||
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<void> {
|
|
||||||
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<Event[]> {
|
|
||||||
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<void> {
|
|
||||||
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<void> {
|
|
||||||
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<void> {
|
|
||||||
// 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
|
|
||||||
|
|
@ -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<Error | null>(null)
|
|
||||||
|
|
||||||
// Configuration
|
|
||||||
const relayUrls = ref<string[]>(config.nostr.relays)
|
|
||||||
const account = ref<NostrAccount | null>(null)
|
|
||||||
|
|
||||||
// Push notifications
|
|
||||||
const pushSubscription = ref<PushSubscriptionData | null>(null)
|
|
||||||
const notificationsEnabled = ref(false)
|
|
||||||
|
|
||||||
// Connection management
|
|
||||||
async function connect(): Promise<void> {
|
|
||||||
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<PushSubscriptionData> {
|
|
||||||
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<void> {
|
|
||||||
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<void> {
|
|
||||||
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<void> {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue