@towns-protocol/sdk 0.0.242 → 0.0.244

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/dist/client.d.ts +5 -1
  2. package/dist/client.d.ts.map +1 -1
  3. package/dist/client.js +64 -18
  4. package/dist/client.js.map +1 -1
  5. package/dist/id.d.ts +3 -1
  6. package/dist/id.d.ts.map +1 -1
  7. package/dist/id.js +3 -0
  8. package/dist/id.js.map +1 -1
  9. package/dist/index.d.ts +4 -8
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +4 -8
  12. package/dist/index.js.map +1 -1
  13. package/dist/makeAuthenticationRpcClient.d.ts +1 -1
  14. package/dist/makeAuthenticationRpcClient.d.ts.map +1 -1
  15. package/dist/makeAuthenticationRpcClient.js +2 -2
  16. package/dist/makeAuthenticationRpcClient.js.map +1 -1
  17. package/dist/makeNotificationRpcClient.d.ts +1 -1
  18. package/dist/makeNotificationRpcClient.d.ts.map +1 -1
  19. package/dist/makeNotificationRpcClient.js +2 -2
  20. package/dist/makeNotificationRpcClient.js.map +1 -1
  21. package/dist/makeStreamRpcClient.d.ts +1 -1
  22. package/dist/makeStreamRpcClient.d.ts.map +1 -1
  23. package/dist/makeStreamRpcClient.js +2 -2
  24. package/dist/makeStreamRpcClient.js.map +1 -1
  25. package/dist/notificationService.d.ts.map +1 -1
  26. package/dist/notificationService.js +2 -1
  27. package/dist/notificationService.js.map +1 -1
  28. package/dist/riverConfig.js +1 -1
  29. package/dist/riverConfig.js.map +1 -1
  30. package/dist/rpcCommon.d.ts +1 -3
  31. package/dist/rpcCommon.d.ts.map +1 -1
  32. package/dist/rpcCommon.js +1 -13
  33. package/dist/rpcCommon.js.map +1 -1
  34. package/dist/sign.d.ts +1 -1
  35. package/dist/sign.d.ts.map +1 -1
  36. package/dist/sign.js +13 -7
  37. package/dist/sign.js.map +1 -1
  38. package/dist/stream.d.ts +4 -2
  39. package/dist/stream.d.ts.map +1 -1
  40. package/dist/stream.js +9 -6
  41. package/dist/stream.js.map +1 -1
  42. package/dist/streamEvents.d.ts +3 -3
  43. package/dist/streamEvents.d.ts.map +1 -1
  44. package/dist/streamStateView.d.ts +9 -6
  45. package/dist/streamStateView.d.ts.map +1 -1
  46. package/dist/streamStateView.js +58 -62
  47. package/dist/streamStateView.js.map +1 -1
  48. package/dist/streamStateView_Space.js +1 -1
  49. package/dist/streamStateView_Space.js.map +1 -1
  50. package/dist/streamStateView_UserMetadata.js +1 -1
  51. package/dist/streamStateView_UserMetadata.js.map +1 -1
  52. package/dist/streamUtils.d.ts.map +1 -1
  53. package/dist/streamUtils.js +2 -0
  54. package/dist/streamUtils.js.map +1 -1
  55. package/dist/streams-view/streamsView.d.ts +18 -0
  56. package/dist/streams-view/streamsView.d.ts.map +1 -0
  57. package/dist/streams-view/streamsView.js +25 -0
  58. package/dist/streams-view/streamsView.js.map +1 -0
  59. package/dist/streams-view/timelineEvents.d.ts +11 -0
  60. package/dist/streams-view/timelineEvents.d.ts.map +1 -0
  61. package/dist/{sync-agent/timeline/models/timelineEvent.js → streams-view/timelineEvents.js} +240 -303
  62. package/dist/streams-view/timelineEvents.js.map +1 -0
  63. package/dist/streams-view/timelinesView.d.ts +27 -0
  64. package/dist/streams-view/timelinesView.d.ts.map +1 -0
  65. package/dist/streams-view/timelinesView.js +109 -0
  66. package/dist/streams-view/timelinesView.js.map +1 -0
  67. package/dist/streams-view/timelinesViewModel.d.ts +38 -0
  68. package/dist/streams-view/timelinesViewModel.d.ts.map +1 -0
  69. package/dist/streams-view/timelinesViewModel.js +681 -0
  70. package/dist/streams-view/timelinesViewModel.js.map +1 -0
  71. package/dist/sync-agent/dms/models/dm.js +2 -2
  72. package/dist/sync-agent/dms/models/dm.js.map +1 -1
  73. package/dist/sync-agent/gdms/models/gdm.js +2 -2
  74. package/dist/sync-agent/gdms/models/gdm.js.map +1 -1
  75. package/dist/sync-agent/spaces/models/channel.js +3 -3
  76. package/dist/sync-agent/spaces/models/channel.js.map +1 -1
  77. package/dist/sync-agent/syncAgent.d.ts.map +1 -1
  78. package/dist/sync-agent/syncAgent.js +4 -0
  79. package/dist/sync-agent/syncAgent.js.map +1 -1
  80. package/dist/sync-agent/timeline/models/timeline-types.d.ts +2 -1
  81. package/dist/sync-agent/timeline/models/timeline-types.d.ts.map +1 -1
  82. package/dist/sync-agent/timeline/models/timeline-types.js +109 -0
  83. package/dist/sync-agent/timeline/models/timeline-types.js.map +1 -1
  84. package/dist/sync-agent/timeline/timeline.d.ts +9 -26
  85. package/dist/sync-agent/timeline/timeline.d.ts.map +1 -1
  86. package/dist/sync-agent/timeline/timeline.js +21 -226
  87. package/dist/sync-agent/timeline/timeline.js.map +1 -1
  88. package/dist/syncedStream.d.ts +2 -1
  89. package/dist/syncedStream.d.ts.map +1 -1
  90. package/dist/syncedStream.js +11 -12
  91. package/dist/syncedStream.js.map +1 -1
  92. package/dist/syncedStreams.d.ts +1 -0
  93. package/dist/syncedStreams.d.ts.map +1 -1
  94. package/dist/syncedStreams.js.map +1 -1
  95. package/dist/syncedStreamsLoop.d.ts +1 -0
  96. package/dist/syncedStreamsLoop.d.ts.map +1 -1
  97. package/dist/syncedStreamsLoop.js +6 -1
  98. package/dist/syncedStreamsLoop.js.map +1 -1
  99. package/dist/tags.d.ts.map +1 -1
  100. package/dist/tags.js +15 -16
  101. package/dist/tags.js.map +1 -1
  102. package/dist/tests/bob_testUtils.d.ts.map +1 -1
  103. package/dist/tests/bob_testUtils.js +4 -1
  104. package/dist/tests/bob_testUtils.js.map +1 -1
  105. package/dist/tests/multi/entitlements/channelsWithEntitlements.test.js +3 -3
  106. package/dist/tests/multi/entitlements/channelsWithEntitlements.test.js.map +1 -1
  107. package/dist/tests/multi/sync-agent/timeline.test.js +12 -7
  108. package/dist/tests/multi/sync-agent/timeline.test.js.map +1 -1
  109. package/dist/tests/multi/transactions_SpaceReview.test.js +11 -17
  110. package/dist/tests/multi/transactions_SpaceReview.test.js.map +1 -1
  111. package/dist/tests/multi/transactions_Tip.test.js +16 -20
  112. package/dist/tests/multi/transactions_Tip.test.js.map +1 -1
  113. package/dist/tests/multi_ne/channels.test.js +6 -8
  114. package/dist/tests/multi_ne/channels.test.js.map +1 -1
  115. package/dist/tests/multi_ne/client.test.js +45 -62
  116. package/dist/tests/multi_ne/client.test.js.map +1 -1
  117. package/dist/tests/multi_ne/clientDecryptionExtensions.test.js +6 -11
  118. package/dist/tests/multi_ne/clientDecryptionExtensions.test.js.map +1 -1
  119. package/dist/tests/multi_ne/deviceKeyMessage.test.js +6 -4
  120. package/dist/tests/multi_ne/deviceKeyMessage.test.js.map +1 -1
  121. package/dist/tests/multi_ne/gdms.test.js +6 -1
  122. package/dist/tests/multi_ne/gdms.test.js.map +1 -1
  123. package/dist/tests/multi_ne/media.test.js +1 -1
  124. package/dist/tests/multi_ne/media.test.js.map +1 -1
  125. package/dist/tests/multi_ne/space.test.js +1 -1
  126. package/dist/tests/multi_ne/space.test.js.map +1 -1
  127. package/dist/tests/multi_ne/streamRpcClient.test.js +4 -1
  128. package/dist/tests/multi_ne/streamRpcClient.test.js.map +1 -1
  129. package/dist/tests/multi_ne/streamRpcClientSync.test.js +8 -2
  130. package/dist/tests/multi_ne/streamRpcClientSync.test.js.map +1 -1
  131. package/dist/tests/multi_ne/syncWithBlocks.test.js +4 -1
  132. package/dist/tests/multi_ne/syncWithBlocks.test.js.map +1 -1
  133. package/dist/tests/multi_ne/syncedStream.test.js +39 -7
  134. package/dist/tests/multi_ne/syncedStream.test.js.map +1 -1
  135. package/dist/tests/multi_ne/syncedStreams.test.js +9 -365
  136. package/dist/tests/multi_ne/syncedStreams.test.js.map +1 -1
  137. package/dist/tests/multi_ne/tags.test.js +114 -85
  138. package/dist/tests/multi_ne/tags.test.js.map +1 -1
  139. package/dist/tests/multi_ne/userSettings.test.js +15 -15
  140. package/dist/tests/multi_ne/userSettings.test.js.map +1 -1
  141. package/dist/tests/syncAgent_testUtils.d.ts.map +1 -1
  142. package/dist/tests/syncAgent_testUtils.js +4 -0
  143. package/dist/tests/syncAgent_testUtils.js.map +1 -1
  144. package/dist/tests/testDriver_testUtils.d.ts.map +1 -1
  145. package/dist/tests/testDriver_testUtils.js +4 -7
  146. package/dist/tests/testDriver_testUtils.js.map +1 -1
  147. package/dist/tests/testUtils.d.ts +1 -0
  148. package/dist/tests/testUtils.d.ts.map +1 -1
  149. package/dist/tests/testUtils.js +13 -4
  150. package/dist/tests/testUtils.js.map +1 -1
  151. package/dist/tests/unit/crypto.test.js +6 -0
  152. package/dist/tests/unit/crypto.test.js.map +1 -1
  153. package/dist/tests/unit/crypto_utils.test.js +1 -1
  154. package/dist/tests/unit/crypto_utils.test.js.map +1 -1
  155. package/dist/tests/unit/helpers/ConversationBuilder.d.ts +39 -0
  156. package/dist/tests/unit/helpers/ConversationBuilder.d.ts.map +1 -0
  157. package/dist/tests/unit/helpers/ConversationBuilder.js +171 -0
  158. package/dist/tests/unit/helpers/ConversationBuilder.js.map +1 -0
  159. package/dist/tests/unit/timelineStoreInterface.test.d.ts +5 -0
  160. package/dist/tests/unit/timelineStoreInterface.test.d.ts.map +1 -0
  161. package/dist/tests/unit/timelineStoreInterface.test.js +343 -0
  162. package/dist/tests/unit/timelineStoreInterface.test.js.map +1 -0
  163. package/dist/unauthenticatedClient.d.ts +2 -1
  164. package/dist/unauthenticatedClient.d.ts.map +1 -1
  165. package/dist/unauthenticatedClient.js +2 -2
  166. package/dist/unauthenticatedClient.js.map +1 -1
  167. package/package.json +11 -10
  168. package/dist/crypto_utils.d.ts +0 -15
  169. package/dist/crypto_utils.d.ts.map +0 -1
  170. package/dist/crypto_utils.js +0 -99
  171. package/dist/crypto_utils.js.map +0 -1
  172. package/dist/sync-agent/timeline/models/pendingReplacedEvents.d.ts +0 -12
  173. package/dist/sync-agent/timeline/models/pendingReplacedEvents.d.ts.map +0 -1
  174. package/dist/sync-agent/timeline/models/pendingReplacedEvents.js +0 -20
  175. package/dist/sync-agent/timeline/models/pendingReplacedEvents.js.map +0 -1
  176. package/dist/sync-agent/timeline/models/reactions.d.ts +0 -13
  177. package/dist/sync-agent/timeline/models/reactions.d.ts.map +0 -1
  178. package/dist/sync-agent/timeline/models/reactions.js +0 -67
  179. package/dist/sync-agent/timeline/models/reactions.js.map +0 -1
  180. package/dist/sync-agent/timeline/models/replacedEvents.d.ts +0 -18
  181. package/dist/sync-agent/timeline/models/replacedEvents.d.ts.map +0 -1
  182. package/dist/sync-agent/timeline/models/replacedEvents.js +0 -19
  183. package/dist/sync-agent/timeline/models/replacedEvents.js.map +0 -1
  184. package/dist/sync-agent/timeline/models/threadStats.d.ts +0 -14
  185. package/dist/sync-agent/timeline/models/threadStats.d.ts.map +0 -1
  186. package/dist/sync-agent/timeline/models/threadStats.js +0 -99
  187. package/dist/sync-agent/timeline/models/threadStats.js.map +0 -1
  188. package/dist/sync-agent/timeline/models/threads.d.ts +0 -14
  189. package/dist/sync-agent/timeline/models/threads.d.ts.map +0 -1
  190. package/dist/sync-agent/timeline/models/threads.js +0 -57
  191. package/dist/sync-agent/timeline/models/threads.js.map +0 -1
  192. package/dist/sync-agent/timeline/models/timelineEvent.d.ts +0 -12
  193. package/dist/sync-agent/timeline/models/timelineEvent.d.ts.map +0 -1
  194. package/dist/sync-agent/timeline/models/timelineEvent.js.map +0 -1
  195. package/dist/sync-agent/timeline/models/timelineEvents.d.ts +0 -13
  196. package/dist/sync-agent/timeline/models/timelineEvents.d.ts.map +0 -1
  197. package/dist/sync-agent/timeline/models/timelineEvents.js +0 -37
  198. package/dist/sync-agent/timeline/models/timelineEvents.js.map +0 -1
