FeedType.ts raw

   1  /**
   2   * FeedType Value Object
   3   *
   4   * Represents the type of feed being displayed.
   5   * Immutable and self-validating.
   6   */
   7  
   8  export type FeedTypeValue = 'following' | 'pinned' | 'relays' | 'relay'
   9  
  10  export class FeedType {
  11    private constructor(
  12      private readonly _value: FeedTypeValue,
  13      private readonly _relaySetId: string | null,
  14      private readonly _relayUrl: string | null
  15    ) {}
  16  
  17    /**
  18     * Create a following feed type (shows posts from followed users)
  19     */
  20    static following(): FeedType {
  21      return new FeedType('following', null, null)
  22    }
  23  
  24    /**
  25     * Create a pinned feed type (shows posts from pinned users)
  26     */
  27    static pinned(): FeedType {
  28      return new FeedType('pinned', null, null)
  29    }
  30  
  31    /**
  32     * Create a relay set feed type (shows posts from a group of relays)
  33     */
  34    static relays(setId: string): FeedType {
  35      if (!setId || setId.trim() === '') {
  36        throw new Error('Relay set ID cannot be empty')
  37      }
  38      return new FeedType('relays', setId, null)
  39    }
  40  
  41    /**
  42     * Create a single relay feed type (shows posts from one relay)
  43     */
  44    static relay(url: string): FeedType {
  45      if (!url || url.trim() === '') {
  46        throw new Error('Relay URL cannot be empty')
  47      }
  48      return new FeedType('relay', null, url)
  49    }
  50  
  51    /**
  52     * Parse from string representation
  53     */
  54    static tryFromString(value: string, id?: string): FeedType | null {
  55      switch (value) {
  56        case 'following':
  57          return FeedType.following()
  58        case 'pinned':
  59          return FeedType.pinned()
  60        case 'relays':
  61          return id ? FeedType.relays(id) : null
  62        case 'relay':
  63          return id ? FeedType.relay(id) : null
  64        default:
  65          return null
  66      }
  67    }
  68  
  69    get value(): FeedTypeValue {
  70      return this._value
  71    }
  72  
  73    get relaySetId(): string | null {
  74      return this._relaySetId
  75    }
  76  
  77    get relayUrl(): string | null {
  78      return this._relayUrl
  79    }
  80  
  81    /**
  82     * Check if this is a social feed (following or pinned)
  83     */
  84    get isSocialFeed(): boolean {
  85      return this._value === 'following' || this._value === 'pinned'
  86    }
  87  
  88    /**
  89     * Check if this is a relay-based feed
  90     */
  91    get isRelayFeed(): boolean {
  92      return this._value === 'relays' || this._value === 'relay'
  93    }
  94  
  95    equals(other: FeedType): boolean {
  96      if (this._value !== other._value) return false
  97      if (this._relaySetId !== other._relaySetId) return false
  98      if (this._relayUrl !== other._relayUrl) return false
  99      return true
 100    }
 101  
 102    toString(): string {
 103      switch (this._value) {
 104        case 'following':
 105          return 'following'
 106        case 'pinned':
 107          return 'pinned'
 108        case 'relays':
 109          return `relays:${this._relaySetId}`
 110        case 'relay':
 111          return `relay:${this._relayUrl}`
 112      }
 113    }
 114  }
 115