chore(nostr-feed): delete dead-code ReactionService + useReactions duplicates #80
2 changed files with 0 additions and 687 deletions
|
|
@ -1,102 +0,0 @@
|
|||
import { computed } from 'vue'
|
||||
import { injectService, SERVICE_TOKENS } from '@/core/di-container'
|
||||
import type { ReactionService, EventReactions } from '../services/ReactionService'
|
||||
import { useToast } from '@/core/composables/useToast'
|
||||
|
||||
/**
|
||||
* Composable for managing reactions in the feed
|
||||
*/
|
||||
export function useReactions() {
|
||||
const reactionService = injectService<ReactionService>(SERVICE_TOKENS.REACTION_SERVICE)
|
||||
const toast = useToast()
|
||||
|
||||
/**
|
||||
* Get reactions for a specific event
|
||||
*/
|
||||
const getEventReactions = (eventId: string): EventReactions => {
|
||||
if (!reactionService) {
|
||||
return {
|
||||
eventId,
|
||||
likes: 0,
|
||||
dislikes: 0,
|
||||
totalReactions: 0,
|
||||
userHasLiked: false,
|
||||
userHasDisliked: false,
|
||||
reactions: []
|
||||
}
|
||||
}
|
||||
return reactionService.getEventReactions(eventId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to reactions for a list of event IDs
|
||||
*/
|
||||
const subscribeToReactions = async (eventIds: string[]): Promise<void> => {
|
||||
if (!reactionService || eventIds.length === 0) return
|
||||
|
||||
try {
|
||||
await reactionService.subscribeToReactions(eventIds)
|
||||
} catch (error) {
|
||||
console.error('Failed to subscribe to reactions:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle like on an event - like if not liked, unlike if already liked
|
||||
*/
|
||||
const toggleLike = async (eventId: string, eventPubkey: string, eventKind: number): Promise<void> => {
|
||||
if (!reactionService) {
|
||||
toast.error('Reaction service not available')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await reactionService.toggleLikeEvent(eventId, eventPubkey, eventKind)
|
||||
|
||||
// Check if we liked or unliked
|
||||
const eventReactions = reactionService.getEventReactions(eventId)
|
||||
if (eventReactions.userHasLiked) {
|
||||
toast.success('Post liked!')
|
||||
} else {
|
||||
toast.success('Like removed')
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Failed to toggle reaction'
|
||||
|
||||
if (message.includes('authenticated')) {
|
||||
toast.error('Please sign in to react to posts')
|
||||
} else if (message.includes('Not connected')) {
|
||||
toast.error('Not connected to relays')
|
||||
} else {
|
||||
toast.error(message)
|
||||
}
|
||||
|
||||
console.error('Failed to toggle like:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get loading state
|
||||
*/
|
||||
const isLoading = computed(() => {
|
||||
return reactionService?.isLoading ?? false
|
||||
})
|
||||
|
||||
/**
|
||||
* Get all event reactions (for debugging)
|
||||
*/
|
||||
const allEventReactions = computed(() => {
|
||||
return reactionService?.eventReactions ?? new Map()
|
||||
})
|
||||
|
||||
return {
|
||||
// Methods
|
||||
getEventReactions,
|
||||
subscribeToReactions,
|
||||
toggleLike,
|
||||
|
||||
// State
|
||||
isLoading,
|
||||
allEventReactions
|
||||
}
|
||||
}
|
||||
|
|
@ -1,585 +0,0 @@
|
|||
import { ref, reactive } from 'vue'
|
||||
import { BaseService } from '@/core/base/BaseService'
|
||||
import { injectService, SERVICE_TOKENS } from '@/core/di-container'
|
||||
import { finalizeEvent, type EventTemplate } from 'nostr-tools'
|
||||
import type { Event as NostrEvent } from 'nostr-tools'
|
||||
|
||||
export interface Reaction {
|
||||
id: string
|
||||
eventId: string // The event being reacted to
|
||||
pubkey: string // Who reacted
|
||||
content: string // The reaction content ('+', '-', emoji)
|
||||
created_at: number
|
||||
}
|
||||
|
||||
export interface EventReactions {
|
||||
eventId: string
|
||||
likes: number
|
||||
dislikes: number
|
||||
totalReactions: number
|
||||
userHasLiked: boolean
|
||||
userHasDisliked: boolean
|
||||
userReactionId?: string // Track the user's reaction ID for deletion
|
||||
reactions: Reaction[]
|
||||
}
|
||||
|
||||
export class ReactionService extends BaseService {
|
||||
protected readonly metadata = {
|
||||
name: 'ReactionService',
|
||||
version: '1.0.0',
|
||||
dependencies: []
|
||||
}
|
||||
|
||||
protected relayHub: any = null
|
||||
protected authService: any = null
|
||||
|
||||
// Reaction state - indexed by event ID
|
||||
private _eventReactions = reactive(new Map<string, EventReactions>())
|
||||
private _isLoading = ref(false)
|
||||
|
||||
// Track reaction subscription
|
||||
private currentSubscription: string | null = null
|
||||
private currentUnsubscribe: (() => void) | null = null
|
||||
|
||||
// Track which events we're monitoring
|
||||
private monitoredEvents = new Set<string>()
|
||||
|
||||
// Track deleted reactions to hide them
|
||||
private deletedReactions = new Set<string>()
|
||||
|
||||
protected async onInitialize(): Promise<void> {
|
||||
console.log('ReactionService: Starting initialization...')
|
||||
|
||||
this.relayHub = injectService(SERVICE_TOKENS.RELAY_HUB)
|
||||
this.authService = injectService(SERVICE_TOKENS.AUTH_SERVICE)
|
||||
|
||||
if (!this.relayHub) {
|
||||
throw new Error('RelayHub service not available')
|
||||
}
|
||||
|
||||
// Deletion monitoring is now handled by FeedService's consolidated subscription
|
||||
console.log('ReactionService: Initialization complete (deletion monitoring handled by FeedService)')
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reactions for a specific event
|
||||
*/
|
||||
getEventReactions(eventId: string): EventReactions {
|
||||
if (!this._eventReactions.has(eventId)) {
|
||||
this._eventReactions.set(eventId, {
|
||||
eventId,
|
||||
likes: 0,
|
||||
dislikes: 0,
|
||||
totalReactions: 0,
|
||||
userHasLiked: false,
|
||||
userHasDisliked: false,
|
||||
reactions: []
|
||||
})
|
||||
}
|
||||
return this._eventReactions.get(eventId)!
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to reactions for a list of event IDs
|
||||
*/
|
||||
async subscribeToReactions(eventIds: string[]): Promise<void> {
|
||||
if (eventIds.length === 0) return
|
||||
|
||||
// Filter out events we're already monitoring
|
||||
const newEventIds = eventIds.filter(id => !this.monitoredEvents.has(id))
|
||||
if (newEventIds.length === 0) return
|
||||
|
||||
console.log(`ReactionService: Subscribing to reactions for ${newEventIds.length} events`)
|
||||
|
||||
try {
|
||||
if (!this.relayHub?.isConnected) {
|
||||
await this.relayHub?.connect()
|
||||
}
|
||||
|
||||
// Add to monitored set
|
||||
newEventIds.forEach(id => this.monitoredEvents.add(id))
|
||||
|
||||
const subscriptionId = `reactions-${Date.now()}`
|
||||
|
||||
// Subscribe to reactions (kind 7) for these events
|
||||
// Deletions (kind 5) are now handled by FeedService's consolidated subscription
|
||||
const filters = [
|
||||
{
|
||||
kinds: [7], // Reactions
|
||||
'#e': newEventIds, // Events being reacted to
|
||||
limit: 1000
|
||||
}
|
||||
]
|
||||
|
||||
const unsubscribe = this.relayHub.subscribe({
|
||||
id: subscriptionId,
|
||||
filters: filters,
|
||||
onEvent: (event: NostrEvent) => {
|
||||
this.handleReactionEvent(event)
|
||||
},
|
||||
onEose: () => {
|
||||
console.log(`ReactionService: Subscription ${subscriptionId} ready`)
|
||||
}
|
||||
})
|
||||
|
||||
// Store subscription info (we can have multiple)
|
||||
if (!this.currentSubscription) {
|
||||
this.currentSubscription = subscriptionId
|
||||
this.currentUnsubscribe = unsubscribe
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to subscribe to reactions:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle incoming reaction event
|
||||
* Made public so FeedService can route kind 7 events to this service
|
||||
*/
|
||||
public handleReactionEvent(event: NostrEvent): void {
|
||||
try {
|
||||
// Find the event being reacted to
|
||||
const eTag = event.tags.find(tag => tag[0] === 'e')
|
||||
if (!eTag || !eTag[1]) {
|
||||
console.warn('Reaction event missing e tag:', event.id)
|
||||
return
|
||||
}
|
||||
|
||||
const eventId = eTag[1]
|
||||
const content = event.content.trim()
|
||||
|
||||
// Create reaction object
|
||||
const reaction: Reaction = {
|
||||
id: event.id,
|
||||
eventId,
|
||||
pubkey: event.pubkey,
|
||||
content,
|
||||
created_at: event.created_at
|
||||
}
|
||||
|
||||
// Update event reactions
|
||||
const eventReactions = this.getEventReactions(eventId)
|
||||
|
||||
// Check if this reaction already exists (deduplication) or is deleted
|
||||
const existingIndex = eventReactions.reactions.findIndex(r => r.id === reaction.id)
|
||||
if (existingIndex >= 0) {
|
||||
return // Already have this reaction
|
||||
}
|
||||
|
||||
// Check if this reaction has been deleted
|
||||
if (this.deletedReactions.has(reaction.id)) {
|
||||
return // This reaction was deleted
|
||||
}
|
||||
|
||||
// IMPORTANT: Remove any previous reaction from the same user
|
||||
// This ensures one reaction per user per event, even if deletion events aren't processed
|
||||
const previousReactionIndex = eventReactions.reactions.findIndex(r =>
|
||||
r.pubkey === reaction.pubkey &&
|
||||
r.content === reaction.content
|
||||
)
|
||||
|
||||
if (previousReactionIndex >= 0) {
|
||||
// Replace the old reaction with the new one
|
||||
eventReactions.reactions[previousReactionIndex] = reaction
|
||||
} else {
|
||||
// Add as new reaction
|
||||
eventReactions.reactions.push(reaction)
|
||||
}
|
||||
|
||||
// Recalculate counts and user state
|
||||
this.recalculateEventReactions(eventId)
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to handle reaction event:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle deletion event (called by FeedService when a kind 5 event with k=7 is received)
|
||||
* Made public so FeedService can route deletion events to this service
|
||||
*/
|
||||
public handleDeletionEvent(event: NostrEvent): void {
|
||||
try {
|
||||
// Process each deleted event
|
||||
const eTags = event.tags.filter(tag => tag[0] === 'e')
|
||||
const deletionAuthor = event.pubkey
|
||||
|
||||
for (const eTag of eTags) {
|
||||
const deletedEventId = eTag[1]
|
||||
if (deletedEventId) {
|
||||
// Add to deleted set
|
||||
this.deletedReactions.add(deletedEventId)
|
||||
|
||||
// Find and remove the reaction from all event reactions
|
||||
for (const [eventId, eventReactions] of this._eventReactions) {
|
||||
const reactionIndex = eventReactions.reactions.findIndex(r => r.id === deletedEventId)
|
||||
|
||||
if (reactionIndex >= 0) {
|
||||
const reaction = eventReactions.reactions[reactionIndex]
|
||||
|
||||
// IMPORTANT: Only process deletion if it's from the same user who created the reaction
|
||||
// This follows NIP-09 spec: "Relays SHOULD delete or stop publishing any referenced events
|
||||
// that have an identical `pubkey` as the deletion request"
|
||||
if (reaction.pubkey === deletionAuthor) {
|
||||
eventReactions.reactions.splice(reactionIndex, 1)
|
||||
// Recalculate counts for this event
|
||||
this.recalculateEventReactions(eventId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to handle deletion event:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculate reaction counts and user state for an event
|
||||
*/
|
||||
private recalculateEventReactions(eventId: string): void {
|
||||
const eventReactions = this.getEventReactions(eventId)
|
||||
const userPubkey = this.authService?.user?.value?.pubkey
|
||||
|
||||
// Use Sets to track unique users who liked/disliked
|
||||
const likedUsers = new Set<string>()
|
||||
const dislikedUsers = new Set<string>()
|
||||
let userHasLiked = false
|
||||
let userHasDisliked = false
|
||||
let userReactionId: string | undefined
|
||||
|
||||
// Group reactions by user, keeping only the most recent
|
||||
const latestReactionsByUser = new Map<string, Reaction>()
|
||||
|
||||
for (const reaction of eventReactions.reactions) {
|
||||
// Skip deleted reactions
|
||||
if (this.deletedReactions.has(reaction.id)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Keep only the latest reaction from each user
|
||||
const existing = latestReactionsByUser.get(reaction.pubkey)
|
||||
if (!existing || reaction.created_at > existing.created_at) {
|
||||
latestReactionsByUser.set(reaction.pubkey, reaction)
|
||||
}
|
||||
}
|
||||
|
||||
// Now count unique reactions
|
||||
for (const reaction of latestReactionsByUser.values()) {
|
||||
const isLike = reaction.content === '+' || reaction.content === '❤️' || reaction.content === ''
|
||||
const isDislike = reaction.content === '-'
|
||||
|
||||
if (isLike) {
|
||||
likedUsers.add(reaction.pubkey)
|
||||
if (userPubkey && reaction.pubkey === userPubkey) {
|
||||
userHasLiked = true
|
||||
userReactionId = reaction.id
|
||||
}
|
||||
} else if (isDislike) {
|
||||
dislikedUsers.add(reaction.pubkey)
|
||||
if (userPubkey && reaction.pubkey === userPubkey) {
|
||||
userHasDisliked = true
|
||||
userReactionId = reaction.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the reactive state with unique user counts
|
||||
eventReactions.likes = likedUsers.size
|
||||
eventReactions.dislikes = dislikedUsers.size
|
||||
eventReactions.totalReactions = latestReactionsByUser.size
|
||||
eventReactions.userHasLiked = userHasLiked
|
||||
eventReactions.userHasDisliked = userHasDisliked
|
||||
eventReactions.userReactionId = userReactionId
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a heart reaction (like) to an event
|
||||
*/
|
||||
async likeEvent(eventId: string, eventPubkey: string, eventKind: number): Promise<void> {
|
||||
if (!this.authService?.isAuthenticated?.value) {
|
||||
throw new Error('Must be authenticated to react')
|
||||
}
|
||||
|
||||
if (!this.relayHub?.isConnected) {
|
||||
throw new Error('Not connected to relays')
|
||||
}
|
||||
|
||||
const userPubkey = this.authService.user.value?.pubkey
|
||||
const userPrivkey = this.authService.user.value?.prvkey
|
||||
|
||||
if (!userPubkey || !userPrivkey) {
|
||||
throw new Error('User keys not available')
|
||||
}
|
||||
|
||||
// Check if user already liked this event
|
||||
const eventReactions = this.getEventReactions(eventId)
|
||||
if (eventReactions.userHasLiked) {
|
||||
throw new Error('Already liked this event')
|
||||
}
|
||||
|
||||
try {
|
||||
this._isLoading.value = true
|
||||
|
||||
// Create reaction event template according to NIP-25
|
||||
const eventTemplate: EventTemplate = {
|
||||
kind: 7, // Reaction
|
||||
content: '+', // Like reaction
|
||||
tags: [
|
||||
['e', eventId, '', eventPubkey], // Event being reacted to
|
||||
['p', eventPubkey], // Author of the event being reacted to
|
||||
['k', eventKind.toString()] // Kind of the event being reacted to
|
||||
],
|
||||
created_at: Math.floor(Date.now() / 1000)
|
||||
}
|
||||
|
||||
// Sign the event
|
||||
const privkeyBytes = this.hexToUint8Array(userPrivkey)
|
||||
const signedEvent = finalizeEvent(eventTemplate, privkeyBytes)
|
||||
|
||||
// Publish the reaction
|
||||
await this.relayHub.publishEvent(signedEvent)
|
||||
|
||||
// Optimistically update local state
|
||||
this.handleReactionEvent(signedEvent)
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to like event:', error)
|
||||
throw error
|
||||
} finally {
|
||||
this._isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a like from an event (unlike) using NIP-09 deletion events
|
||||
*/
|
||||
async unlikeEvent(eventId: string): Promise<void> {
|
||||
if (!this.authService?.isAuthenticated?.value) {
|
||||
throw new Error('Must be authenticated to remove reaction')
|
||||
}
|
||||
|
||||
if (!this.relayHub?.isConnected) {
|
||||
throw new Error('Not connected to relays')
|
||||
}
|
||||
|
||||
const userPubkey = this.authService.user.value?.pubkey
|
||||
const userPrivkey = this.authService.user.value?.prvkey
|
||||
|
||||
if (!userPubkey || !userPrivkey) {
|
||||
throw new Error('User keys not available')
|
||||
}
|
||||
|
||||
// Get the user's reaction ID to delete
|
||||
const eventReactions = this.getEventReactions(eventId)
|
||||
|
||||
if (!eventReactions.userHasLiked || !eventReactions.userReactionId) {
|
||||
throw new Error('No reaction to remove')
|
||||
}
|
||||
|
||||
try {
|
||||
this._isLoading.value = true
|
||||
|
||||
// Create deletion event according to NIP-09
|
||||
const eventTemplate: EventTemplate = {
|
||||
kind: 5, // Deletion request
|
||||
content: '', // Empty content or reason
|
||||
tags: [
|
||||
['e', eventReactions.userReactionId], // The reaction event to delete
|
||||
['k', '7'] // Kind of event being deleted (reaction)
|
||||
],
|
||||
created_at: Math.floor(Date.now() / 1000)
|
||||
}
|
||||
|
||||
// Sign the event
|
||||
const privkeyBytes = this.hexToUint8Array(userPrivkey)
|
||||
const signedEvent = finalizeEvent(eventTemplate, privkeyBytes)
|
||||
|
||||
// Publish the deletion
|
||||
const result = await this.relayHub.publishEvent(signedEvent)
|
||||
|
||||
console.log(`ReactionService: Deletion published to ${result.success}/${result.total} relays`)
|
||||
|
||||
// Optimistically update local state
|
||||
this.handleDeletionEvent(signedEvent)
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to unlike event:', error)
|
||||
throw error
|
||||
} finally {
|
||||
this._isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle like on an event - like if not liked, unlike if already liked
|
||||
*/
|
||||
async toggleLikeEvent(eventId: string, eventPubkey: string, eventKind: number): Promise<void> {
|
||||
const eventReactions = this.getEventReactions(eventId)
|
||||
|
||||
if (eventReactions.userHasLiked) {
|
||||
// Unlike the event
|
||||
await this.unlikeEvent(eventId)
|
||||
} else {
|
||||
// Like the event
|
||||
await this.likeEvent(eventId, eventPubkey, eventKind)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a dislike reaction to an event
|
||||
*/
|
||||
async dislikeEvent(eventId: string, eventPubkey: string, eventKind: number): Promise<void> {
|
||||
if (!this.authService?.isAuthenticated?.value) {
|
||||
throw new Error('Must be authenticated to react')
|
||||
}
|
||||
|
||||
if (!this.relayHub?.isConnected) {
|
||||
throw new Error('Not connected to relays')
|
||||
}
|
||||
|
||||
const userPubkey = this.authService.user.value?.pubkey
|
||||
const userPrivkey = this.authService.user.value?.prvkey
|
||||
|
||||
if (!userPubkey || !userPrivkey) {
|
||||
throw new Error('User keys not available')
|
||||
}
|
||||
|
||||
// Check if user already disliked this event
|
||||
const eventReactions = this.getEventReactions(eventId)
|
||||
if (eventReactions.userHasDisliked) {
|
||||
throw new Error('Already disliked this event')
|
||||
}
|
||||
|
||||
try {
|
||||
this._isLoading.value = true
|
||||
|
||||
// Create reaction event template according to NIP-25
|
||||
const eventTemplate: EventTemplate = {
|
||||
kind: 7, // Reaction
|
||||
content: '-', // Dislike reaction
|
||||
tags: [
|
||||
['e', eventId, '', eventPubkey], // Event being reacted to
|
||||
['p', eventPubkey], // Author of the event being reacted to
|
||||
['k', eventKind.toString()] // Kind of the event being reacted to
|
||||
],
|
||||
created_at: Math.floor(Date.now() / 1000)
|
||||
}
|
||||
|
||||
// Sign the event
|
||||
const privkeyBytes = this.hexToUint8Array(userPrivkey)
|
||||
const signedEvent = finalizeEvent(eventTemplate, privkeyBytes)
|
||||
|
||||
// Publish the reaction
|
||||
await this.relayHub.publishEvent(signedEvent)
|
||||
|
||||
// Optimistically update local state
|
||||
this.handleReactionEvent(signedEvent)
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to dislike event:', error)
|
||||
throw error
|
||||
} finally {
|
||||
this._isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a dislike from an event using NIP-09 deletion events
|
||||
*/
|
||||
async undislikeEvent(eventId: string): Promise<void> {
|
||||
if (!this.authService?.isAuthenticated?.value) {
|
||||
throw new Error('Must be authenticated to remove reaction')
|
||||
}
|
||||
|
||||
if (!this.relayHub?.isConnected) {
|
||||
throw new Error('Not connected to relays')
|
||||
}
|
||||
|
||||
const userPubkey = this.authService.user.value?.pubkey
|
||||
const userPrivkey = this.authService.user.value?.prvkey
|
||||
|
||||
if (!userPubkey || !userPrivkey) {
|
||||
throw new Error('User keys not available')
|
||||
}
|
||||
|
||||
// Get the user's reaction ID to delete
|
||||
const eventReactions = this.getEventReactions(eventId)
|
||||
|
||||
if (!eventReactions.userHasDisliked || !eventReactions.userReactionId) {
|
||||
throw new Error('No dislike reaction to remove')
|
||||
}
|
||||
|
||||
try {
|
||||
this._isLoading.value = true
|
||||
|
||||
// Create deletion event according to NIP-09
|
||||
const eventTemplate: EventTemplate = {
|
||||
kind: 5, // Deletion request
|
||||
content: '', // Empty content or reason
|
||||
tags: [
|
||||
['e', eventReactions.userReactionId], // The reaction event to delete
|
||||
['k', '7'] // Kind of event being deleted (reaction)
|
||||
],
|
||||
created_at: Math.floor(Date.now() / 1000)
|
||||
}
|
||||
|
||||
// Sign the event
|
||||
const privkeyBytes = this.hexToUint8Array(userPrivkey)
|
||||
const signedEvent = finalizeEvent(eventTemplate, privkeyBytes)
|
||||
|
||||
// Publish the deletion
|
||||
const result = await this.relayHub.publishEvent(signedEvent)
|
||||
|
||||
console.log(`ReactionService: Dislike deletion published to ${result.success}/${result.total} relays`)
|
||||
|
||||
// Optimistically update local state
|
||||
this.handleDeletionEvent(signedEvent)
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to remove dislike:', error)
|
||||
throw error
|
||||
} finally {
|
||||
this._isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to convert hex string to Uint8Array
|
||||
*/
|
||||
private hexToUint8Array(hex: string): Uint8Array {
|
||||
const bytes = new Uint8Array(hex.length / 2)
|
||||
for (let i = 0; i < hex.length; i += 2) {
|
||||
bytes[i / 2] = parseInt(hex.substr(i, 2), 16)
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all event reactions
|
||||
*/
|
||||
get eventReactions(): Map<string, EventReactions> {
|
||||
return this._eventReactions
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if currently loading
|
||||
*/
|
||||
get isLoading(): boolean {
|
||||
return this._isLoading.value
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup
|
||||
*/
|
||||
protected async onDestroy(): Promise<void> {
|
||||
if (this.currentUnsubscribe) {
|
||||
this.currentUnsubscribe()
|
||||
}
|
||||
// deletionUnsubscribe is no longer used - deletions handled by FeedService
|
||||
this._eventReactions.clear()
|
||||
this.monitoredEvents.clear()
|
||||
this.deletedReactions.clear()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue