@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.
- package/dist/client.d.ts +5 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +64 -18
- package/dist/client.js.map +1 -1
- package/dist/id.d.ts +3 -1
- package/dist/id.d.ts.map +1 -1
- package/dist/id.js +3 -0
- package/dist/id.js.map +1 -1
- package/dist/index.d.ts +4 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -8
- package/dist/index.js.map +1 -1
- package/dist/makeAuthenticationRpcClient.d.ts +1 -1
- package/dist/makeAuthenticationRpcClient.d.ts.map +1 -1
- package/dist/makeAuthenticationRpcClient.js +2 -2
- package/dist/makeAuthenticationRpcClient.js.map +1 -1
- package/dist/makeNotificationRpcClient.d.ts +1 -1
- package/dist/makeNotificationRpcClient.d.ts.map +1 -1
- package/dist/makeNotificationRpcClient.js +2 -2
- package/dist/makeNotificationRpcClient.js.map +1 -1
- package/dist/makeStreamRpcClient.d.ts +1 -1
- package/dist/makeStreamRpcClient.d.ts.map +1 -1
- package/dist/makeStreamRpcClient.js +2 -2
- package/dist/makeStreamRpcClient.js.map +1 -1
- package/dist/notificationService.d.ts.map +1 -1
- package/dist/notificationService.js +2 -1
- package/dist/notificationService.js.map +1 -1
- package/dist/riverConfig.js +1 -1
- package/dist/riverConfig.js.map +1 -1
- package/dist/rpcCommon.d.ts +1 -3
- package/dist/rpcCommon.d.ts.map +1 -1
- package/dist/rpcCommon.js +1 -13
- package/dist/rpcCommon.js.map +1 -1
- package/dist/sign.d.ts +1 -1
- package/dist/sign.d.ts.map +1 -1
- package/dist/sign.js +13 -7
- package/dist/sign.js.map +1 -1
- package/dist/stream.d.ts +4 -2
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +9 -6
- package/dist/stream.js.map +1 -1
- package/dist/streamEvents.d.ts +3 -3
- package/dist/streamEvents.d.ts.map +1 -1
- package/dist/streamStateView.d.ts +9 -6
- package/dist/streamStateView.d.ts.map +1 -1
- package/dist/streamStateView.js +58 -62
- package/dist/streamStateView.js.map +1 -1
- package/dist/streamStateView_Space.js +1 -1
- package/dist/streamStateView_Space.js.map +1 -1
- package/dist/streamStateView_UserMetadata.js +1 -1
- package/dist/streamStateView_UserMetadata.js.map +1 -1
- package/dist/streamUtils.d.ts.map +1 -1
- package/dist/streamUtils.js +2 -0
- package/dist/streamUtils.js.map +1 -1
- package/dist/streams-view/streamsView.d.ts +18 -0
- package/dist/streams-view/streamsView.d.ts.map +1 -0
- package/dist/streams-view/streamsView.js +25 -0
- package/dist/streams-view/streamsView.js.map +1 -0
- package/dist/streams-view/timelineEvents.d.ts +11 -0
- package/dist/streams-view/timelineEvents.d.ts.map +1 -0
- package/dist/{sync-agent/timeline/models/timelineEvent.js → streams-view/timelineEvents.js} +240 -303
- package/dist/streams-view/timelineEvents.js.map +1 -0
- package/dist/streams-view/timelinesView.d.ts +27 -0
- package/dist/streams-view/timelinesView.d.ts.map +1 -0
- package/dist/streams-view/timelinesView.js +109 -0
- package/dist/streams-view/timelinesView.js.map +1 -0
- package/dist/streams-view/timelinesViewModel.d.ts +38 -0
- package/dist/streams-view/timelinesViewModel.d.ts.map +1 -0
- package/dist/streams-view/timelinesViewModel.js +681 -0
- package/dist/streams-view/timelinesViewModel.js.map +1 -0
- package/dist/sync-agent/dms/models/dm.js +2 -2
- package/dist/sync-agent/dms/models/dm.js.map +1 -1
- package/dist/sync-agent/gdms/models/gdm.js +2 -2
- package/dist/sync-agent/gdms/models/gdm.js.map +1 -1
- package/dist/sync-agent/spaces/models/channel.js +3 -3
- package/dist/sync-agent/spaces/models/channel.js.map +1 -1
- package/dist/sync-agent/syncAgent.d.ts.map +1 -1
- package/dist/sync-agent/syncAgent.js +4 -0
- package/dist/sync-agent/syncAgent.js.map +1 -1
- package/dist/sync-agent/timeline/models/timeline-types.d.ts +2 -1
- package/dist/sync-agent/timeline/models/timeline-types.d.ts.map +1 -1
- package/dist/sync-agent/timeline/models/timeline-types.js +109 -0
- package/dist/sync-agent/timeline/models/timeline-types.js.map +1 -1
- package/dist/sync-agent/timeline/timeline.d.ts +9 -26
- package/dist/sync-agent/timeline/timeline.d.ts.map +1 -1
- package/dist/sync-agent/timeline/timeline.js +21 -226
- package/dist/sync-agent/timeline/timeline.js.map +1 -1
- package/dist/syncedStream.d.ts +2 -1
- package/dist/syncedStream.d.ts.map +1 -1
- package/dist/syncedStream.js +11 -12
- package/dist/syncedStream.js.map +1 -1
- package/dist/syncedStreams.d.ts +1 -0
- package/dist/syncedStreams.d.ts.map +1 -1
- package/dist/syncedStreams.js.map +1 -1
- package/dist/syncedStreamsLoop.d.ts +1 -0
- package/dist/syncedStreamsLoop.d.ts.map +1 -1
- package/dist/syncedStreamsLoop.js +6 -1
- package/dist/syncedStreamsLoop.js.map +1 -1
- package/dist/tags.d.ts.map +1 -1
- package/dist/tags.js +15 -16
- package/dist/tags.js.map +1 -1
- package/dist/tests/bob_testUtils.d.ts.map +1 -1
- package/dist/tests/bob_testUtils.js +4 -1
- package/dist/tests/bob_testUtils.js.map +1 -1
- package/dist/tests/multi/entitlements/channelsWithEntitlements.test.js +3 -3
- package/dist/tests/multi/entitlements/channelsWithEntitlements.test.js.map +1 -1
- package/dist/tests/multi/sync-agent/timeline.test.js +12 -7
- package/dist/tests/multi/sync-agent/timeline.test.js.map +1 -1
- package/dist/tests/multi/transactions_SpaceReview.test.js +11 -17
- package/dist/tests/multi/transactions_SpaceReview.test.js.map +1 -1
- package/dist/tests/multi/transactions_Tip.test.js +16 -20
- package/dist/tests/multi/transactions_Tip.test.js.map +1 -1
- package/dist/tests/multi_ne/channels.test.js +6 -8
- package/dist/tests/multi_ne/channels.test.js.map +1 -1
- package/dist/tests/multi_ne/client.test.js +45 -62
- package/dist/tests/multi_ne/client.test.js.map +1 -1
- package/dist/tests/multi_ne/clientDecryptionExtensions.test.js +6 -11
- package/dist/tests/multi_ne/clientDecryptionExtensions.test.js.map +1 -1
- package/dist/tests/multi_ne/deviceKeyMessage.test.js +6 -4
- package/dist/tests/multi_ne/deviceKeyMessage.test.js.map +1 -1
- package/dist/tests/multi_ne/gdms.test.js +6 -1
- package/dist/tests/multi_ne/gdms.test.js.map +1 -1
- package/dist/tests/multi_ne/media.test.js +1 -1
- package/dist/tests/multi_ne/media.test.js.map +1 -1
- package/dist/tests/multi_ne/space.test.js +1 -1
- package/dist/tests/multi_ne/space.test.js.map +1 -1
- package/dist/tests/multi_ne/streamRpcClient.test.js +4 -1
- package/dist/tests/multi_ne/streamRpcClient.test.js.map +1 -1
- package/dist/tests/multi_ne/streamRpcClientSync.test.js +8 -2
- package/dist/tests/multi_ne/streamRpcClientSync.test.js.map +1 -1
- package/dist/tests/multi_ne/syncWithBlocks.test.js +4 -1
- package/dist/tests/multi_ne/syncWithBlocks.test.js.map +1 -1
- package/dist/tests/multi_ne/syncedStream.test.js +39 -7
- package/dist/tests/multi_ne/syncedStream.test.js.map +1 -1
- package/dist/tests/multi_ne/syncedStreams.test.js +9 -365
- package/dist/tests/multi_ne/syncedStreams.test.js.map +1 -1
- package/dist/tests/multi_ne/tags.test.js +114 -85
- package/dist/tests/multi_ne/tags.test.js.map +1 -1
- package/dist/tests/multi_ne/userSettings.test.js +15 -15
- package/dist/tests/multi_ne/userSettings.test.js.map +1 -1
- package/dist/tests/syncAgent_testUtils.d.ts.map +1 -1
- package/dist/tests/syncAgent_testUtils.js +4 -0
- package/dist/tests/syncAgent_testUtils.js.map +1 -1
- package/dist/tests/testDriver_testUtils.d.ts.map +1 -1
- package/dist/tests/testDriver_testUtils.js +4 -7
- package/dist/tests/testDriver_testUtils.js.map +1 -1
- package/dist/tests/testUtils.d.ts +1 -0
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +13 -4
- package/dist/tests/testUtils.js.map +1 -1
- package/dist/tests/unit/crypto.test.js +6 -0
- package/dist/tests/unit/crypto.test.js.map +1 -1
- package/dist/tests/unit/crypto_utils.test.js +1 -1
- package/dist/tests/unit/crypto_utils.test.js.map +1 -1
- package/dist/tests/unit/helpers/ConversationBuilder.d.ts +39 -0
- package/dist/tests/unit/helpers/ConversationBuilder.d.ts.map +1 -0
- package/dist/tests/unit/helpers/ConversationBuilder.js +171 -0
- package/dist/tests/unit/helpers/ConversationBuilder.js.map +1 -0
- package/dist/tests/unit/timelineStoreInterface.test.d.ts +5 -0
- package/dist/tests/unit/timelineStoreInterface.test.d.ts.map +1 -0
- package/dist/tests/unit/timelineStoreInterface.test.js +343 -0
- package/dist/tests/unit/timelineStoreInterface.test.js.map +1 -0
- package/dist/unauthenticatedClient.d.ts +2 -1
- package/dist/unauthenticatedClient.d.ts.map +1 -1
- package/dist/unauthenticatedClient.js +2 -2
- package/dist/unauthenticatedClient.js.map +1 -1
- package/package.json +11 -10
- package/dist/crypto_utils.d.ts +0 -15
- package/dist/crypto_utils.d.ts.map +0 -1
- package/dist/crypto_utils.js +0 -99
- package/dist/crypto_utils.js.map +0 -1
- package/dist/sync-agent/timeline/models/pendingReplacedEvents.d.ts +0 -12
- package/dist/sync-agent/timeline/models/pendingReplacedEvents.d.ts.map +0 -1
- package/dist/sync-agent/timeline/models/pendingReplacedEvents.js +0 -20
- package/dist/sync-agent/timeline/models/pendingReplacedEvents.js.map +0 -1
- package/dist/sync-agent/timeline/models/reactions.d.ts +0 -13
- package/dist/sync-agent/timeline/models/reactions.d.ts.map +0 -1
- package/dist/sync-agent/timeline/models/reactions.js +0 -67
- package/dist/sync-agent/timeline/models/reactions.js.map +0 -1
- package/dist/sync-agent/timeline/models/replacedEvents.d.ts +0 -18
- package/dist/sync-agent/timeline/models/replacedEvents.d.ts.map +0 -1
- package/dist/sync-agent/timeline/models/replacedEvents.js +0 -19
- package/dist/sync-agent/timeline/models/replacedEvents.js.map +0 -1
- package/dist/sync-agent/timeline/models/threadStats.d.ts +0 -14
- package/dist/sync-agent/timeline/models/threadStats.d.ts.map +0 -1
- package/dist/sync-agent/timeline/models/threadStats.js +0 -99
- package/dist/sync-agent/timeline/models/threadStats.js.map +0 -1
- package/dist/sync-agent/timeline/models/threads.d.ts +0 -14
- package/dist/sync-agent/timeline/models/threads.d.ts.map +0 -1
- package/dist/sync-agent/timeline/models/threads.js +0 -57
- package/dist/sync-agent/timeline/models/threads.js.map +0 -1
- package/dist/sync-agent/timeline/models/timelineEvent.d.ts +0 -12
- package/dist/sync-agent/timeline/models/timelineEvent.d.ts.map +0 -1
- package/dist/sync-agent/timeline/models/timelineEvent.js.map +0 -1
- package/dist/sync-agent/timeline/models/timelineEvents.d.ts +0 -13
- package/dist/sync-agent/timeline/models/timelineEvents.d.ts.map +0 -1
- package/dist/sync-agent/timeline/models/timelineEvents.js +0 -37
- 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
|