SocialEventHandlers.ts raw

   1  import {
   2    UserFollowed,
   3    UserUnfollowed,
   4    UserMuted,
   5    UserUnmuted,
   6    MuteVisibilityChanged,
   7    FollowListPublished,
   8    MuteListPublished
   9  } from '@/domain/social/events'
  10  import { EventHandler, eventDispatcher } from '@/domain/shared'
  11  
  12  /**
  13   * Handlers for social domain events
  14   *
  15   * These handlers coordinate cross-context updates when social events occur.
  16   * They bridge the Social context with Feed, Notification, and Cache contexts.
  17   */
  18  
  19  /**
  20   * Callback type for feed refresh requests
  21   */
  22  export type FeedRefreshCallback = () => void
  23  
  24  /**
  25   * Callback type for content refiltering requests
  26   */
  27  export type RefilterCallback = () => void
  28  
  29  /**
  30   * Callback type for profile prefetch requests
  31   */
  32  export type PrefetchProfileCallback = (pubkey: string) => void
  33  
  34  /**
  35   * Service callbacks that can be injected for cross-context coordination
  36   */
  37  export interface SocialHandlerCallbacks {
  38    onFeedRefreshNeeded?: FeedRefreshCallback
  39    onRefilterNeeded?: RefilterCallback
  40    onPrefetchProfile?: PrefetchProfileCallback
  41  }
  42  
  43  let callbacks: SocialHandlerCallbacks = {}
  44  
  45  /**
  46   * Set the callbacks for cross-context coordination
  47   * Call this during provider initialization
  48   */
  49  export function setSocialHandlerCallbacks(newCallbacks: SocialHandlerCallbacks): void {
  50    callbacks = { ...callbacks, ...newCallbacks }
  51  }
  52  
  53  /**
  54   * Clear all callbacks (for cleanup/testing)
  55   */
  56  export function clearSocialHandlerCallbacks(): void {
  57    callbacks = {}
  58  }
  59  
  60  /**
  61   * Handler for user followed events
  62   * Coordinates with:
  63   * - Feed context: Add followed user's content to timeline
  64   * - Cache context: Prefetch followed user's profile and notes
  65   */
  66  export const handleUserFollowed: EventHandler<UserFollowed> = async (event) => {
  67    console.debug('[SocialEventHandler] User followed:', {
  68      actor: event.actor.formatted,
  69      followed: event.followed.formatted,
  70      petname: event.petname
  71    })
  72  
  73    // Prefetch the followed user's profile for better UX
  74    if (callbacks.onPrefetchProfile) {
  75      callbacks.onPrefetchProfile(event.followed.hex)
  76    }
  77  }
  78  
  79  /**
  80   * Handler for user unfollowed events
  81   * Can be used to:
  82   * - Update feed context to exclude unfollowed user's content
  83   * - Clean up cached data for unfollowed user
  84   */
  85  export const handleUserUnfollowed: EventHandler<UserUnfollowed> = async (event) => {
  86    console.debug('[SocialEventHandler] User unfollowed:', {
  87      actor: event.actor.formatted,
  88      unfollowed: event.unfollowed.formatted
  89    })
  90  
  91    // Future: Dispatch to feed context to update content sources
  92  }
  93  
  94  /**
  95   * Handler for user muted events
  96   * Coordinates with:
  97   * - Feed context: Refilter timeline to hide muted user's content
  98   * - Notification context: Filter notifications from muted user
  99   * - DM context: Update DM filtering
 100   */
 101  export const handleUserMuted: EventHandler<UserMuted> = async (event) => {
 102    console.debug('[SocialEventHandler] User muted:', {
 103      actor: event.actor.formatted,
 104      muted: event.muted.formatted,
 105      visibility: event.visibility
 106    })
 107  
 108    // Trigger immediate refiltering of current timeline
 109    if (callbacks.onRefilterNeeded) {
 110      callbacks.onRefilterNeeded()
 111    }
 112  }
 113  
 114  /**
 115   * Handler for user unmuted events
 116   * Coordinates with:
 117   * - Feed context: Refilter timeline to show unmuted user's content
 118   * - Notification context: Restore notifications from unmuted user
 119   */
 120  export const handleUserUnmuted: EventHandler<UserUnmuted> = async (event) => {
 121    console.debug('[SocialEventHandler] User unmuted:', {
 122      actor: event.actor.formatted,
 123      unmuted: event.unmuted.formatted
 124    })
 125  
 126    // Trigger refiltering to restore unmuted user's content
 127    if (callbacks.onRefilterNeeded) {
 128      callbacks.onRefilterNeeded()
 129    }
 130  }
 131  
 132  /**
 133   * Handler for mute visibility changed events
 134   */
 135  export const handleMuteVisibilityChanged: EventHandler<MuteVisibilityChanged> = async (event) => {
 136    console.debug('[SocialEventHandler] Mute visibility changed:', {
 137      actor: event.actor.formatted,
 138      target: event.target.formatted,
 139      from: event.from,
 140      to: event.to
 141    })
 142  }
 143  
 144  /**
 145   * Handler for follow list published events
 146   * Coordinates with:
 147   * - Feed context: Refresh following feed with new list
 148   * - Cache context: Invalidate author caches
 149   */
 150  export const handleFollowListPublished: EventHandler<FollowListPublished> = async (event) => {
 151    console.debug('[SocialEventHandler] Follow list published:', {
 152      owner: event.owner.formatted,
 153      followingCount: event.followingCount
 154    })
 155  
 156    // Trigger feed refresh to reflect new following list
 157    if (callbacks.onFeedRefreshNeeded) {
 158      callbacks.onFeedRefreshNeeded()
 159    }
 160  }
 161  
 162  /**
 163   * Handler for mute list published events
 164   * Coordinates with:
 165   * - Feed context: Refilter timeline with new mute list
 166   * - Notification context: Update notification filtering
 167   */
 168  export const handleMuteListPublished: EventHandler<MuteListPublished> = async (event) => {
 169    console.debug('[SocialEventHandler] Mute list published:', {
 170      owner: event.owner.formatted,
 171      publicMuteCount: event.publicMuteCount,
 172      privateMuteCount: event.privateMuteCount
 173    })
 174  
 175    // Trigger refiltering with updated mute list
 176    if (callbacks.onRefilterNeeded) {
 177      callbacks.onRefilterNeeded()
 178    }
 179  }
 180  
 181  /**
 182   * Register all social event handlers with the event dispatcher
 183   */
 184  export function registerSocialEventHandlers(): void {
 185    eventDispatcher.on('social.user_followed', handleUserFollowed)
 186    eventDispatcher.on('social.user_unfollowed', handleUserUnfollowed)
 187    eventDispatcher.on('social.user_muted', handleUserMuted)
 188    eventDispatcher.on('social.user_unmuted', handleUserUnmuted)
 189    eventDispatcher.on('social.mute_visibility_changed', handleMuteVisibilityChanged)
 190    eventDispatcher.on('social.follow_list_published', handleFollowListPublished)
 191    eventDispatcher.on('social.mute_list_published', handleMuteListPublished)
 192  }
 193  
 194  /**
 195   * Unregister all social event handlers
 196   */
 197  export function unregisterSocialEventHandlers(): void {
 198    eventDispatcher.off('social.user_followed', handleUserFollowed)
 199    eventDispatcher.off('social.user_unfollowed', handleUserUnfollowed)
 200    eventDispatcher.off('social.user_muted', handleUserMuted)
 201    eventDispatcher.off('social.user_unmuted', handleUserUnmuted)
 202    eventDispatcher.off('social.mute_visibility_changed', handleMuteVisibilityChanged)
 203    eventDispatcher.off('social.follow_list_published', handleFollowListPublished)
 204    eventDispatcher.off('social.mute_list_published', handleMuteListPublished)
 205  }
 206