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