@@ -0,0 +1,681 @@
1
+ import reverse from 'lodash/reverse';
2
+ import { isMessageTipEvent, RiverTimelineEvent, } from '../sync-agent/timeline/models/timeline-types';
3
+ import { dlogger } from '@towns-protocol/dlog';
4
+ import { getFallbackContent } from './timelineEvents';
5
+ const logger = dlogger('csb:timelineInterface');
6
+ export function makeTimelinesViewInterface(setState) {
7
+ const initializeStream = (userId, streamId) => {
8
+ setState((state) => _initializeStream(state, streamId));
9
+ };
10
+ const _initializeStream = (state, streamId) => {
11
+ const aggregated = {
12
+ threadStats: {},
13
+ threads: {},
14
+ reactions: {},
15
+ tips: {},
16
+ };
17
+ return {
18
+ timelines: { ...state.timelines, [streamId]: [] },
19
+ replacedEvents: state.replacedEvents,
20
+ pendingReplacedEvents: state.pendingReplacedEvents,
21
+ threadsStats: {
22
+ ...state.threadsStats,
23
+ [streamId]: aggregated.threadStats,
24
+ },
25
+ threads: {
26
+ ...state.threads,
27
+ [streamId]: aggregated.threads,
28
+ },
29
+ reactions: {
30
+ ...state.reactions,
31
+ [streamId]: aggregated.reactions,
32
+ },
33
+ tips: {
34
+ ...state.tips,
35
+ [streamId]: aggregated.tips,
36
+ },
37
+ lastestEventByUser: state.lastestEventByUser,
38
+ };
39
+ };
40
+ const reset = (streamIds) => {
41
+ setState((prev) => {
42
+ for (const streamId of streamIds) {
43
+ delete prev.timelines[streamId];
44
+ delete prev.replacedEvents[streamId];
45
+ delete prev.pendingReplacedEvents[streamId];
46
+ delete prev.threadsStats[streamId];
47
+ delete prev.threads[streamId];
48
+ delete prev.reactions[streamId];
49
+ delete prev.tips[streamId];
50
+ }
51
+ return prev;
52
+ });
53
+ };
54
+ const removeEvent = (state, streamId, eventId) => {
55
+ const eventIndex = state.timelines[streamId]?.findIndex((e) => e.eventId == eventId);
56
+ if ((eventIndex ?? -1) < 0) {
57
+ return state;
58
+ }
59
+ const event = state.timelines[streamId][eventIndex];
60
+ return {
61
+ timelines: removeTimelineEvent(streamId, eventIndex, state.timelines),
62
+ replacedEvents: state.replacedEvents,
63
+ pendingReplacedEvents: state.pendingReplacedEvents,
64
+ threadsStats: removeThreadStat(streamId, event, state.threadsStats),
65
+ threads: removeThreadEvent(streamId, event, state.threads),
66
+ reactions: removeReaction(streamId, event, state.reactions),
67
+ tips: removeTip(streamId, event, state.tips),
68
+ lastestEventByUser: state.lastestEventByUser,
69
+ };
70
+ };
71
+ const appendEvent = (state, userId, streamId, timelineEvent) => {
72
+ return {
73
+ timelines: appendTimelineEvent(streamId, timelineEvent, state.timelines),
74
+ replacedEvents: state.replacedEvents,
75
+ pendingReplacedEvents: state.pendingReplacedEvents,
76
+ threadsStats: addThreadStats(streamId, timelineEvent, state.threadsStats, state.timelines[streamId], userId),
77
+ threads: insertThreadEvent(streamId, timelineEvent, state.threads),
78
+ reactions: addReactions(streamId, timelineEvent, state.reactions),
79
+ tips: addTips(streamId, timelineEvent, state.tips, 'append'),
80
+ lastestEventByUser: state.lastestEventByUser,
81
+ };
82
+ };
83
+ const prependEvent = (state, userId, streamId, inTimelineEvent) => {
84
+ const timelineEvent = state.pendingReplacedEvents[streamId]?.[inTimelineEvent.eventId]
85
+ ? toReplacedMessageEvent(inTimelineEvent, state.pendingReplacedEvents[streamId][inTimelineEvent.eventId])
86
+ : inTimelineEvent;
87
+ return {
88
+ timelines: prependTimelineEvent(streamId, timelineEvent, state.timelines),
89
+ replacedEvents: state.replacedEvents,
90
+ pendingReplacedEvents: state.pendingReplacedEvents,
91
+ threadsStats: addThreadStats(streamId, timelineEvent, state.threadsStats, state.timelines[streamId], userId),
92
+ threads: insertThreadEvent(streamId, timelineEvent, state.threads),
93
+ reactions: addReactions(streamId, timelineEvent, state.reactions),
94
+ tips: addTips(streamId, timelineEvent, state.tips, 'prepend'),
95
+ lastestEventByUser: state.lastestEventByUser,
96
+ };
97
+ };
98
+ const replaceEvent = (state, userId, streamId, replacedMsgId, timelineEvent) => {
99
+ const timeline = state.timelines[streamId] ?? [];
100
+ const eventIndex = timeline.findIndex((e) => e.eventId === replacedMsgId ||
101
+ (e.localEventId && e.localEventId === timelineEvent.localEventId));
102
+ if (eventIndex === -1) {
103
+ // if we didn't find an event to replace...
104
+ if (state.pendingReplacedEvents[streamId]?.[replacedMsgId] &&
105
+ state.pendingReplacedEvents[streamId][replacedMsgId].latestEventNum >
106
+ timelineEvent.latestEventNum) {
107
+ // if we already have a replacement here, leave it, because we sync backwards, we assume the first one is the correct one
108
+ return state;
109
+ }
110
+ else {
111
+ // otherwise add it to the pending list
112
+ return {
113
+ ...state,
114
+ pendingReplacedEvents: {
115
+ ...state.pendingReplacedEvents,
116
+ [streamId]: {
117
+ ...state.pendingReplacedEvents[streamId],
118
+ [replacedMsgId]: timelineEvent,
119
+ },
120
+ },
121
+ };
122
+ }
123
+ }
124
+ const oldEvent = timeline[eventIndex];
125
+ if (timelineEvent.latestEventNum < oldEvent.latestEventNum) {
126
+ return state;
127
+ }
128
+ const newEvent = toReplacedMessageEvent(oldEvent, timelineEvent);
129
+ const threadParentId = newEvent.threadParentId;
130
+ const threadTimeline = threadParentId
131
+ ? state.threads[streamId]?.[threadParentId]
132
+ : undefined;
133
+ const threadEventIndex = threadTimeline?.findIndex((e) => e.eventId === replacedMsgId ||
134
+ (e.localEventId && e.localEventId === timelineEvent.localEventId)) ?? -1;
135
+ return {
136
+ timelines: replaceTimelineEvent(streamId, newEvent, eventIndex, timeline, state.timelines),
137
+ replacedEvents: {
138
+ ...state.replacedEvents,
139
+ [streamId]: [...(state.replacedEvents[streamId] ?? []), { oldEvent, newEvent }],
140
+ },
141
+ pendingReplacedEvents: state.pendingReplacedEvents,
142
+ threadsStats: addThreadStats(streamId, newEvent, removeThreadStat(streamId, oldEvent, state.threadsStats), state.timelines[streamId], userId),
143
+ threads: threadParentId && threadTimeline && threadEventIndex >= 0
144
+ ? {
145
+ ...state.threads,
146
+ [streamId]: replaceTimelineEvent(threadParentId, newEvent, threadEventIndex, threadTimeline, state.threads[streamId]),
147
+ }
148
+ : threadParentId
149
+ ? insertThreadEvent(streamId, newEvent, state.threads)
150
+ : state.threads,
151
+ reactions: addReactions(streamId, newEvent, removeReaction(streamId, oldEvent, state.reactions)),
152
+ tips: addTips(streamId, newEvent, removeTip(streamId, oldEvent, state.tips), 'append'), // not sure one will ever replace a tip
153
+ lastestEventByUser: state.lastestEventByUser,
154
+ };
155
+ };
156
+ function confirmEvent(state, streamId, confirmation) {
157
+ // very very similar to replaceEvent, but we only swap out the confirmedInBlockNum and confirmedEventNum
158
+ const timeline = state.timelines[streamId] ?? [];
159
+ const eventIndex = timeline.findIndex((e) => e.eventId === confirmation.eventId);
160
+ if (eventIndex === -1) {
161
+ return state;
162
+ }
163
+ const oldEvent = timeline[eventIndex];
164
+ const newEvent = {
165
+ ...oldEvent,
166
+ confirmedEventNum: confirmation.confirmedEventNum,
167
+ confirmedInBlockNum: confirmation.confirmedInBlockNum,
168
+ };
169
+ const threadParentId = newEvent.threadParentId;
170
+ const threadTimeline = threadParentId
171
+ ? state.threads[streamId]?.[threadParentId]
172
+ : undefined;
173
+ const threadEventIndex = threadTimeline?.findIndex((e) => e.eventId === confirmation.eventId) ?? -1;
174
+ return {
175
+ timelines: replaceTimelineEvent(streamId, newEvent, eventIndex, timeline, state.timelines),
176
+ replacedEvents: {
177
+ ...state.replacedEvents,
178
+ [streamId]: [...(state.replacedEvents[streamId] ?? []), { oldEvent, newEvent }],
179
+ },
180
+ pendingReplacedEvents: state.pendingReplacedEvents,
181
+ threadsStats: state.threadsStats,
182
+ threads: threadParentId && threadTimeline && threadEventIndex >= 0
183
+ ? {
184
+ ...state.threads,
185
+ [streamId]: replaceTimelineEvent(threadParentId, newEvent, threadEventIndex, threadTimeline, state.threads[streamId]),
186
+ }
187
+ : state.threads,
188
+ reactions: state.reactions,
189
+ tips: state.tips,
190
+ lastestEventByUser: state.lastestEventByUser,
191
+ };
192
+ }
193
+ function processEvent(state, event, userId, streamId, updatingEventId) {
194
+ const editsEventId = getEditsId(event.content);
195
+ const redactsEventId = getRedactsId(event.content);
196
+ if (redactsEventId) {
197
+ const redactedEvent = makeRedactionEvent(event);
198
+ state = replaceEvent(state, userId, streamId, redactsEventId, redactedEvent);
199
+ if (updatingEventId) {
200
+ // replace the formerly encrypted event
201
+ state = replaceEvent(state, userId, streamId, updatingEventId, event);
202
+ }
203
+ else {
204
+ state = appendEvent(state, userId, streamId, event);
205
+ }
206
+ }
207
+ else if (editsEventId) {
208
+ if (updatingEventId) {
209
+ // remove the formerly encrypted event
210
+ state = removeEvent(state, streamId, updatingEventId);
211
+ }
212
+ state = replaceEvent(state, userId, streamId, editsEventId, event);
213
+ }
214
+ else {
215
+ if (updatingEventId) {
216
+ // replace the formerly encrypted event
217
+ state = replaceEvent(state, userId, streamId, updatingEventId, event);
218
+ }
219
+ else {
220
+ state = appendEvent(state, userId, streamId, event);
221
+ }
222
+ }
223
+ const prevLatestEvent = state.lastestEventByUser[event.sender.id];
224
+ if ((prevLatestEvent?.createdAtEpochMs ?? 0) < event.createdAtEpochMs) {
225
+ state = {
226
+ ...state,
227
+ lastestEventByUser: {
228
+ ...state.lastestEventByUser,
229
+ [event.sender.id]: event,
230
+ },
231
+ };
232
+ }
233
+ return state;
234
+ }
235
+ function appendEvents(events, userId, streamId, specialFunction) {
236
+ setState((state) => {
237
+ if (specialFunction === 'initializeStream') {
238
+ state = _initializeStream(state, streamId);
239
+ }
240
+ for (const event of events) {
241
+ state = processEvent(state, event, userId, streamId, undefined);
242
+ }
243
+ return state;
244
+ });
245
+ }
246
+ function prependEvents(events, userId, streamId) {
247
+ setState((state) => {
248
+ for (const event of reverse(events)) {
249
+ const editsEventId = getEditsId(event.content);
250
+ const redactsEventId = getRedactsId(event.content);
251
+ if (redactsEventId) {
252
+ const redactedEvent = makeRedactionEvent(event);
253
+ state = prependEvent(state, userId, streamId, event);
254
+ state = replaceEvent(state, userId, streamId, redactsEventId, redactedEvent);
255
+ }
256
+ else if (editsEventId) {
257
+ state = replaceEvent(state, userId, streamId, editsEventId, event);
258
+ }
259
+ else {
260
+ state = prependEvent(state, userId, streamId, event);
261
+ }
262
+ }
263
+ return state;
264
+ });
265
+ }
266
+ function updateEvents(events, userId, streamId) {
267
+ setState((state) => {
268
+ for (const event of events) {
269
+ state = processEvent(state, event, userId, streamId, event.eventId);
270
+ }
271
+ return state;
272
+ });
273
+ }
274
+ function updateEvent(event, userId, streamId, replacingEventId) {
275
+ setState((state) => {
276
+ return processEvent(state, event, userId, streamId, replacingEventId);
277
+ });
278
+ }
279
+ function confirmEvents(confirmations, streamId) {
280
+ setState((state) => {
281
+ confirmations.forEach((confirmation) => {
282
+ state = confirmEvent(state, streamId, confirmation);
283
+ });
284
+ return state;
285
+ });
286
+ }
287
+ return {
288
+ initializeStream,
289
+ reset,
290
+ appendEvents,
291
+ prependEvents,
292
+ updateEvents,
293
+ updateEvent,
294
+ confirmEvents,
295
+ };
296
+ }
297
+ function canReplaceEvent(prev, next) {
298
+ if (next.content?.kind === RiverTimelineEvent.RedactedEvent && next.content.isAdminRedaction) {
299
+ return true;
300
+ }
301
+ if (next.sender.id === prev.sender.id) {
302
+ return true;
303
+ }
304
+ logger.info('cannot replace event', { prev, next });
305
+ return false;
306
+ }
307
+ function toReplacedMessageEvent(prev, next) {
308
+ const isLocalId = prev.eventId.startsWith('~');
309
+ if (!canReplaceEvent(prev, next)) {
310
+ return prev;
311
+ }
312
+ else if (next.content?.kind === RiverTimelineEvent.ChannelMessage &&
313
+ prev.content?.kind === RiverTimelineEvent.ChannelMessage) {
314
+ // when we replace an event, we copy the content up to the root event
315
+ // so we keep the prev id, but use the next content
316
+ const eventId = !isLocalId ? prev.eventId : next.eventId;
317
+ return {
318
+ ...next,
319
+ eventId: eventId,
320
+ eventNum: prev.eventNum,
321
+ latestEventId: next.eventId,
322
+ latestEventNum: next.eventNum,
323
+ confirmedEventNum: prev.confirmedEventNum ?? next.confirmedEventNum,
324
+ confirmedInBlockNum: prev.confirmedInBlockNum ?? next.confirmedInBlockNum,
325
+ createdAtEpochMs: prev.createdAtEpochMs,
326
+ updatedAtEpochMs: next.createdAtEpochMs,
327
+ content: {
328
+ ...next.content,
329
+ threadId: prev.content.threadId,
330
+ },
331
+ threadParentId: prev.threadParentId,
332
+ reactionParentId: prev.reactionParentId,
333
+ sender: prev.sender,
334
+ };
335
+ }
336
+ else if (next.content?.kind === RiverTimelineEvent.RedactedEvent) {
337
+ // for redacted events, carry over previous pointers to content
338
+ // we don't want to lose thread info
339
+ return {
340
+ ...next,
341
+ eventId: prev.eventId,
342
+ eventNum: prev.eventNum,
343
+ latestEventId: next.eventId,
344
+ latestEventNum: next.eventNum,
345
+ confirmedEventNum: prev.confirmedEventNum ?? next.confirmedEventNum,
346
+ confirmedInBlockNum: prev.confirmedInBlockNum ?? next.confirmedInBlockNum,
347
+ createdAtEpochMs: prev.createdAtEpochMs,
348
+ updatedAtEpochMs: next.createdAtEpochMs,
349
+ threadParentId: prev.threadParentId,
350
+ reactionParentId: prev.reactionParentId,
351
+ };
352
+ }
353
+ else if (prev.content?.kind === RiverTimelineEvent.RedactedEvent) {
354
+ // replacing a redacted event should maintain the redacted state
355
+ return {
356
+ ...prev,
357
+ latestEventId: next.eventId,
358
+ latestEventNum: next.eventNum,
359
+ confirmedEventNum: prev.confirmedEventNum ?? next.confirmedEventNum,
360
+ confirmedInBlockNum: prev.confirmedInBlockNum ?? next.confirmedInBlockNum,
361
+ };
362
+ }
363
+ else {
364
+ // make sure we carry the createdAtEpochMs of the previous event
365
+ // so we don't end up with a timeline that has events out of order.
366
+ const eventId = isLocalId ? next.eventId : prev.eventId;
367
+ return {
368
+ ...next,
369
+ eventId: eventId,
370
+ eventNum: prev.eventNum,
371
+ latestEventId: next.eventId,
372
+ latestEventNum: next.eventNum,
373
+ confirmedEventNum: prev.confirmedEventNum ?? next.confirmedEventNum,
374
+ confirmedInBlockNum: prev.confirmedInBlockNum ?? next.confirmedInBlockNum,
375
+ createdAtEpochMs: prev.createdAtEpochMs,
376
+ updatedAtEpochMs: next.createdAtEpochMs,
377
+ };
378
+ }
379
+ }
380
+ function makeRedactionEvent(redactionAction) {
381
+ if (redactionAction.content?.kind !== RiverTimelineEvent.RedactionActionEvent) {
382
+ throw new Error('makeRedactionEvent called with non-redaction action event');
383
+ }
384
+ const newContent = {
385
+ kind: RiverTimelineEvent.RedactedEvent,
386
+ isAdminRedaction: redactionAction.content.adminRedaction,
387
+ };
388
+ return {
389
+ ...redactionAction,
390
+ content: newContent,
391
+ fallbackContent: getFallbackContent('', newContent),
392
+ isRedacted: true,
393
+ };
394
+ }
395
+ function addThreadStats(streamId, timelineEvent, threadsStats, timeline, userId) {
396
+ const parentId = timelineEvent.threadParentId;
397
+ // if we have a parent...
398
+ if (parentId) {
399
+ return {
400
+ ...threadsStats,
401
+ [streamId]: {
402
+ ...threadsStats[streamId],
403
+ [parentId]: addThreadStat(timelineEvent, parentId, threadsStats[streamId]?.[parentId], timeline, userId),
404
+ },
405
+ };
406
+ }
407
+ // if we are a parent...
408
+ if (threadsStats[streamId]?.[timelineEvent.eventId]) {
409
+ // update ourself in the map
410
+ return {
411
+ ...threadsStats,
412
+ [streamId]: {
413
+ ...threadsStats[streamId],
414
+ [timelineEvent.eventId]: {
415
+ ...threadsStats[streamId][timelineEvent.eventId],
416
+ parentEvent: timelineEvent,
417
+ parentMessageContent: getChannelMessageContent(timelineEvent),
418
+ isParticipating: threadsStats[streamId][timelineEvent.eventId].isParticipating ||
419
+ (timelineEvent.content?.kind !== RiverTimelineEvent.RedactedEvent &&
420
+ threadsStats[streamId][timelineEvent.eventId].replyEventIds.size > 0 &&
421
+ (timelineEvent.sender.id === userId || timelineEvent.isMentioned)),
422
+ },
423
+ },
424
+ };
425
+ }
426
+ // otherwise noop
427
+ return threadsStats;
428
+ }
429
+ function makeNewThreadStats(event, parentId, timeline) {
430
+ const parent = timeline?.find((t) => t.eventId === parentId); // one time lookup of the parent message for the first reply
431
+ return {
432
+ replyEventIds: new Set(),
433
+ userIds: new Set(),
434
+ latestTs: event.createdAtEpochMs,
435
+ parentId,
436
+ parentEvent: parent,
437
+ parentMessageContent: getChannelMessageContent(parent),
438
+ isParticipating: false,
439
+ };
440
+ }
441
+ function addThreadStat(event, parentId, entry, timeline, userId) {
442
+ const updated = entry ? { ...entry } : makeNewThreadStats(event, parentId, timeline);
443
+ if (event.content?.kind === RiverTimelineEvent.RedactedEvent) {
444
+ return updated;
445
+ }
446
+ updated.replyEventIds.add(event.eventId);
447
+ updated.latestTs = Math.max(updated.latestTs, event.createdAtEpochMs);
448
+ const senderId = getMessageSenderId(event);
449
+ if (senderId) {
450
+ updated.userIds.add(senderId);
451
+ }
452
+ updated.isParticipating =
453
+ updated.isParticipating ||
454
+ updated.userIds.has(userId) ||
455
+ updated.parentEvent?.sender.id === userId ||
456
+ event.isMentioned;
457
+ return updated;
458
+ }
459
+ function removeThreadStat(streamId, timelineEvent, threadsStats) {
460
+ const parentId = timelineEvent.threadParentId;
461
+ if (!parentId) {
462
+ return threadsStats;
463
+ }
464
+ if (!threadsStats[streamId]?.[parentId]) {
465
+ return threadsStats;
466
+ }
467
+ const updated = { ...threadsStats[streamId] };
468
+ const entry = updated[parentId];
469
+ if (entry) {
470
+ entry.replyEventIds.delete(timelineEvent.eventId);
471
+ if (entry.replyEventIds.size === 0) {
472
+ delete updated[parentId];
473
+ }
474
+ else {
475
+ const senderId = getMessageSenderId(timelineEvent);
476
+ if (senderId) {
477
+ entry.userIds.delete(senderId);
478
+ }
479
+ }
480
+ }
481
+ return { ...threadsStats, [streamId]: updated };
482
+ }
483
+ function addTips(streamId, event, tips, direction) {
484
+ if (!isMessageTipEvent(event)) {
485
+ return tips;
486
+ }
487
+ // note to future self, if anyone starts uploading the same transaction multiple times,
488
+ // store the tips in a Record keyed by transactionHash instead of eventId
489
+ return {
490
+ ...tips,
491
+ [streamId]: addTip(event, tips[streamId], direction),
492
+ };
493
+ }
494
+ function addTip(event, tips, direction) {
495
+ const refEventId = event.content.refEventId;
496
+ if (!tips) {
497
+ return {
498
+ [refEventId]: [event],
499
+ };
500
+ }
501
+ if (direction === 'append') {
502
+ return {
503
+ ...tips,
504
+ [refEventId]: [...(tips[refEventId] ?? []), event],
505
+ };
506
+ }
507
+ else {
508
+ return {
509
+ ...tips,
510
+ [refEventId]: [event, ...(tips[refEventId] ?? [])],
511
+ };
512
+ }
513
+ }
514
+ function removeTip(streamId, event, tips) {
515
+ if (!isMessageTipEvent(event)) {
516
+ return tips;
517
+ }
518
+ const refEventId = event.content.refEventId;
519
+ if (!tips[streamId]?.[refEventId]) {
520
+ return tips;
521
+ }
522
+ return {
523
+ ...tips,
524
+ [streamId]: {
525
+ ...tips[streamId],
526
+ [refEventId]: tips[streamId][refEventId].filter((t) => t.eventId !== event.eventId),
527
+ },
528
+ };
529
+ }
530
+ function addReactions(streamId, event, reactions) {
531
+ const parentId = event.reactionParentId;
532
+ if (!parentId) {
533
+ return reactions;
534
+ }
535
+ return {
536
+ ...reactions,
537
+ [streamId]: {
538
+ ...reactions[streamId],
539
+ [parentId]: addReaction(event, reactions[streamId]?.[parentId]),
540
+ },
541
+ };
542
+ }
543
+ function addReaction(event, entry) {
544
+ const content = event.content?.kind === RiverTimelineEvent.Reaction ? event.content : undefined;
545
+ if (!content) {
546
+ return entry ?? {};
547
+ }
548
+ const reactionName = content.reaction;
549
+ const senderId = event.sender.id;
550
+ return {
551
+ ...entry,
552
+ [reactionName]: {
553
+ ...entry?.[reactionName],
554
+ [senderId]: { eventId: event.eventId },
555
+ },
556
+ };
557
+ }
558
+ function removeReaction(streamId, event, reactions) {
559
+ const parentId = event.reactionParentId;
560
+ if (!parentId) {
561
+ return reactions;
562
+ }
563
+ if (!reactions[streamId]?.[parentId]) {
564
+ return reactions;
565
+ }
566
+ const content = event.content?.kind === RiverTimelineEvent.Reaction ? event.content : undefined;
567
+ if (!content) {
568
+ return reactions;
569
+ }
570
+ const reactionName = content.reaction;
571
+ const senderId = event.sender.id;
572
+ const updated = { ...reactions[streamId] };
573
+ const entry = updated[parentId];
574
+ if (entry) {
575
+ const reactions = entry[reactionName];
576
+ if (reactions) {
577
+ delete reactions[senderId];
578
+ }
579
+ if (Object.keys(reactions).length === 0) {
580
+ delete entry[reactionName];
581
+ }
582
+ }
583
+ return { ...reactions, [streamId]: updated };
584
+ }
585
+ function removeThreadEvent(streamId, event, threads) {
586
+ const parentId = event.threadParentId;
587
+ if (!parentId) {
588
+ return threads;
589
+ }
590
+ const threadEventIndex = threads[streamId]?.[parentId]?.findIndex((e) => e.eventId === event.eventId) ?? -1;
591
+ if (threadEventIndex === -1) {
592
+ return threads;
593
+ }
594
+ return {
595
+ ...threads,
596
+ [streamId]: removeTimelineEvent(parentId, threadEventIndex, threads[streamId]),
597
+ };
598
+ }
599
+ function insertThreadEvent(streamId, timelineEvent, threads) {
600
+ if (!timelineEvent.threadParentId) {
601
+ return threads;
602
+ }
603
+ return {
604
+ ...threads,
605
+ [streamId]: insertTimelineEvent(timelineEvent.threadParentId, timelineEvent, threads[streamId] ?? {}),
606
+ };
607
+ }
608
+ function removeTimelineEvent(streamId, eventIndex, timelines) {
609
+ return {
610
+ ...timelines,
611
+ [streamId]: [
612
+ ...timelines[streamId].slice(0, eventIndex),
613
+ ...timelines[streamId].slice(eventIndex + 1),
614
+ ],
615
+ };
616
+ }
617
+ function insertTimelineEvent(streamId, timelineEvent, timelines) {
618
+ // thread items are decrypted in an unpredictable order, so we need to insert them in the correct order
619
+ return {
620
+ ...timelines,
621
+ [streamId]: [timelineEvent, ...(timelines[streamId] ?? [])].sort((a, b) => a.eventNum > b.eventNum ? 1 : -1),
622
+ };
623
+ }
624
+ function appendTimelineEvent(streamId, timelineEvent, timelines) {
625
+ return {
626
+ ...timelines,
627
+ [streamId]: [...(timelines[streamId] ?? []), timelineEvent],
628
+ };
629
+ }
630
+ function prependTimelineEvent(streamId, timelineEvent, timelines) {
631
+ return {
632
+ ...timelines,
633
+ [streamId]: [timelineEvent, ...(timelines[streamId] ?? [])],
634
+ };
635
+ }
636
+ function replaceTimelineEvent(streamId, newEvent, eventIndex, timeline, timelines) {
637
+ return {
638
+ ...timelines,
639
+ [streamId]: [...timeline.slice(0, eventIndex), newEvent, ...timeline.slice(eventIndex + 1)],
640
+ };
641
+ }
642
+ function getChannelMessageContent(event) {
643
+ return event?.content?.kind === RiverTimelineEvent.ChannelMessage ? event.content : undefined;
644
+ }
645
+ function getMessageSenderId(event) {
646
+ if (event.content?.kind === RiverTimelineEvent.ChannelMessage ||
647
+ event.content?.kind === RiverTimelineEvent.TokenTransfer) {
648
+ return event.sender.id;
649
+ }
650
+ return undefined;
651
+ }
652
+ export function getEditsId(content) {
653
+ return content?.kind === RiverTimelineEvent.ChannelMessage ? content.editsEventId : undefined;
654
+ }
655
+ export function getRedactsId(content) {
656
+ return content?.kind === RiverTimelineEvent.RedactionActionEvent
657
+ ? content.refEventId
658
+ : undefined;
659
+ }
660
+ export function getThreadParentId(content) {
661
+ return content?.kind === RiverTimelineEvent.ChannelMessage
662
+ ? content.threadId
663
+ : content?.kind === RiverTimelineEvent.TokenTransfer
664
+ ? content.threadParentId
665
+ : undefined;
666
+ }
667
+ export function getReplyParentId(content) {
668
+ return content?.kind === RiverTimelineEvent.ChannelMessage ? content.replyId : undefined;
669
+ }
670
+ export function getReactionParentId(content) {
671
+ return content?.kind === RiverTimelineEvent.Reaction ? content.targetEventId : undefined;
672
+ }
673
+ export function getIsMentioned(content, userId) {
674
+ //TODO: comparison below should be changed as soon as this HNT-1576 will be resolved
675
+ return content?.kind === RiverTimelineEvent.ChannelMessage
676
+ ? content.mentions.findIndex((x) => (x.userId ?? '')
677
+ .toLowerCase()
678
+ .localeCompare(userId.toLowerCase(), undefined, { sensitivity: 'base' }) == 0) >= 0
679
+ : false;
680
+ }
681
+ //# sourceMappingURL=timelinesViewModel.js.map