@amityco/ts-sdk 6.17.1-e9b6af1.0 → 6.17.2-ea577e1.0
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/@types/core/payload.d.ts +1 -0
- package/dist/@types/core/payload.d.ts.map +1 -1
- package/dist/@types/core/permissions.d.ts +5 -0
- package/dist/@types/core/permissions.d.ts.map +1 -1
- package/dist/@types/domains/client.d.ts +1 -0
- package/dist/@types/domains/client.d.ts.map +1 -1
- package/dist/@types/domains/story.d.ts +4 -0
- package/dist/@types/domains/story.d.ts.map +1 -1
- package/dist/channelRepository/observers/getChannel.d.ts.map +1 -1
- package/dist/channelRepository/observers/getChannels/ChannelLiveCollectionController.d.ts.map +1 -1
- package/dist/client/api/createClient.d.ts.map +1 -1
- package/dist/client/observers/getTotalUnreadCount.d.ts.map +1 -1
- package/dist/client/observers/getUserUnread.d.ts.map +1 -1
- package/dist/client/utils/hasPermission/checkChannelPermission.d.ts +2 -0
- package/dist/client/utils/hasPermission/checkChannelPermission.d.ts.map +1 -0
- package/dist/client/utils/hasPermission/checkCommunityPermission.d.ts +2 -0
- package/dist/client/utils/hasPermission/checkCommunityPermission.d.ts.map +1 -0
- package/dist/client/utils/hasPermission/checkUserPermission.d.ts +2 -0
- package/dist/client/utils/hasPermission/checkUserPermission.d.ts.map +1 -0
- package/dist/client/utils/hasPermission/hasPermission.d.ts +6 -0
- package/dist/client/utils/hasPermission/hasPermission.d.ts.map +1 -0
- package/dist/client/utils/hasPermission/index.d.ts +2 -0
- package/dist/client/utils/hasPermission/index.d.ts.map +1 -0
- package/dist/client/utils/markerSyncEngine.d.ts.map +1 -1
- package/dist/index.cjs.js +510 -70
- package/dist/index.esm.js +510 -71
- package/dist/index.umd.js +4 -4
- package/dist/marker/api/getUserMarker.d.ts +1 -1
- package/dist/marker/api/getUserMarker.d.ts.map +1 -1
- package/dist/marker/events/onChannelMarkerFetched.d.ts.map +1 -1
- package/dist/marker/events/onUserMarkerFetched.d.ts +2 -1
- package/dist/marker/events/onUserMarkerFetched.d.ts.map +1 -1
- package/dist/messageRepository/events/onMessageCreated.d.ts +2 -1
- package/dist/messageRepository/events/onMessageCreated.d.ts.map +1 -1
- package/dist/messageRepository/observers/observeMessages.d.ts.map +1 -1
- package/dist/messageRepository/utils/prepareMessagePayload.d.ts.map +1 -1
- package/dist/storyRepository/constants.d.ts +1 -0
- package/dist/storyRepository/constants.d.ts.map +1 -1
- package/dist/storyRepository/observers/getStoriesByTargetIds/StoryLiveCollectionController.d.ts +14 -0
- package/dist/storyRepository/observers/getStoriesByTargetIds/StoryLiveCollectionController.d.ts.map +1 -0
- package/dist/storyRepository/observers/getStoriesByTargetIds/StoryPaginationNoPageController.d.ts +5 -0
- package/dist/storyRepository/observers/getStoriesByTargetIds/StoryPaginationNoPageController.d.ts.map +1 -0
- package/dist/storyRepository/observers/getStoriesByTargetIds/StoryQueryStreamController.d.ts +16 -0
- package/dist/storyRepository/observers/getStoriesByTargetIds/StoryQueryStreamController.d.ts.map +1 -0
- package/dist/storyRepository/observers/getStoriesByTargetIds/getStoriesByTargetIds.d.ts +5 -0
- package/dist/storyRepository/observers/getStoriesByTargetIds/getStoriesByTargetIds.d.ts.map +1 -0
- package/dist/storyRepository/observers/getStoriesByTargetIds/index.d.ts +2 -0
- package/dist/storyRepository/observers/getStoriesByTargetIds/index.d.ts.map +1 -0
- package/dist/storyRepository/observers/index.d.ts +1 -0
- package/dist/storyRepository/observers/index.d.ts.map +1 -1
- package/dist/subChannelRepository/observers/getSubChannel.d.ts.map +1 -1
- package/dist/subChannelRepository/observers/getSubChannels/SubChannelLiveCollectionController.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/@types/core/payload.ts +1 -0
- package/src/@types/core/permissions.ts +6 -0
- package/src/@types/domains/client.ts +2 -0
- package/src/@types/domains/story.ts +5 -0
- package/src/channelRepository/observers/getChannel.ts +30 -2
- package/src/channelRepository/observers/getChannels/ChannelLiveCollectionController.ts +65 -3
- package/src/client/api/createClient.ts +3 -1
- package/src/client/observers/getTotalUnreadCount.ts +14 -4
- package/src/client/observers/getUserUnread.ts +95 -11
- package/src/client/utils/hasPermission/checkChannelPermission.ts +22 -0
- package/src/client/utils/hasPermission/checkCommunityPermission.ts +22 -0
- package/src/client/utils/hasPermission/checkUserPermission.ts +12 -0
- package/src/client/utils/hasPermission/hasPermission.ts +18 -0
- package/src/client/utils/hasPermission/index.ts +1 -0
- package/src/client/utils/markerSyncEngine.ts +46 -32
- package/src/marker/api/getUserMarker.ts +15 -4
- package/src/marker/events/onChannelMarkerFetched.ts +3 -1
- package/src/marker/events/onUserMarkerFetched.ts +17 -0
- package/src/messageRepository/api/tests/createMessage.test.ts +4 -4
- package/src/messageRepository/events/onMessageCreated.ts +20 -8
- package/src/messageRepository/events/tests/events.test.ts +2 -2
- package/src/messageRepository/observers/getMessages/MessageLiveCollectionController.ts +2 -2
- package/src/messageRepository/observers/observeMessages.ts +8 -2
- package/src/messageRepository/utils/prepareMessagePayload.ts +14 -4
- package/src/storyRepository/constants.ts +1 -0
- package/src/storyRepository/observers/getStoriesByTargetIds/StoryLiveCollectionController.ts +113 -0
- package/src/storyRepository/observers/getStoriesByTargetIds/StoryPaginationNoPageController.ts +18 -0
- package/src/storyRepository/observers/getStoriesByTargetIds/StoryQueryStreamController.ts +97 -0
- package/src/storyRepository/observers/getStoriesByTargetIds/getStoriesByTargetIds.ts +32 -0
- package/src/storyRepository/observers/getStoriesByTargetIds/index.ts +1 -0
- package/src/storyRepository/observers/index.ts +1 -0
- package/src/subChannelRepository/observers/getSubChannel.ts +35 -2
- package/src/subChannelRepository/observers/getSubChannels/SubChannelLiveCollectionController.ts +53 -2
|
@@ -8,7 +8,7 @@ import { onUserMarkerSync } from '~/marker/events/onUserMarkerSync';
|
|
|
8
8
|
import { setIntervalTask } from '~/utils/timer';
|
|
9
9
|
import { getUserMarker } from '~/marker/api';
|
|
10
10
|
import { onFeedMarkerUpdated } from '~/marker/events';
|
|
11
|
-
import {
|
|
11
|
+
import { onMessageCreatedMqtt } from '~/messageRepository/events';
|
|
12
12
|
import { onSubChannelCreated, onSubChannelDeleted } from '~/subChannelRepository/events';
|
|
13
13
|
import { isUnreadCountSupport } from '~/subChannelRepository/utils';
|
|
14
14
|
|
|
@@ -22,25 +22,27 @@ let isSyncRunning = false;
|
|
|
22
22
|
let disposers: (() => void)[] = [];
|
|
23
23
|
let isWaitingForResponse = false;
|
|
24
24
|
|
|
25
|
-
let deviceLastSyncAt:
|
|
25
|
+
let deviceLastSyncAt: Date | null = null;
|
|
26
26
|
|
|
27
27
|
const getDeviceLastSyncAt = () => {
|
|
28
28
|
if (deviceLastSyncAt == null) {
|
|
29
|
-
return new Date()
|
|
29
|
+
return new Date();
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
return deviceLastSyncAt;
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
const saveDeviceLastSyncAt = (lastSyncAt:
|
|
36
|
-
if (
|
|
35
|
+
const saveDeviceLastSyncAt = (lastSyncAt: Date | null) => {
|
|
36
|
+
if (lastSyncAt == null) return;
|
|
37
|
+
if (!deviceLastSyncAt || lastSyncAt.getTime() > deviceLastSyncAt.getTime()) {
|
|
37
38
|
deviceLastSyncAt = lastSyncAt;
|
|
38
39
|
}
|
|
39
40
|
};
|
|
40
41
|
|
|
41
42
|
const fetchDeviceLastSyncAt = async () => {
|
|
42
43
|
const { data: userMarker } = await getUserMarker();
|
|
43
|
-
|
|
44
|
+
if (userMarker == null) return;
|
|
45
|
+
saveDeviceLastSyncAt(new Date(userMarker.lastSyncAt));
|
|
44
46
|
};
|
|
45
47
|
|
|
46
48
|
/**
|
|
@@ -84,24 +86,41 @@ export const markerSyncTrigger = async () => {
|
|
|
84
86
|
// no event that require to call marker sync API
|
|
85
87
|
return;
|
|
86
88
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
89
|
+
try {
|
|
90
|
+
isWaitingForResponse = true;
|
|
91
|
+
// any past events are considered processed here.
|
|
92
|
+
// however during waiting for the response, RTE could add the new message event;
|
|
93
|
+
// which will make the engine trigger another call next round.
|
|
94
|
+
events = [];
|
|
95
|
+
|
|
96
|
+
const response = await markerSync(getDeviceLastSyncAt().toISOString());
|
|
97
|
+
|
|
98
|
+
const latestLastSyncAt: Date | null = response.data.userMarkers.reduce(
|
|
99
|
+
(maxLastSyncAt, userMarker) => {
|
|
100
|
+
if (
|
|
101
|
+
maxLastSyncAt == null ||
|
|
102
|
+
maxLastSyncAt.getTime() < new Date(userMarker.lastSyncAt).getTime()
|
|
103
|
+
) {
|
|
104
|
+
return new Date(userMarker.lastSyncAt);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return maxLastSyncAt;
|
|
108
|
+
},
|
|
109
|
+
null as Date | null,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
saveDeviceLastSyncAt(latestLastSyncAt);
|
|
113
|
+
|
|
114
|
+
if (response.hasMore) {
|
|
115
|
+
events.push(Amity.MarkerSyncEvent.HAS_MORE);
|
|
116
|
+
}
|
|
117
|
+
} catch {
|
|
118
|
+
// prevent sync from stopping
|
|
119
|
+
} finally {
|
|
120
|
+
if (isWaitingForResponse) {
|
|
121
|
+
isWaitingForResponse = false;
|
|
122
|
+
}
|
|
101
123
|
}
|
|
102
|
-
|
|
103
|
-
// eslint-disable-next-line require-atomic-updates
|
|
104
|
-
isWaitingForResponse = false;
|
|
105
124
|
};
|
|
106
125
|
|
|
107
126
|
const registerEventListeners = () => {
|
|
@@ -119,15 +138,10 @@ const registerEventListeners = () => {
|
|
|
119
138
|
events.push(Amity.MarkerSyncEvent.RESUME);
|
|
120
139
|
}),
|
|
121
140
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
},
|
|
127
|
-
// only trigger sync from remote events to prevent an unread count equal `-1`
|
|
128
|
-
// when an optimistic message creation is performed.
|
|
129
|
-
false,
|
|
130
|
-
),
|
|
141
|
+
onMessageCreatedMqtt(message => {
|
|
142
|
+
// only conversation, community and broadcast types can sync
|
|
143
|
+
if (isUnreadCountSupport(message)) events.push(Amity.MarkerSyncEvent.NEW_MESSAGE);
|
|
144
|
+
}),
|
|
131
145
|
|
|
132
146
|
onChannelCreated(() => events.push(Amity.MarkerSyncEvent.CHANNEL_CREATED)),
|
|
133
147
|
onChannelDeleted(() => events.push(Amity.MarkerSyncEvent.CHANNEL_DELETED)),
|
|
@@ -4,7 +4,7 @@ import { fireEvent } from '~/core/events';
|
|
|
4
4
|
// import { convertMarkerResponse } from '~/utils/marker';
|
|
5
5
|
import { convertUserMarkerResponse } from '~/utils/marker';
|
|
6
6
|
|
|
7
|
-
export const getUserMarker = async (): Promise<Amity.Cached<Amity.UserMarker>> => {
|
|
7
|
+
export const getUserMarker = async (): Promise<Amity.Cached<Amity.UserMarker | null>> => {
|
|
8
8
|
const client = getActiveClient();
|
|
9
9
|
client.log('channel/getUserMarker');
|
|
10
10
|
|
|
@@ -14,8 +14,8 @@ export const getUserMarker = async (): Promise<Amity.Cached<Amity.UserMarker>> =
|
|
|
14
14
|
|
|
15
15
|
const { userMarkers: UserMarkersPayload } = payload;
|
|
16
16
|
|
|
17
|
-
/*
|
|
18
|
-
change field isMentioned from backend to be hasMentioned
|
|
17
|
+
/*
|
|
18
|
+
change field isMentioned from backend to be hasMentioned
|
|
19
19
|
*/
|
|
20
20
|
const userMarkers = convertUserMarkerResponse(UserMarkersPayload);
|
|
21
21
|
|
|
@@ -24,5 +24,16 @@ export const getUserMarker = async (): Promise<Amity.Cached<Amity.UserMarker>> =
|
|
|
24
24
|
|
|
25
25
|
fireEvent('local.userMarker.fetched', { userMarkers });
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
const latestUserMarker = userMarkers.reduce((maxUserMarker, userMarker) => {
|
|
28
|
+
if (
|
|
29
|
+
maxUserMarker == null ||
|
|
30
|
+
new Date(maxUserMarker.lastSyncAt).getTime() < new Date(userMarker.lastSyncAt).getTime()
|
|
31
|
+
) {
|
|
32
|
+
return userMarker;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return maxUserMarker;
|
|
36
|
+
}, null as Amity.UserMarker | null);
|
|
37
|
+
|
|
38
|
+
return { data: latestUserMarker, cachedAt };
|
|
28
39
|
};
|
|
@@ -22,7 +22,9 @@ export const onChannelMarkerFetched = (
|
|
|
22
22
|
const client = getActiveClient();
|
|
23
23
|
|
|
24
24
|
const filter = (payload: Amity.Events['local.channelMarker.fetched']) => {
|
|
25
|
-
|
|
25
|
+
payload.userEntityMarkers.forEach(marker => {
|
|
26
|
+
callback(marker);
|
|
27
|
+
});
|
|
26
28
|
};
|
|
27
29
|
|
|
28
30
|
return createEventSubscriber(
|
|
@@ -17,6 +17,23 @@ import { createEventSubscriber } from '~/core/events';
|
|
|
17
17
|
* @category UserMarker Events
|
|
18
18
|
*/
|
|
19
19
|
export const onUserMarkerFetched = (
|
|
20
|
+
callback: Amity.Listener<Amity.UserMarker[]>,
|
|
21
|
+
): Amity.Unsubscriber => {
|
|
22
|
+
const client = getActiveClient();
|
|
23
|
+
|
|
24
|
+
const filter = (payload: Amity.Events['local.userMarker.fetched']) => {
|
|
25
|
+
callback(payload.userMarkers);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return createEventSubscriber(
|
|
29
|
+
client,
|
|
30
|
+
'userMarker/onUserMarkerFetched',
|
|
31
|
+
'local.userMarker.fetched',
|
|
32
|
+
filter,
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const onUserMarkerFetchedLegacy = (
|
|
20
37
|
callback: Amity.Listener<Amity.UserMarker>,
|
|
21
38
|
): Amity.Unsubscriber => {
|
|
22
39
|
const client = getActiveClient();
|
|
@@ -5,7 +5,7 @@ import { disableCache, enableCache, pullFromCache, pushToCache } from '~/cache/a
|
|
|
5
5
|
import { client, convertRawMessage, generateRawMessage, pause } from '~/utils/tests';
|
|
6
6
|
import { createQuery, runQuery } from '~/core/query';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { onMessageCreatedMqtt } from '../../events';
|
|
9
9
|
import { createMessage } from '../createMessage';
|
|
10
10
|
import * as getMessageMarkers from '../../../marker/api/getMessageMarkers';
|
|
11
11
|
|
|
@@ -72,7 +72,7 @@ describe('createMessage', () => {
|
|
|
72
72
|
client.http.post = jest.fn().mockResolvedValueOnce(response);
|
|
73
73
|
|
|
74
74
|
const callbackPromise = new Promise(resolve => {
|
|
75
|
-
dispose =
|
|
75
|
+
dispose = onMessageCreatedMqtt(resolve);
|
|
76
76
|
}).finally(dispose);
|
|
77
77
|
|
|
78
78
|
await createMessage(messageWithoutId);
|
|
@@ -134,7 +134,7 @@ describe('createMessage.optimistically', () => {
|
|
|
134
134
|
let dispose;
|
|
135
135
|
|
|
136
136
|
const callbackPromise = new Promise(resolve => {
|
|
137
|
-
dispose =
|
|
137
|
+
dispose = onMessageCreatedMqtt(resolve);
|
|
138
138
|
}).finally(dispose);
|
|
139
139
|
|
|
140
140
|
createMessage.optimistically(messageWithoutId);
|
|
@@ -193,7 +193,7 @@ describe('uniqueId / referenceId', () => {
|
|
|
193
193
|
};
|
|
194
194
|
const callback = jest.fn();
|
|
195
195
|
|
|
196
|
-
|
|
196
|
+
onMessageCreatedMqtt(callback);
|
|
197
197
|
client.emitter.emit('message.created', {
|
|
198
198
|
messages: [rawNewMessage],
|
|
199
199
|
messageFeeds: [],
|
|
@@ -19,31 +19,43 @@ import { prepareMessagePayload } from '../utils';
|
|
|
19
19
|
*
|
|
20
20
|
* @category Message Events
|
|
21
21
|
*/
|
|
22
|
-
export const
|
|
22
|
+
export const onMessageCreatedMqtt = (
|
|
23
23
|
callback: Amity.Listener<Amity.Message>,
|
|
24
|
-
local = true,
|
|
25
24
|
): Amity.Unsubscriber => {
|
|
26
25
|
const client = getActiveClient();
|
|
27
26
|
|
|
28
27
|
const filter = async (rawPayload: Amity.MessagePayload) => {
|
|
29
28
|
const payload = await prepareMessagePayload(rawPayload);
|
|
30
|
-
const message = payload.messages[0];
|
|
31
29
|
|
|
32
30
|
// Update in cache
|
|
33
31
|
ingestInCache(payload);
|
|
34
32
|
|
|
35
|
-
|
|
33
|
+
payload.messages.forEach(message => {
|
|
34
|
+
callback(message);
|
|
35
|
+
});
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
const disposers = [
|
|
39
39
|
createEventSubscriber(client, 'message/onMessageCreated', 'message.created', filter),
|
|
40
40
|
];
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
return () => {
|
|
43
|
+
disposers.forEach(fn => fn());
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const onMessageCreatedLocal = (
|
|
48
|
+
callback: Amity.Listener<Amity.Message>,
|
|
49
|
+
): Amity.Unsubscriber => {
|
|
50
|
+
const client = getActiveClient();
|
|
51
|
+
|
|
52
|
+
const disposers = [
|
|
43
53
|
createEventSubscriber(client, 'message/onMessageCreated', 'local.message.created', payload =>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
54
|
+
payload.messages.forEach(message => {
|
|
55
|
+
callback(message);
|
|
56
|
+
}),
|
|
57
|
+
),
|
|
58
|
+
];
|
|
47
59
|
|
|
48
60
|
return () => {
|
|
49
61
|
disposers.forEach(fn => fn());
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { client, generateRawMessage, pause } from '~/utils/tests';
|
|
2
2
|
import { convertFromRaw } from '~/messageRepository/utils';
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
onMessageCreatedMqtt,
|
|
5
5
|
onMessageUpdated,
|
|
6
6
|
onMessageDeleted,
|
|
7
7
|
onMessageFlagged,
|
|
@@ -18,7 +18,7 @@ describe('Message Events', () => {
|
|
|
18
18
|
const cases: Array<
|
|
19
19
|
[(callback: Amity.Listener<Amity.Message>) => Amity.Unsubscriber, keyof Amity.MqttMessageEvents]
|
|
20
20
|
> = [
|
|
21
|
-
[
|
|
21
|
+
[onMessageCreatedMqtt, 'message.created'],
|
|
22
22
|
[onMessageUpdated, 'message.updated'],
|
|
23
23
|
[onMessageDeleted, 'message.deleted'],
|
|
24
24
|
[onMessageFlagged, 'message.flagged'],
|
|
@@ -3,7 +3,7 @@ import { pullFromCache, pushToCache } from '~/cache/api';
|
|
|
3
3
|
import { MessagePaginationController } from './MessagePaginationController';
|
|
4
4
|
import { MessageQueryStreamController } from './MessageQueryStreamController';
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
onMessageCreatedMqtt,
|
|
7
7
|
onMessageDeleted,
|
|
8
8
|
onMessageFlagCleared,
|
|
9
9
|
onMessageFlagged,
|
|
@@ -75,7 +75,7 @@ export class MessageLiveCollectionController extends LiveCollectionController<
|
|
|
75
75
|
|
|
76
76
|
startSubscription() {
|
|
77
77
|
return this.queryStreamController.subscribeRTE([
|
|
78
|
-
{ fn:
|
|
78
|
+
{ fn: onMessageCreatedMqtt, action: 'onCreate' },
|
|
79
79
|
{ fn: onMessageDeleted, action: 'onDelete' },
|
|
80
80
|
{ fn: onMessageUpdated, action: 'onUpdate' },
|
|
81
81
|
{ fn: onMessageFlagged, action: 'onFlagged' },
|
|
@@ -2,7 +2,12 @@ import { getActiveClient } from '~/client/api';
|
|
|
2
2
|
import { onMessageMarked, onMessageMarkerFetched } from '~/marker/events';
|
|
3
3
|
import { convertEventPayload } from '~/utils/event';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
onMessageCreatedMqtt,
|
|
7
|
+
onMessageUpdated,
|
|
8
|
+
onMessageDeleted,
|
|
9
|
+
onMessageCreatedLocal,
|
|
10
|
+
} from '../events';
|
|
6
11
|
|
|
7
12
|
/**
|
|
8
13
|
* ```js
|
|
@@ -48,7 +53,8 @@ export const observeMessages = (
|
|
|
48
53
|
};
|
|
49
54
|
|
|
50
55
|
disposers.push(
|
|
51
|
-
|
|
56
|
+
onMessageCreatedMqtt(data => router({ data, loading: false, origin: 'event' }, 'onCreate')),
|
|
57
|
+
onMessageCreatedLocal(data => router({ data, loading: false, origin: 'event' }, 'onCreate')),
|
|
52
58
|
onMessageUpdated(data => router({ data, loading: false, origin: 'event' }, 'onUpdate')),
|
|
53
59
|
onMessageDeleted(data => router({ data, loading: false, origin: 'event' }, 'onDelete')),
|
|
54
60
|
convertEventPayload(
|
|
@@ -92,6 +92,9 @@ const preUpdateMessageCache = (rawPayload: Amity.MessagePayload) => {
|
|
|
92
92
|
});
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
+
const DEBOUNCE_TIME = 2000;
|
|
96
|
+
const currentDebounceMap: Record<string, NodeJS.Timeout | undefined> = {};
|
|
97
|
+
|
|
95
98
|
export const prepareMessagePayload = async (
|
|
96
99
|
payload: Amity.MessagePayload,
|
|
97
100
|
event?: keyof Amity.MqttMessageEvents,
|
|
@@ -102,11 +105,18 @@ export const prepareMessagePayload = async (
|
|
|
102
105
|
// since the get markers method requires a channel cache to function with the reducer.
|
|
103
106
|
preUpdateMessageCache(payload);
|
|
104
107
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
const markerIdsKey = markerIds.join('');
|
|
109
|
+
|
|
110
|
+
if (currentDebounceMap[markerIdsKey]) {
|
|
111
|
+
clearTimeout(currentDebounceMap[markerIdsKey]);
|
|
109
112
|
}
|
|
113
|
+
currentDebounceMap[markerIdsKey] = setTimeout(() => {
|
|
114
|
+
try {
|
|
115
|
+
getMessageMarkers(markerIds);
|
|
116
|
+
} catch (_error) {
|
|
117
|
+
// do nothing
|
|
118
|
+
}
|
|
119
|
+
}, DEBOUNCE_TIME);
|
|
110
120
|
}
|
|
111
121
|
|
|
112
122
|
const { messageFeeds, ...restPayload } = payload;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import hash from 'object-hash';
|
|
2
|
+
import { LiveCollectionController } from '~/core/liveCollection/LiveCollectionController';
|
|
3
|
+
import { pullFromCache, pushToCache } from '~/cache/api';
|
|
4
|
+
import { LinkedObject } from '~/utils/linkedObject';
|
|
5
|
+
import { sortByFirstCreated, sortByLastCreated } from '~/core/query';
|
|
6
|
+
import { onStoryCreated } from '~/storyRepository/events/onStoryCreated';
|
|
7
|
+
import { onStoryUpdated } from '~/storyRepository/events/onStoryUpdated';
|
|
8
|
+
import { onStoryDeleted } from '~/storyRepository/events/onStoryDeleted';
|
|
9
|
+
import { onStoryError } from '~/storyRepository/events/onStoryError';
|
|
10
|
+
import { STORY_KEY_CACHE } from '~/storyRepository/constants';
|
|
11
|
+
import { StoryQueryStreamController } from './StoryQueryStreamController';
|
|
12
|
+
import { StoryPaginationNoPageController } from './StoryPaginationNoPageController';
|
|
13
|
+
|
|
14
|
+
export class StoryLiveCollectionController extends LiveCollectionController<
|
|
15
|
+
'story',
|
|
16
|
+
Amity.StoryLiveCollection,
|
|
17
|
+
Amity.Story,
|
|
18
|
+
StoryPaginationNoPageController
|
|
19
|
+
> {
|
|
20
|
+
private queryStreamController: StoryQueryStreamController;
|
|
21
|
+
|
|
22
|
+
private query: Amity.StoryLiveCollection;
|
|
23
|
+
|
|
24
|
+
constructor(
|
|
25
|
+
query: Amity.StoryLiveCollection,
|
|
26
|
+
callback: Amity.LiveCollectionCallback<Amity.Story>,
|
|
27
|
+
) {
|
|
28
|
+
const queryStreamId = hash(query);
|
|
29
|
+
const cacheKey = [STORY_KEY_CACHE.STORY_TARGET_IDS, 'collection', queryStreamId];
|
|
30
|
+
const paginationController = new StoryPaginationNoPageController(query);
|
|
31
|
+
|
|
32
|
+
super(paginationController, queryStreamId, cacheKey, callback);
|
|
33
|
+
|
|
34
|
+
this.query = query;
|
|
35
|
+
|
|
36
|
+
this.queryStreamController = new StoryQueryStreamController(
|
|
37
|
+
this.query,
|
|
38
|
+
this.cacheKey,
|
|
39
|
+
this.notifyChange.bind(this),
|
|
40
|
+
paginationController,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
this.paginationController = paginationController;
|
|
44
|
+
this.callback = callback.bind(this);
|
|
45
|
+
this.loadPage(true);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
protected setup() {
|
|
49
|
+
const collection = pullFromCache<Amity.StoryLiveCollectionCache>(this.cacheKey)?.data;
|
|
50
|
+
if (!collection) {
|
|
51
|
+
pushToCache(this.cacheKey, {
|
|
52
|
+
data: [],
|
|
53
|
+
params: {},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
protected persistModel(response: Amity.StoryPayload) {
|
|
59
|
+
this.queryStreamController.saveToMainDB(response);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
protected persistQueryStream({
|
|
63
|
+
response,
|
|
64
|
+
direction,
|
|
65
|
+
refresh,
|
|
66
|
+
}: Amity.LiveCollectionPersistQueryStreamParams<'story'>) {
|
|
67
|
+
this.queryStreamController.appendToQueryStream(response, direction, refresh);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
notifyChange({ origin, loading, error }: Amity.LiveCollectionNotifyParams) {
|
|
71
|
+
const collection = pullFromCache<Amity.StoryLiveCollectionCache>(this.cacheKey)?.data;
|
|
72
|
+
if (!collection) return;
|
|
73
|
+
|
|
74
|
+
let data = collection.data
|
|
75
|
+
.map(
|
|
76
|
+
referenceId =>
|
|
77
|
+
pullFromCache<Amity.InternalStory>([STORY_KEY_CACHE.STORY, 'get', referenceId])!, // Story use referenceId instead of storyId
|
|
78
|
+
)
|
|
79
|
+
.filter(Boolean)
|
|
80
|
+
.map(internStory => LinkedObject.story(internStory.data));
|
|
81
|
+
|
|
82
|
+
if (!this.shouldNotify(data) && origin === 'event') return;
|
|
83
|
+
|
|
84
|
+
data = this.applyFilter(data);
|
|
85
|
+
|
|
86
|
+
this.callback({
|
|
87
|
+
onNextPage: undefined,
|
|
88
|
+
data,
|
|
89
|
+
hasNextPage: false,
|
|
90
|
+
loading,
|
|
91
|
+
error,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private applyFilter(data: Amity.Story[]) {
|
|
96
|
+
const internalStories = data;
|
|
97
|
+
|
|
98
|
+
const orderBy = this.query?.options?.orderBy || 'desc';
|
|
99
|
+
|
|
100
|
+
return orderBy === 'asc'
|
|
101
|
+
? internalStories.sort(sortByFirstCreated)
|
|
102
|
+
: internalStories.sort(sortByLastCreated);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
startSubscription() {
|
|
106
|
+
return this.queryStreamController.subscribeRTE([
|
|
107
|
+
{ fn: onStoryCreated, action: Amity.StoryActionType.OnCreate },
|
|
108
|
+
{ fn: onStoryUpdated, action: Amity.StoryActionType.OnUpdate },
|
|
109
|
+
{ fn: onStoryDeleted, action: Amity.StoryActionType.OnDelete },
|
|
110
|
+
{ fn: onStoryError, action: Amity.StoryActionType.OnError },
|
|
111
|
+
]);
|
|
112
|
+
}
|
|
113
|
+
}
|
package/src/storyRepository/observers/getStoriesByTargetIds/StoryPaginationNoPageController.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* eslint-disable no-use-before-define */
|
|
2
|
+
|
|
3
|
+
import { PaginationNoPageController } from '~/core/liveCollection/PaginationNoPageController';
|
|
4
|
+
|
|
5
|
+
export class StoryPaginationNoPageController extends PaginationNoPageController<
|
|
6
|
+
'story',
|
|
7
|
+
Pick<Amity.StoryLiveCollection, 'targets'>
|
|
8
|
+
> {
|
|
9
|
+
async getRequest(queryParams: Pick<Amity.StoryLiveCollection, 'targets'>) {
|
|
10
|
+
const { data: queryResponse } = await this.http.get<Amity.StoryPayload>(
|
|
11
|
+
'/api/v4/stories-by-targets',
|
|
12
|
+
{
|
|
13
|
+
params: { ...queryParams },
|
|
14
|
+
},
|
|
15
|
+
);
|
|
16
|
+
return queryResponse;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { QueryStreamController } from '~/core/liveCollection/QueryStreamController';
|
|
2
|
+
import { pullFromCache, pushToCache } from '~/cache/api';
|
|
3
|
+
import { getActiveClient } from '~/client';
|
|
4
|
+
import { ingestInCache } from '~/cache/api/ingestInCache';
|
|
5
|
+
import { updateLocalLastStoryExpires } from '~/storyRepository/utils/updateLocalLastStoryExpires';
|
|
6
|
+
import { mappingStoryIdToReferenceId } from '~/storyRepository/utils/mappingStoryIdToReferenceId';
|
|
7
|
+
import { convertStoryRawToInternal } from '~/storyRepository/utils/convertRawToStory';
|
|
8
|
+
import { StoryPaginationNoPageController } from './StoryPaginationNoPageController';
|
|
9
|
+
|
|
10
|
+
export class StoryQueryStreamController extends QueryStreamController<
|
|
11
|
+
Amity.StoryPayload,
|
|
12
|
+
Amity.StoryLiveCollection
|
|
13
|
+
> {
|
|
14
|
+
private notifyChange: (params: Amity.LiveCollectionNotifyParams) => void;
|
|
15
|
+
|
|
16
|
+
private paginationController: StoryPaginationNoPageController;
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
query: Amity.StoryLiveCollection,
|
|
20
|
+
cacheKey: string[],
|
|
21
|
+
notifyChange: (params: Amity.LiveCollectionNotifyParams) => void,
|
|
22
|
+
paginationController: StoryPaginationNoPageController,
|
|
23
|
+
) {
|
|
24
|
+
super(query, cacheKey);
|
|
25
|
+
this.notifyChange = notifyChange;
|
|
26
|
+
this.paginationController = paginationController;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// eslint-disable-next-line class-methods-use-this
|
|
30
|
+
saveToMainDB(response: Amity.StoryPayload) {
|
|
31
|
+
const client = getActiveClient();
|
|
32
|
+
const cachedAt = client.cache && Date.now();
|
|
33
|
+
|
|
34
|
+
const convertedData: Amity.StoryPayload = convertStoryRawToInternal(response);
|
|
35
|
+
|
|
36
|
+
if (client.cache) {
|
|
37
|
+
ingestInCache(convertedData, { cachedAt });
|
|
38
|
+
|
|
39
|
+
// Update local last story expires
|
|
40
|
+
updateLocalLastStoryExpires(convertedData.stories);
|
|
41
|
+
|
|
42
|
+
// Map storyId to referenceId
|
|
43
|
+
mappingStoryIdToReferenceId(convertedData.stories);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// eslint-disable-next-line class-methods-use-this
|
|
48
|
+
getStoryReferenceIds(story: Amity.RawStory) {
|
|
49
|
+
if (story?.referenceId) {
|
|
50
|
+
return story.referenceId;
|
|
51
|
+
}
|
|
52
|
+
return story.storyId;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
appendToQueryStream(
|
|
56
|
+
response: Amity.StoryPayload,
|
|
57
|
+
direction: Amity.LiveCollectionPageDirection,
|
|
58
|
+
refresh = false,
|
|
59
|
+
) {
|
|
60
|
+
if (refresh) {
|
|
61
|
+
pushToCache(this.cacheKey, {
|
|
62
|
+
data: response.stories.map(this.getStoryReferenceIds),
|
|
63
|
+
});
|
|
64
|
+
} else {
|
|
65
|
+
const collection = pullFromCache<Amity.StoryLiveCollectionCache>(this.cacheKey)?.data;
|
|
66
|
+
const stories = collection?.data ?? [];
|
|
67
|
+
|
|
68
|
+
pushToCache(this.cacheKey, {
|
|
69
|
+
...collection,
|
|
70
|
+
data: [...new Set([...stories, ...response.stories.map(this.getStoryReferenceIds)])],
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
reactor(action: Amity.StoryActionType) {
|
|
76
|
+
return (payload: Amity.InternalStory[]) => {
|
|
77
|
+
const collection = pullFromCache<Amity.StoryLiveCollectionCache>(this.cacheKey)?.data;
|
|
78
|
+
if (!collection) return;
|
|
79
|
+
|
|
80
|
+
const newReferenceIds = payload.map(({ referenceId }) => referenceId);
|
|
81
|
+
|
|
82
|
+
collection.data = [...new Set([...newReferenceIds, ...collection.data])];
|
|
83
|
+
pushToCache(this.cacheKey, collection);
|
|
84
|
+
|
|
85
|
+
this.notifyChange({ origin: Amity.LiveDataOrigin.EVENT, loading: false });
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
subscribeRTE(
|
|
90
|
+
createSubscriber: {
|
|
91
|
+
fn: (reactor: Amity.Listener<Amity.InternalStory[]>) => Amity.Unsubscriber;
|
|
92
|
+
action: Amity.StoryActionType;
|
|
93
|
+
}[],
|
|
94
|
+
) {
|
|
95
|
+
return createSubscriber.map(subscriber => subscriber.fn(this.reactor(subscriber.action)));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { dropFromCache } from '~/cache/api';
|
|
2
|
+
import { getActiveClient } from '~/client';
|
|
3
|
+
import { ENABLE_CACHE_MESSAGE } from '~/utils/constants';
|
|
4
|
+
import { StoryLiveCollectionController } from './StoryLiveCollectionController';
|
|
5
|
+
|
|
6
|
+
export const getStoriesByTargetIds = (
|
|
7
|
+
params: { targets: Amity.StoryTargetQueryParam[]; options?: Amity.StorySortOption },
|
|
8
|
+
callback: Amity.LiveCollectionCallback<Amity.Story>,
|
|
9
|
+
config?: Amity.LiveCollectionConfig,
|
|
10
|
+
) => {
|
|
11
|
+
const { log, cache, userId } = getActiveClient();
|
|
12
|
+
|
|
13
|
+
if (!cache) {
|
|
14
|
+
console.log(ENABLE_CACHE_MESSAGE);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const timestamp = Date.now();
|
|
18
|
+
log(`getStoriesByTargetIds(tmpid: ${timestamp}) > listen`);
|
|
19
|
+
|
|
20
|
+
const storyLiveCollection = new StoryLiveCollectionController(params, callback);
|
|
21
|
+
const disposers = storyLiveCollection.startSubscription();
|
|
22
|
+
const cacheKey = storyLiveCollection.getCacheKey();
|
|
23
|
+
|
|
24
|
+
disposers.push(() => {
|
|
25
|
+
dropFromCache(cacheKey);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return () => {
|
|
29
|
+
log(`getStoriesByTargetIds(tmpid: ${timestamp}) > dispose`);
|
|
30
|
+
disposers.forEach(fn => fn());
|
|
31
|
+
};
|
|
32
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './getStoriesByTargetIds';
|