@amityco/ts-sdk 6.18.1-c018952.0 → 6.19.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/.env +26 -26
- package/dist/@types/core/events.d.ts +4 -0
- package/dist/@types/core/events.d.ts.map +1 -1
- package/dist/@types/core/objectResolver.d.ts +13 -0
- package/dist/@types/core/objectResolver.d.ts.map +1 -0
- package/dist/@types/domains/channel.d.ts +12 -1
- package/dist/@types/domains/channel.d.ts.map +1 -1
- package/dist/analytic/api/syncEvent.d.ts.map +1 -1
- package/dist/analytic/service/analytic/AnalyticsEngine.d.ts +1 -0
- package/dist/analytic/service/analytic/AnalyticsEngine.d.ts.map +1 -1
- package/dist/analytic/service/analytic/AnalyticsEventCapturer.d.ts +1 -0
- package/dist/analytic/service/analytic/AnalyticsEventCapturer.d.ts.map +1 -1
- package/dist/analytic/service/analytic/AnalyticsEventSyncer.d.ts.map +1 -1
- package/dist/channelRepository/api/getChannel.d.ts.map +1 -1
- package/dist/channelRepository/api/getChannelByIds.d.ts.map +1 -1
- package/dist/channelRepository/events/onChannelFetched.d.ts +17 -0
- package/dist/channelRepository/events/onChannelFetched.d.ts.map +1 -0
- package/dist/channelRepository/observers/getChannels/ChannelLiveCollectionController.d.ts.map +1 -1
- package/dist/channelRepository/observers/getChannels/ChannelQueryStreamController.d.ts +3 -3
- package/dist/channelRepository/observers/getChannels/ChannelQueryStreamController.d.ts.map +1 -1
- package/dist/channelRepository/observers/observeChannel.d.ts +1 -1
- package/dist/channelRepository/observers/observeChannel.d.ts.map +1 -1
- package/dist/channelRepository/utils/prepareChannelPayload.d.ts.map +1 -1
- package/dist/channelRepository/utils/prepateUnreadCountInfo.d.ts +2 -0
- package/dist/channelRepository/utils/prepateUnreadCountInfo.d.ts.map +1 -0
- package/dist/channelRepository/utils/resolveUnreadInfoOnChannelEvent.d.ts +2 -0
- package/dist/channelRepository/utils/resolveUnreadInfoOnChannelEvent.d.ts.map +1 -0
- package/dist/client/api/login.d.ts.map +1 -1
- package/dist/client/utils/ObjectResolver/objectResolverEngine.d.ts +26 -0
- package/dist/client/utils/ObjectResolver/objectResolverEngine.d.ts.map +1 -0
- package/dist/client/utils/ObjectResolver/objectResolverEngineOnLoginHandler.d.ts +3 -0
- package/dist/client/utils/ObjectResolver/objectResolverEngineOnLoginHandler.d.ts.map +1 -0
- package/dist/client/utils/onOffline.d.ts +2 -0
- package/dist/client/utils/onOffline.d.ts.map +1 -0
- package/dist/core/events.d.ts +3 -3
- package/dist/core/events.d.ts.map +1 -1
- package/dist/index.cjs.js +446 -158
- package/dist/index.esm.js +446 -158
- package/dist/index.umd.js +4 -4
- package/dist/marker/api/getUserMessageFeedMarkers.d.ts.map +1 -1
- package/dist/marker/events/onUserFeedMarkerFetched.d.ts +17 -0
- package/dist/marker/events/onUserFeedMarkerFetched.d.ts.map +1 -0
- package/dist/marker/utils/addFlagSubChannelUnreadByChannelId.d.ts +2 -0
- package/dist/marker/utils/addFlagSubChannelUnreadByChannelId.d.ts.map +1 -0
- package/dist/marker/utils/addFlagSubChannelUnreadBySubChannelId.d.ts +2 -0
- package/dist/marker/utils/addFlagSubChannelUnreadBySubChannelId.d.ts.map +1 -0
- package/dist/messageRepository/observers/tests/markReadMessage.test.d.ts +2 -0
- package/dist/messageRepository/observers/tests/markReadMessage.test.d.ts.map +1 -0
- package/dist/reactionRepository/events/onReactionAdded.d.ts.map +1 -1
- package/dist/subChannelRepository/events/onSubChannelCreated.d.ts.map +1 -1
- package/dist/subChannelRepository/utils/persistOptimisticUnreadInfo.d.ts +1 -1
- package/dist/subChannelRepository/utils/persistOptimisticUnreadInfo.d.ts.map +1 -1
- package/dist/utils/liveObject.d.ts.map +1 -1
- package/dist/utils/tests/dummy/message.d.ts +5 -4
- package/dist/utils/tests/dummy/message.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/@types/core/events.ts +5 -0
- package/src/@types/core/objectResolver.ts +14 -0
- package/src/@types/domains/channel.ts +12 -10
- package/src/analytic/api/syncEvent.ts +1 -3
- package/src/analytic/service/analytic/AnalyticsEngine.ts +6 -6
- package/src/analytic/service/analytic/AnalyticsEventCapturer.ts +9 -1
- package/src/analytic/service/analytic/AnalyticsEventSyncer.ts +9 -3
- package/src/channelRepository/api/getChannel.ts +5 -0
- package/src/channelRepository/api/getChannelByIds.ts +3 -0
- package/src/channelRepository/events/onChannelFetched.ts +28 -0
- package/src/channelRepository/observers/getChannels/ChannelLiveCollectionController.ts +86 -26
- package/src/channelRepository/observers/getChannels/ChannelQueryStreamController.ts +21 -8
- package/src/channelRepository/observers/observeChannel.ts +22 -9
- package/src/channelRepository/observers/observeChannels.ts +9 -9
- package/src/channelRepository/utils/prepareChannelPayload.ts +0 -13
- package/src/channelRepository/utils/prepateUnreadCountInfo.ts +20 -0
- package/src/channelRepository/utils/resolveUnreadInfoOnChannelEvent.ts +15 -0
- package/src/channelRepository/utils/tests/prepareChannelPayload.test.ts +2 -2
- package/src/client/api/login.ts +3 -1
- package/src/client/utils/ObjectResolver/objectResolverEngine.ts +143 -0
- package/src/client/utils/ObjectResolver/objectResolverEngineOnLoginHandler.ts +21 -0
- package/src/client/utils/onOffline.ts +15 -0
- package/src/marker/api/getUserMessageFeedMarkers.ts +3 -0
- package/src/marker/events/onUserFeedMarkerFetched.ts +37 -0
- package/src/marker/utils/addFlagSubChannelUnreadByChannelId.ts +19 -0
- package/src/marker/utils/addFlagSubChannelUnreadBySubChannelId.ts +13 -0
- package/src/messageRepository/observers/tests/markReadMessage.test.ts +225 -0
- package/src/reactionRepository/events/onReactionAdded.ts +1 -0
- package/src/subChannelRepository/events/onSubChannelCreated.ts +8 -1
- package/src/subChannelRepository/utils/persistOptimisticUnreadInfo.ts +24 -19
- package/src/utils/liveObject.ts +1 -0
- package/src/utils/tests/client.ts +1 -1
- package/src/utils/tests/dummy/channel.ts +4 -4
- package/src/utils/tests/dummy/message.ts +11 -2
|
@@ -56,25 +56,38 @@ export const observeChannel = <Events extends [Exclude<Amity.ChannelActionType,
|
|
|
56
56
|
const disposers: Amity.Unsubscriber[] = [];
|
|
57
57
|
|
|
58
58
|
disposers.push(
|
|
59
|
-
onChannelUpdated(data =>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
onChannelUpdated(data =>
|
|
60
|
+
router({ data, loading: false, origin: 'event' }, Amity.ChannelActionType.OnUpdate),
|
|
61
|
+
),
|
|
62
|
+
onChannelDeleted(data =>
|
|
63
|
+
router({ data, loading: false, origin: 'event' }, Amity.ChannelActionType.OnDelete),
|
|
64
|
+
),
|
|
65
|
+
onChannelJoined(data =>
|
|
66
|
+
router({ data, loading: false, origin: 'event' }, Amity.ChannelActionType.OnJoin),
|
|
67
|
+
),
|
|
68
|
+
onChannelLeft(data =>
|
|
69
|
+
router({ data, loading: false, origin: 'event' }, Amity.ChannelActionType.OnLeft),
|
|
70
|
+
),
|
|
71
|
+
onChannelMuted(data =>
|
|
72
|
+
router({ data, loading: false, origin: 'event' }, Amity.ChannelActionType.OnMute),
|
|
73
|
+
),
|
|
64
74
|
onChannelMemberAdded(data =>
|
|
65
|
-
router({ data, loading: false, origin: 'event' },
|
|
75
|
+
router({ data, loading: false, origin: 'event' }, Amity.ChannelActionType.OnMemberAdded),
|
|
66
76
|
),
|
|
67
77
|
onChannelMemberRemoved(data =>
|
|
68
|
-
router({ data, loading: false, origin: 'event' },
|
|
78
|
+
router({ data, loading: false, origin: 'event' }, Amity.ChannelActionType.OnMemberRemoved),
|
|
69
79
|
),
|
|
70
80
|
convertEventPayload(
|
|
71
81
|
onChannelMarkerFetched,
|
|
72
82
|
'entityId',
|
|
73
83
|
'channel',
|
|
74
|
-
)(data => router({ data, loading: false, origin: 'event' },
|
|
84
|
+
)(data => router({ data, loading: false, origin: 'event' }, Amity.ChannelActionType.OnUpdate)),
|
|
75
85
|
);
|
|
76
86
|
|
|
77
|
-
runQuery(
|
|
87
|
+
runQuery(
|
|
88
|
+
createQuery(getChannel, channelId),
|
|
89
|
+
result => result.data && router(result, Amity.ChannelActionType.OnFetch),
|
|
90
|
+
);
|
|
78
91
|
|
|
79
92
|
return () => {
|
|
80
93
|
log(`observeChannel(tmpid: ${timestamp}) > dispose`);
|
|
@@ -46,19 +46,19 @@ export const observeChannels = (
|
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
disposers.push(
|
|
49
|
-
onChannelCreated(data => router(data,
|
|
50
|
-
onChannelUpdated(data => router(data,
|
|
51
|
-
onChannelDeleted(data => router(data,
|
|
52
|
-
onChannelJoined(data => router(data,
|
|
53
|
-
onChannelLeft(data => router(data,
|
|
54
|
-
onChannelMuted(data => router(data,
|
|
55
|
-
onChannelMemberAdded(data => router(data,
|
|
56
|
-
onChannelMemberRemoved(data => router(data,
|
|
49
|
+
onChannelCreated(data => router(data, Amity.ChannelActionType.OnCreate)),
|
|
50
|
+
onChannelUpdated(data => router(data, Amity.ChannelActionType.OnUpdate)),
|
|
51
|
+
onChannelDeleted(data => router(data, Amity.ChannelActionType.OnDelete)),
|
|
52
|
+
onChannelJoined(data => router(data, Amity.ChannelActionType.OnJoin)),
|
|
53
|
+
onChannelLeft(data => router(data, Amity.ChannelActionType.OnLeft)),
|
|
54
|
+
onChannelMuted(data => router(data, Amity.ChannelActionType.OnMute)),
|
|
55
|
+
onChannelMemberAdded(data => router(data, Amity.ChannelActionType.OnMemberAdded)),
|
|
56
|
+
onChannelMemberRemoved(data => router(data, Amity.ChannelActionType.OnMemberRemoved)),
|
|
57
57
|
convertEventPayload(
|
|
58
58
|
onChannelMarkerFetched,
|
|
59
59
|
'entityId',
|
|
60
60
|
'channel',
|
|
61
|
-
)(data => router(data,
|
|
61
|
+
)(data => router(data, Amity.ChannelActionType.OnUpdate)),
|
|
62
62
|
);
|
|
63
63
|
|
|
64
64
|
return () => {
|
|
@@ -68,19 +68,6 @@ export const prepareChannelPayload = async (
|
|
|
68
68
|
const client = getActiveClient();
|
|
69
69
|
const networkPreviewSetting = await client.getMessagePreviewSetting(false);
|
|
70
70
|
|
|
71
|
-
// if consistent mode is enabled, persist the unread count info to the cache
|
|
72
|
-
if (client.isUnreadCountEnabled && client.getMarkerSyncConsistentMode()) {
|
|
73
|
-
const queryPayload = await getUserMessageFeedMakers(
|
|
74
|
-
rawPayload.channels.map(({ channelId }) => channelId),
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
const { feedMarkers, userFeedMarkers } = queryPayload;
|
|
78
|
-
persistUnreadCountInfo({
|
|
79
|
-
feedMarkers,
|
|
80
|
-
userFeedMarkers,
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
|
|
84
71
|
if (
|
|
85
72
|
options.isMessagePreviewUpdated &&
|
|
86
73
|
networkPreviewSetting !== Amity.MessagePreviewSetting.NO_MESSAGE_PREVIEW &&
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getActiveClient } from '~/client/api/activeClient';
|
|
2
|
+
import { getUserMessageFeedMakers } from '~/marker/api/getUserMessageFeedMarkers';
|
|
3
|
+
import { persistUnreadCountInfo } from '~/marker/utils/persistUnreadCountInfo';
|
|
4
|
+
|
|
5
|
+
export const prepareUnreadCountInfo = async (rawPayload: Amity.ChannelPayload) => {
|
|
6
|
+
const client = getActiveClient();
|
|
7
|
+
// if consistent mode is enabled, persist the unread count info to the cache
|
|
8
|
+
|
|
9
|
+
const queryPayload = await getUserMessageFeedMakers(
|
|
10
|
+
rawPayload.channels.map(({ channelId }) => channelId),
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const { feedMarkers, userFeedMarkers } = queryPayload;
|
|
14
|
+
persistUnreadCountInfo({
|
|
15
|
+
feedMarkers,
|
|
16
|
+
userFeedMarkers,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
client.log('channel/prepareUnreadCountInfo', rawPayload.channels);
|
|
20
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getActiveClient } from '~/client/api/activeClient';
|
|
2
|
+
import ObjectResolverEngine from '~/client/utils/ObjectResolver/objectResolverEngine';
|
|
3
|
+
|
|
4
|
+
export const resolveUnreadInfoOnChannelEvent = (
|
|
5
|
+
channel: Amity.InternalChannel,
|
|
6
|
+
callback: Amity.Listener<Amity.InternalChannel>,
|
|
7
|
+
) => {
|
|
8
|
+
const client = getActiveClient();
|
|
9
|
+
|
|
10
|
+
if (client.isUnreadCountEnabled && client.getMarkerSyncConsistentMode()) {
|
|
11
|
+
const objectResolverEngine = ObjectResolverEngine.getInstance();
|
|
12
|
+
|
|
13
|
+
objectResolverEngine.resolve(channel.channelId, Amity.ReferenceType.USER_MESSAGE_FEED_MARKER);
|
|
14
|
+
} else callback(channel);
|
|
15
|
+
};
|
|
@@ -60,9 +60,9 @@ const convertedChannelPayload: Amity.ProcessedChannelPayload = {
|
|
|
60
60
|
isUnreadCountSupport: true,
|
|
61
61
|
unreadCount: channelMarker1.unreadCount,
|
|
62
62
|
hasMentioned: channelMarker1.hasMentioned,
|
|
63
|
+
isMentioned: channelMarker1.hasMentioned,
|
|
63
64
|
messagePreviewId: undefined,
|
|
64
65
|
subChannelsUnreadCount: channelMarker1.unreadCount,
|
|
65
|
-
isMentioned: channelMarker1.hasMentioned,
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
68
|
...liveChannel,
|
|
@@ -70,9 +70,9 @@ const convertedChannelPayload: Amity.ProcessedChannelPayload = {
|
|
|
70
70
|
isUnreadCountSupport: false,
|
|
71
71
|
unreadCount: 0,
|
|
72
72
|
hasMentioned: false,
|
|
73
|
+
isMentioned: false,
|
|
73
74
|
messagePreviewId: undefined,
|
|
74
75
|
subChannelsUnreadCount: 0,
|
|
75
|
-
isMentioned: false,
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
78
|
...conversationChannel,
|
package/src/client/api/login.ts
CHANGED
|
@@ -10,7 +10,8 @@ import { markReadEngineOnLoginHandler } from '~/subChannelRepository/utils/markR
|
|
|
10
10
|
import { onUserDeleted } from '~/userRepository/events/onUserDeleted';
|
|
11
11
|
|
|
12
12
|
import analyticsEngineOnLoginHandler from '~/analytic/utils/analyticsEngineOnLoginHandler';
|
|
13
|
-
import readReceiptSyncEngineOnLoginHandler from '
|
|
13
|
+
import readReceiptSyncEngineOnLoginHandler from '~/client/utils/ReadReceiptSync/readReceiptSyncEngineOnLoginHandler';
|
|
14
|
+
import objectResolverEngineOnLoginHandler from '~/client/utils/ObjectResolver/objectResolverEngineOnLoginHandler';
|
|
14
15
|
import { logout } from './logout';
|
|
15
16
|
|
|
16
17
|
import { getActiveClient } from './activeClient';
|
|
@@ -198,6 +199,7 @@ export const login = async (
|
|
|
198
199
|
markReadEngineOnLoginHandler(),
|
|
199
200
|
analyticsEngineOnLoginHandler(),
|
|
200
201
|
readReceiptSyncEngineOnLoginHandler(),
|
|
202
|
+
objectResolverEngineOnLoginHandler(),
|
|
201
203
|
);
|
|
202
204
|
|
|
203
205
|
const markerSyncUnsubscriber = await startMarkerSync();
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { onOnline } from '../onOnline';
|
|
2
|
+
import { getChannelByIds } from '~/channelRepository/api/getChannelByIds';
|
|
3
|
+
|
|
4
|
+
import { onOffline } from '../onOffline';
|
|
5
|
+
import { getUserMessageFeedMakers } from '~/marker/api/getUserMessageFeedMarkers';
|
|
6
|
+
import { SECOND } from '~/utils/constants';
|
|
7
|
+
|
|
8
|
+
class ObjectResolverEngine {
|
|
9
|
+
private readonly TIMER_INTERVAL_MS = SECOND;
|
|
10
|
+
|
|
11
|
+
private readonly BUFFER_ID_LIMIT = 100;
|
|
12
|
+
|
|
13
|
+
private buffer: Amity.ObjectIdBuffer = {
|
|
14
|
+
[Amity.ReferenceType.CHANNEL]: [],
|
|
15
|
+
[Amity.ReferenceType.USER_MESSAGE_FEED_MARKER]: [],
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
private timer: NodeJS.Timer | undefined;
|
|
19
|
+
|
|
20
|
+
private isResolvingTask = false;
|
|
21
|
+
|
|
22
|
+
private connectionListener: (() => void)[] = [];
|
|
23
|
+
|
|
24
|
+
private isConnected = true;
|
|
25
|
+
|
|
26
|
+
constructor() {
|
|
27
|
+
this.addConnectionListener();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
startResolver() {
|
|
31
|
+
if (!this.timer) {
|
|
32
|
+
this.timer = setInterval(() => {
|
|
33
|
+
if (this.isConnected) this.resolveObjects();
|
|
34
|
+
}, this.TIMER_INTERVAL_MS);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
stopResolver() {
|
|
39
|
+
if (this.timer) {
|
|
40
|
+
clearInterval(this.timer);
|
|
41
|
+
this.timer = undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
resolve(id: string, referenceType: Amity.ReferenceType) {
|
|
46
|
+
const objectIdList = this.getBuffer(referenceType);
|
|
47
|
+
|
|
48
|
+
if (objectIdList.includes(id)) return;
|
|
49
|
+
|
|
50
|
+
if (objectIdList.length >= this.BUFFER_ID_LIMIT) {
|
|
51
|
+
objectIdList.shift(); // Remove first element of the array
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
objectIdList.push(id);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private addConnectionListener() {
|
|
58
|
+
if (this.connectionListener.length > 0) return;
|
|
59
|
+
this.connectionListener.push(
|
|
60
|
+
onOnline(() => {
|
|
61
|
+
this.isConnected = true;
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
this.connectionListener.push(
|
|
66
|
+
onOffline(() => {
|
|
67
|
+
this.isConnected = false;
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private removeConnectionListener() {
|
|
73
|
+
if (this.connectionListener.length > 0) this.connectionListener.forEach(listener => listener());
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private resolveObjects() {
|
|
77
|
+
if (this.isResolvingTask) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.isResolvingTask = true;
|
|
82
|
+
|
|
83
|
+
// Get first 100 ids for all reference type supported.
|
|
84
|
+
const channelIds = this.getBuffer(Amity.ReferenceType.CHANNEL);
|
|
85
|
+
const userMessageFeedMarkerIds = this.getBuffer(Amity.ReferenceType.USER_MESSAGE_FEED_MARKER);
|
|
86
|
+
|
|
87
|
+
// Clear buffer
|
|
88
|
+
this.clearBuffer();
|
|
89
|
+
|
|
90
|
+
// Send Requests.
|
|
91
|
+
// Incase of failure: Ignore
|
|
92
|
+
// Incase of success: Persist in Domain DB & Notify Live collection
|
|
93
|
+
if (channelIds.length > 0) getChannelByIds(channelIds);
|
|
94
|
+
if (userMessageFeedMarkerIds.length > 0) getUserMessageFeedMakers(userMessageFeedMarkerIds);
|
|
95
|
+
|
|
96
|
+
// After sending request
|
|
97
|
+
this.isResolvingTask = false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private clearBuffer() {
|
|
101
|
+
this.buffer = {
|
|
102
|
+
[Amity.ReferenceType.CHANNEL]: [],
|
|
103
|
+
[Amity.ReferenceType.USER_MESSAGE_FEED_MARKER]: [],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private getBuffer(referenceType: Amity.ReferenceType) {
|
|
108
|
+
return this.buffer[referenceType];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Session Management : SessionComponent
|
|
112
|
+
onSessionEstablished() {
|
|
113
|
+
this.startResolver();
|
|
114
|
+
this.addConnectionListener();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
onSessionDestroyed() {
|
|
118
|
+
// Stop timer
|
|
119
|
+
this.stopResolver();
|
|
120
|
+
|
|
121
|
+
// Clear buffer
|
|
122
|
+
this.clearBuffer();
|
|
123
|
+
|
|
124
|
+
// Reset state
|
|
125
|
+
this.isResolvingTask = false;
|
|
126
|
+
|
|
127
|
+
// remove connection listener
|
|
128
|
+
this.removeConnectionListener();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
onTokenExpired() {
|
|
132
|
+
this.stopResolver();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let instance: ObjectResolverEngine | null = null;
|
|
137
|
+
|
|
138
|
+
export default {
|
|
139
|
+
getInstance: () => {
|
|
140
|
+
if (!instance) instance = new ObjectResolverEngine();
|
|
141
|
+
return instance;
|
|
142
|
+
},
|
|
143
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { onSessionStateChange } from '~/client/events/onSessionStateChange';
|
|
2
|
+
import ObjectResolverEngine from './objectResolverEngine';
|
|
3
|
+
|
|
4
|
+
export default () => {
|
|
5
|
+
const objectResolverEngine = ObjectResolverEngine.getInstance();
|
|
6
|
+
objectResolverEngine.startResolver();
|
|
7
|
+
|
|
8
|
+
onSessionStateChange(state => {
|
|
9
|
+
if (state === Amity.SessionStates.ESTABLISHED) {
|
|
10
|
+
objectResolverEngine.onSessionEstablished();
|
|
11
|
+
} else if (state === Amity.SessionStates.TOKEN_EXPIRED) {
|
|
12
|
+
objectResolverEngine.onTokenExpired();
|
|
13
|
+
} else {
|
|
14
|
+
objectResolverEngine.onSessionDestroyed();
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return () => {
|
|
19
|
+
objectResolverEngine.onSessionDestroyed();
|
|
20
|
+
};
|
|
21
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const onOffline = (callback: () => void) => {
|
|
2
|
+
if (typeof window !== 'undefined' && window.addEventListener) {
|
|
3
|
+
window.addEventListener('offline', callback);
|
|
4
|
+
return () => window.removeEventListener('offline', callback);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
if (typeof document !== 'undefined' && document.addEventListener) {
|
|
8
|
+
document.addEventListener('offline', callback);
|
|
9
|
+
return () => document.removeEventListener('offline', callback);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Handle unsupported environment
|
|
13
|
+
console.error('Unsupported environment');
|
|
14
|
+
return () => console.error('Unsupported environment');
|
|
15
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getActiveClient } from '~/client/api/activeClient';
|
|
2
|
+
import { fireEvent } from '~/core/events';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* ```js
|
|
@@ -31,5 +32,7 @@ export const getUserMessageFeedMakers = async (
|
|
|
31
32
|
},
|
|
32
33
|
);
|
|
33
34
|
|
|
35
|
+
fireEvent('local.userMessageFeedMarker.fetched', { userMessageFeedMarker: data });
|
|
36
|
+
|
|
34
37
|
return data;
|
|
35
38
|
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { getActiveClient } from '~/client/api/activeClient';
|
|
2
|
+
import { createEventSubscriber } from '~/core/events';
|
|
3
|
+
import { persistUnreadCountInfo } from '../utils/persistUnreadCountInfo';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ```js
|
|
7
|
+
* import { onFeedMarkerUpdated } from '@amityco/ts-sdk'
|
|
8
|
+
* const dispose = onFeedMarkerUpdated(feedMarker => {
|
|
9
|
+
* // ...
|
|
10
|
+
* })
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Fired when an {@link Amity.UserMessageFeedMarkerPayload} has been updated
|
|
14
|
+
*
|
|
15
|
+
* @param callback The function to call when the event was fired
|
|
16
|
+
* @returns an {@link Amity.Unsubscriber} function to stop listening
|
|
17
|
+
*
|
|
18
|
+
* @category FeedMarker Events
|
|
19
|
+
*/
|
|
20
|
+
export const onUserFeedMarkerFetched = (
|
|
21
|
+
callback: Amity.Listener<Amity.UserMessageFeedMarkerPayload>,
|
|
22
|
+
): Amity.Unsubscriber => {
|
|
23
|
+
const client = getActiveClient();
|
|
24
|
+
|
|
25
|
+
const filter = (payload: Amity.Events['local.userMessageFeedMarker.fetched']) => {
|
|
26
|
+
// update sub channel unread info and channel unread info in cache
|
|
27
|
+
persistUnreadCountInfo(payload.userMessageFeedMarker);
|
|
28
|
+
callback(payload.userMessageFeedMarker);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return createEventSubscriber(
|
|
32
|
+
client,
|
|
33
|
+
'feedMarker/onUserFeedMarkerFetched',
|
|
34
|
+
'local.userMessageFeedMarker.fetched',
|
|
35
|
+
filter,
|
|
36
|
+
);
|
|
37
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { dropFromCache, pushToCache, queryCache } from '~/cache/api';
|
|
2
|
+
|
|
3
|
+
export const addFlagSubChannelUnreadByChannelId = (channelId: string) => {
|
|
4
|
+
const cacheKey = ['subChannelUnreadInfo', 'get'];
|
|
5
|
+
const cachedSubChannelUnread = queryCache<Amity.SubChannelUnreadInfo>(cacheKey);
|
|
6
|
+
|
|
7
|
+
if (cachedSubChannelUnread) {
|
|
8
|
+
const cachedTargetSubChannelUnread = cachedSubChannelUnread?.filter(
|
|
9
|
+
({ data }) => data.channelId === channelId,
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
cachedTargetSubChannelUnread.forEach(({ key, data }) => {
|
|
13
|
+
pushToCache(key, {
|
|
14
|
+
...data,
|
|
15
|
+
isDeleted: true,
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { pullFromCache, pushToCache } from '~/cache/api';
|
|
2
|
+
|
|
3
|
+
export const addFlagSubChannelUnreadBySubChannelId = (subChannelId: string) => {
|
|
4
|
+
const cacheKey = ['subChannelUnreadInfo', 'get', subChannelId];
|
|
5
|
+
const cachedSubChannelUnread = pullFromCache<Amity.SubChannelUnreadInfo>(cacheKey)?.data;
|
|
6
|
+
|
|
7
|
+
if (cachedSubChannelUnread) {
|
|
8
|
+
pushToCache(cacheKey, {
|
|
9
|
+
...cachedSubChannelUnread,
|
|
10
|
+
isDeleted: true,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
};
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import {
|
|
2
|
+
client,
|
|
3
|
+
generateMessageMarker,
|
|
4
|
+
generateRawChannel,
|
|
5
|
+
generateRawMessage,
|
|
6
|
+
messages,
|
|
7
|
+
pause,
|
|
8
|
+
generateRawSubChannel,
|
|
9
|
+
} from '~/utils/tests';
|
|
10
|
+
import { getMessages } from '../getMessages';
|
|
11
|
+
import { getChannels } from '~/channelRepository/observers/getChannels';
|
|
12
|
+
|
|
13
|
+
import { enableUnreadCount } from '~/client';
|
|
14
|
+
|
|
15
|
+
import ReadReceiptSyncEngine from '~/client/utils/ReadReceiptSync/readReceiptSyncEngine';
|
|
16
|
+
import { ingestInCache } from '~/cache/api/ingestInCache';
|
|
17
|
+
import { pushToCache } from '~/cache/api';
|
|
18
|
+
|
|
19
|
+
describe('readReceiptSyncEngine', () => {
|
|
20
|
+
const channelsReturn: Amity.ChannelPayload = {
|
|
21
|
+
channels: [generateRawChannel({ channelId: 'channel-id' })],
|
|
22
|
+
channelUsers: [],
|
|
23
|
+
messagePreviews: [],
|
|
24
|
+
messageFeedsInfo: [],
|
|
25
|
+
users: [],
|
|
26
|
+
files: [],
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const channelMarkerReturn: Amity.ChannelMarkerPayload = {
|
|
30
|
+
userEntityMarkers: [],
|
|
31
|
+
userMarkers: [],
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const messageReturn: Amity.MessagePayload = {
|
|
35
|
+
messages: [
|
|
36
|
+
generateRawMessage({
|
|
37
|
+
messageId: messages.page1[0],
|
|
38
|
+
messageFeedId: 'channel-id',
|
|
39
|
+
channelId: 'channel-id',
|
|
40
|
+
}),
|
|
41
|
+
],
|
|
42
|
+
messageFeeds: [],
|
|
43
|
+
reactions: [],
|
|
44
|
+
users: [],
|
|
45
|
+
files: [],
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const messasgeMarkersReturn: Amity.MessageMarkerPayload = {
|
|
49
|
+
contentMarkers: [
|
|
50
|
+
generateMessageMarker({
|
|
51
|
+
contentId: messages.page1[0],
|
|
52
|
+
feedId: messageReturn.messages[0].messageFeedId,
|
|
53
|
+
creatorId: messageReturn.messages[0].creatorId,
|
|
54
|
+
}),
|
|
55
|
+
],
|
|
56
|
+
feedMarkers: [],
|
|
57
|
+
userMarkers: [],
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const userMessageFeedMarkersReturn: Amity.UserMessageFeedMarkerPayload = {
|
|
61
|
+
feedMarkers: [
|
|
62
|
+
{
|
|
63
|
+
feedId: 'channel-id',
|
|
64
|
+
entityId: 'channel-id',
|
|
65
|
+
lastSegment: 0,
|
|
66
|
+
isDeleted: false,
|
|
67
|
+
createdAt: new Date().toISOString(),
|
|
68
|
+
updatedAt: new Date().toISOString(),
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
userFeedMarkers: [
|
|
72
|
+
{
|
|
73
|
+
userId: 'user-id',
|
|
74
|
+
entityId: 'channel-id',
|
|
75
|
+
feedId: 'channel-id',
|
|
76
|
+
readToSegment: 0,
|
|
77
|
+
deliveredToSegment: 0,
|
|
78
|
+
unreadCount: 0,
|
|
79
|
+
oldUnreadCount: 0,
|
|
80
|
+
lastMentionSegment: 0,
|
|
81
|
+
isMentioned: false,
|
|
82
|
+
createdAt: new Date().toISOString(),
|
|
83
|
+
updatedAt: new Date().toISOString(),
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
const { subChannelId } = messages;
|
|
88
|
+
|
|
89
|
+
const rawNewMessage = generateRawMessage({
|
|
90
|
+
messageId: 'new-message-id',
|
|
91
|
+
messageFeedId: 'channel-id',
|
|
92
|
+
channelPublicId: 'channel-id',
|
|
93
|
+
channelId: 'channel-id',
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
client.use();
|
|
97
|
+
client.http.get = jest.fn().mockImplementation(params => {
|
|
98
|
+
if (params.includes('/api/v5/messages')) return { data: { ...messageReturn } };
|
|
99
|
+
if (params.includes('/api/v3/channels')) return { data: { ...channelsReturn } };
|
|
100
|
+
if (params.includes('/api/v1/markers/user-message-feed'))
|
|
101
|
+
return { data: { ...userMessageFeedMarkersReturn } };
|
|
102
|
+
if (params.includes('/api/v1/markers/channels'))
|
|
103
|
+
return {
|
|
104
|
+
data: { ...channelMarkerReturn },
|
|
105
|
+
};
|
|
106
|
+
if (params.includes('/api/v1/markers/messages'))
|
|
107
|
+
return {
|
|
108
|
+
data: { ...messasgeMarkersReturn },
|
|
109
|
+
};
|
|
110
|
+
return { data: {} };
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should increase unreadCount when a message is received', async () => {
|
|
114
|
+
enableUnreadCount();
|
|
115
|
+
|
|
116
|
+
const callbackChannels = jest.fn();
|
|
117
|
+
|
|
118
|
+
getChannels({}, callbackChannels);
|
|
119
|
+
|
|
120
|
+
await pause();
|
|
121
|
+
|
|
122
|
+
client.emitter.emit('message.created', {
|
|
123
|
+
messages: [rawNewMessage],
|
|
124
|
+
messageFeeds: [
|
|
125
|
+
{
|
|
126
|
+
...generateRawSubChannel({
|
|
127
|
+
channelId: channelsReturn.channels[0].channelId,
|
|
128
|
+
messageFeedId: channelsReturn.channels[0].channelId,
|
|
129
|
+
name: 'general',
|
|
130
|
+
}),
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
users: [],
|
|
134
|
+
files: [],
|
|
135
|
+
reactions: [],
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
await pause();
|
|
139
|
+
|
|
140
|
+
expect(callbackChannels.mock.calls[2][0].data[0].unreadCount).toBe(1);
|
|
141
|
+
expect(callbackChannels.mock.calls[2][0].data[0].subChannelsUnreadCount).toBe(1);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should mark a message as read', async () => {
|
|
145
|
+
const readReceiptSyncEngine = ReadReceiptSyncEngine.getInstance();
|
|
146
|
+
|
|
147
|
+
readReceiptSyncEngine.startSyncReadReceipt();
|
|
148
|
+
|
|
149
|
+
const spyPut = jest.spyOn(client.http, 'put').mockResolvedValue({});
|
|
150
|
+
const callback = jest.fn();
|
|
151
|
+
|
|
152
|
+
getMessages({ subChannelId }, callback);
|
|
153
|
+
|
|
154
|
+
callback.mock.calls[1][0].data[0].markRead();
|
|
155
|
+
|
|
156
|
+
await pause(2000);
|
|
157
|
+
|
|
158
|
+
expect(spyPut).toHaveBeenCalledWith(`/api/v1/markers/message-feeds/${subChannelId}/mark-read`, {
|
|
159
|
+
readToSegment: 1,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
readReceiptSyncEngine.onSessionDestroyed();
|
|
163
|
+
|
|
164
|
+
client.emitter.emit('message.created', {
|
|
165
|
+
messages: [rawNewMessage],
|
|
166
|
+
messageFeeds: [
|
|
167
|
+
{
|
|
168
|
+
...generateRawSubChannel({
|
|
169
|
+
channelId: channelsReturn.channels[0].channelId,
|
|
170
|
+
messageFeedId: channelsReturn.channels[0].channelId,
|
|
171
|
+
name: 'general',
|
|
172
|
+
}),
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
users: [],
|
|
176
|
+
files: [],
|
|
177
|
+
reactions: [],
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it.only('should increase message readCount when a message is marked as read', async () => {
|
|
182
|
+
const callback = jest.fn();
|
|
183
|
+
getMessages({ subChannelId: 'channel-id' }, callback);
|
|
184
|
+
|
|
185
|
+
pushToCache(['messageMarker', 'get', 'channel-id#message11#user-id'], {
|
|
186
|
+
feedId: 'channel-id',
|
|
187
|
+
contentId: 'message11',
|
|
188
|
+
creatorId: 'user-id',
|
|
189
|
+
readCount: 0,
|
|
190
|
+
deliveredCount: 0,
|
|
191
|
+
createdAt: new Date().toISOString(),
|
|
192
|
+
updatedAt: new Date().toISOString(),
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
await pause();
|
|
196
|
+
|
|
197
|
+
client.emitter.emit('marker.marked-message', {
|
|
198
|
+
contentMarkers: [
|
|
199
|
+
{
|
|
200
|
+
feedId: 'channel-id',
|
|
201
|
+
contentId: 'message11',
|
|
202
|
+
creatorId: 'user-id',
|
|
203
|
+
readCount: 1,
|
|
204
|
+
deliveredCount: 0,
|
|
205
|
+
createdAt: new Date().toISOString(),
|
|
206
|
+
updatedAt: new Date().toISOString(),
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
feedMarkers: [
|
|
210
|
+
{
|
|
211
|
+
feedId: 'channel-id',
|
|
212
|
+
entityId: 'channel-id',
|
|
213
|
+
lastSegment: 1,
|
|
214
|
+
isDeleted: false,
|
|
215
|
+
createdAt: new Date().toISOString(),
|
|
216
|
+
updatedAt: new Date().toISOString(),
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
await pause();
|
|
222
|
+
|
|
223
|
+
expect(callback.mock.calls[2][0].data[0].readCount).toBe(1);
|
|
224
|
+
});
|
|
225
|
+
});
|