@amityco/ts-sdk 7.1.1-e887d15f.0 → 7.2.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.
Files changed (43) hide show
  1. package/.env +26 -26
  2. package/dist/@types/domains/channel.d.ts +2 -2
  3. package/dist/@types/domains/channel.d.ts.map +1 -1
  4. package/dist/@types/domains/client.d.ts +1 -0
  5. package/dist/@types/domains/client.d.ts.map +1 -1
  6. package/dist/channelRepository/events/onChannelDeleted.d.ts.map +1 -1
  7. package/dist/channelRepository/internalApi/getTotalChannelsUnread.d.ts +11 -0
  8. package/dist/channelRepository/internalApi/getTotalChannelsUnread.d.ts.map +1 -0
  9. package/dist/channelRepository/observers/getTotalChannelsUnread.d.ts +20 -0
  10. package/dist/channelRepository/observers/getTotalChannelsUnread.d.ts.map +1 -0
  11. package/dist/channelRepository/observers/index.d.ts +1 -0
  12. package/dist/channelRepository/observers/index.d.ts.map +1 -1
  13. package/dist/channelRepository/utils/prepareChannelPayload.d.ts.map +1 -1
  14. package/dist/client/api/createClient.d.ts +1 -0
  15. package/dist/client/api/createClient.d.ts.map +1 -1
  16. package/dist/client/utils/ReadReceiptSync/readReceiptSyncEngine.d.ts.map +1 -1
  17. package/dist/client/utils/endpoints.d.ts +1 -0
  18. package/dist/client/utils/endpoints.d.ts.map +1 -1
  19. package/dist/client/utils/setClientToken.d.ts.map +1 -1
  20. package/dist/commentRepository/events/utils.d.ts.map +1 -1
  21. package/dist/index.cjs.js +224 -25
  22. package/dist/index.esm.js +224 -25
  23. package/dist/index.umd.js +4 -4
  24. package/dist/messageRepository/events/onMessageCreated.d.ts.map +1 -1
  25. package/dist/messageRepository/observers/getMessage.d.ts.map +1 -1
  26. package/package.json +1 -1
  27. package/src/@types/domains/channel.ts +2 -2
  28. package/src/@types/domains/client.ts +1 -0
  29. package/src/channelRepository/events/onChannelDeleted.ts +9 -2
  30. package/src/channelRepository/internalApi/getTotalChannelsUnread.ts +38 -0
  31. package/src/channelRepository/observers/getTotalChannelsUnread.ts +129 -0
  32. package/src/channelRepository/observers/index.ts +1 -0
  33. package/src/channelRepository/utils/prepareChannelPayload.ts +21 -10
  34. package/src/client/api/createClient.ts +4 -1
  35. package/src/client/utils/ReadReceiptSync/readReceiptSyncEngine.ts +5 -1
  36. package/src/client/utils/endpoints.ts +1 -0
  37. package/src/client/utils/setClientToken.ts +8 -0
  38. package/src/commentRepository/events/utils.ts +73 -0
  39. package/src/fileRepository/api/uploadFile.ts +1 -1
  40. package/src/fileRepository/api/uploadImage.ts +1 -1
  41. package/src/fileRepository/api/uploadVideo.ts +1 -1
  42. package/src/messageRepository/events/onMessageCreated.ts +19 -9
  43. package/src/messageRepository/observers/getMessage.ts +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"onMessageCreated.d.ts","sourceRoot":"","sources":["../../../src/messageRepository/events/onMessageCreated.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,oBAAoB,aACrB,MAAM,QAAQ,CAAC,MAAM,eAAe,CAAC,KAC9C,MAAM,YAgER,CAAC;AAEF,eAAO,MAAM,qBAAqB,aACtB,MAAM,QAAQ,CAAC,MAAM,eAAe,CAAC,KAC9C,MAAM,YAoBR,CAAC"}
1
+ {"version":3,"file":"onMessageCreated.d.ts","sourceRoot":"","sources":["../../../src/messageRepository/events/onMessageCreated.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,oBAAoB,aACrB,MAAM,QAAQ,CAAC,MAAM,eAAe,CAAC,KAC9C,MAAM,YA0ER,CAAC;AAEF,eAAO,MAAM,qBAAqB,aACtB,MAAM,QAAQ,CAAC,MAAM,eAAe,CAAC,KAC9C,MAAM,YAoBR,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"getMessage.d.ts","sourceRoot":"","sources":["../../../src/messageRepository/observers/getMessage.ts"],"names":[],"mappings":"AAoBA;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,UAAU,cACV,MAAM,OAAO,CAAC,WAAW,CAAC,YAC3B,MAAM,kBAAkB,CAAC,MAAM,OAAO,CAAC,KAChD,MAAM,YAmBR,CAAC"}
1
+ {"version":3,"file":"getMessage.d.ts","sourceRoot":"","sources":["../../../src/messageRepository/observers/getMessage.ts"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,UAAU,cACV,MAAM,OAAO,CAAC,WAAW,CAAC,YAC3B,MAAM,kBAAkB,CAAC,MAAM,OAAO,CAAC,KAChD,MAAM,YAmBR,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amityco/ts-sdk",
3
- "version": "7.1.1-e887d15f.0",
3
+ "version": "7.2.0",
4
4
  "license": "CC-BY-ND-4.0",
5
5
  "author": "amity.co <developers@amity.co> (https://amity.co)",
6
6
  "description": "Amity Social Cloud Typescript SDK",
@@ -145,9 +145,9 @@ declare global {
145
145
  channelId: Amity.Channel['channelId'];
146
146
  unreadCount: number;
147
147
  isMentioned: boolean;
148
- readToSegment: number;
148
+ readToSegment: number | null;
149
149
  lastSegment: number;
150
- lastMentionSegment: number;
150
+ lastMentionedSegment: number | null;
151
151
  isDeleted: boolean;
152
152
  };
153
153
  }
@@ -36,6 +36,7 @@ declare global {
36
36
 
37
37
  log: Logger;
38
38
  http: AxiosInstance;
39
+ upload: AxiosInstance;
39
40
  mqtt?: Amity.MqttClient;
40
41
  ws?: SocketIOClient.Socket;
41
42
  emitter: Emitter<Amity.Events>;
@@ -6,7 +6,7 @@ import { ingestInCache } from '~/cache/api/ingestInCache';
6
6
  import { prepareChannelPayload } from '../utils/prepareChannelPayload';
7
7
  import { addFlagIsDeletedSubChannelUnreadByChannelId } from '~/marker/utils/addFlagIsDeletedSubChannelUnreadByChannelId';
8
8
  import { deleteChannelUnreadByChannelId } from '../../marker/utils/deleteChannelUnreadByChannelId';
9
- import { dropFromCache } from '~/cache/api';
9
+ import { dropFromCache, pullFromCache, pushToCache } from '~/cache/api';
10
10
 
11
11
  type CallbackFn = (channel: Amity.StaticInternalChannel) => void;
12
12
  const callbacks: CallbackFn[] = [];
@@ -37,7 +37,14 @@ export const onChannelDeleted = (callback: Amity.Listener<Amity.StaticInternalCh
37
37
  addFlagIsDeletedSubChannelUnreadByChannelId(channel.channelId);
38
38
  deleteChannelUnreadByChannelId(channel.channelId);
39
39
  } else if (isLegacyUnreadCount) {
40
- dropFromCache(['channelUnread', 'get', channel.channelId]);
40
+ const cacheKey = ['channelUnread', 'get', channel.channelId];
41
+ const cache = pullFromCache<Amity.ChannelUnread>(cacheKey);
42
+ if (cache) {
43
+ pushToCache(cacheKey, {
44
+ ...cache,
45
+ isDeleted: true,
46
+ });
47
+ }
41
48
  }
42
49
  });
43
50
 
@@ -0,0 +1,38 @@
1
+ import { queryCache } from '~/cache/api';
2
+ import { getActiveClient } from '~/client/api/activeClient';
3
+
4
+ /**
5
+ *
6
+ * Calculate user unread from {@link Amity.ChannelUnread} objects
7
+ *
8
+ * @returns the {@link Amity.UserUnread} objects
9
+ *
10
+ * @category Channel API
11
+ * @async
12
+ */
13
+
14
+ export const getTotalChannelsUnread = (): Amity.Cached<Amity.UserUnread> => {
15
+ const client = getActiveClient();
16
+ client.log('channel/getTotalChannelsUnread.locally');
17
+
18
+ const cachedChannelsUnread =
19
+ queryCache<Amity.ChannelUnread>(['channelUnread', 'get'])?.filter(({ data }) => {
20
+ return !data.isDeleted;
21
+ }) || [];
22
+
23
+ const totalChannelsUnread: Amity.UserUnread = cachedChannelsUnread?.reduce(
24
+ (acc, { data }) => {
25
+ acc.unreadCount += data.unreadCount;
26
+ acc.isMentioned = acc.isMentioned || data.isMentioned;
27
+ return acc;
28
+ },
29
+ { unreadCount: 0, isMentioned: false as boolean },
30
+ ) || { unreadCount: 0, isMentioned: false };
31
+
32
+ const cachedAt = client.cache && Date.now();
33
+
34
+ return {
35
+ data: totalChannelsUnread,
36
+ cachedAt,
37
+ };
38
+ };
@@ -0,0 +1,129 @@
1
+ import { getActiveUser } from '~/client/api/activeUser';
2
+ import { getTotalChannelsUnread as _getTotalChannelsUnread } from '../internalApi/getTotalChannelsUnread';
3
+ import { onChannelUnreadUpdatedLocal } from '../events/onChannelUnreadUpdatedLocal';
4
+ import { ASCApiError, ASCError } from '~/core/errors';
5
+ import { getActiveClient } from '~/client';
6
+ import { convertGetterPropsToStatic } from '~/utils/object';
7
+ import { createQuery, runQuery } from '~/core/query';
8
+ import {
9
+ UNSYNCED_OBJECT_CACHED_AT_MESSAGE,
10
+ UNSYNCED_OBJECT_CACHED_AT_VALUE,
11
+ } from '~/utils/constants';
12
+ import { isEqual } from '~/utils/isEqual';
13
+
14
+ /* begin_public_function
15
+ id: totalChannelsUnread.get
16
+ */
17
+ /**
18
+ * ```js
19
+ * import { ChannelRepository } from '@amityco/ts-sdk';
20
+ *
21
+ * let totalChannelsUnread;
22
+ *
23
+ * const unsubscribe = ChannelRepository.getTotalChannelsUnread(response => {
24
+ * unread = response.data;
25
+ * });
26
+ * ```
27
+ *
28
+ * Observe all mutation on a given {@link Amity.UserUnread}
29
+ *
30
+ * @returns An {@link Amity.UserUnread} function to run when willing to stop observing the message
31
+ *
32
+ * @category User Unread Live Object
33
+ *
34
+ */
35
+
36
+ export const getTotalChannelsUnread = (
37
+ callback: Amity.LiveObjectCallback<Amity.UserUnread | undefined>,
38
+ ): Amity.Unsubscriber => {
39
+ const { _id: userId } = getActiveUser();
40
+
41
+ if (!userId)
42
+ throw new ASCError(
43
+ 'The _id has not been defined in ActiveUser',
44
+ Amity.ClientError.UNKNOWN_ERROR,
45
+ Amity.ErrorLevel.ERROR,
46
+ );
47
+
48
+ const { log, cache } = getActiveClient();
49
+
50
+ if (!cache) {
51
+ console.log('For using Live Object feature you need to enable Cache!');
52
+ }
53
+
54
+ const timestamp = Date.now();
55
+ log(`liveTotalChannelsUnread(tmpid: ${timestamp}) > listen`);
56
+
57
+ const disposers: Amity.Unsubscriber[] = [];
58
+
59
+ let isUnsyncedModel = false; // for messages
60
+
61
+ let model: Amity.UserUnread | undefined;
62
+
63
+ const dispatcher = (data: Amity.LiveObject<Amity.UserUnread | undefined>) => {
64
+ const { data: userUnread } = data;
65
+
66
+ const callbackModel = userUnread
67
+ ? {
68
+ unreadCount: userUnread.unreadCount,
69
+ isMentioned: userUnread.isMentioned,
70
+ }
71
+ : undefined;
72
+
73
+ model = callbackModel ? convertGetterPropsToStatic(callbackModel) : callbackModel;
74
+
75
+ callback({
76
+ data: callbackModel
77
+ ? { ...callbackModel, isMentioned: callbackModel.isMentioned }
78
+ : callbackModel,
79
+
80
+ loading: data.loading,
81
+ error: data.error,
82
+ });
83
+ };
84
+
85
+ const realtimeRouter = (userUnread: Amity.UserUnread) => {
86
+ if (isEqual(model, userUnread)) return;
87
+
88
+ dispatcher({
89
+ loading: false,
90
+ data: userUnread,
91
+ });
92
+ };
93
+
94
+ const onFetch = () => {
95
+ const query = createQuery(async () => _getTotalChannelsUnread());
96
+
97
+ runQuery(query, ({ error, data, loading, origin, cachedAt }) => {
98
+ if (cachedAt === UNSYNCED_OBJECT_CACHED_AT_VALUE) {
99
+ dispatcher({
100
+ data,
101
+ origin,
102
+ loading: false,
103
+ error: new ASCApiError(
104
+ UNSYNCED_OBJECT_CACHED_AT_MESSAGE,
105
+ Amity.ClientError.DISALOOW_UNSYNCED_OBJECT,
106
+ Amity.ErrorLevel.ERROR,
107
+ ),
108
+ });
109
+
110
+ isUnsyncedModel = true;
111
+ disposers.forEach(fn => fn());
112
+ } else if (!isUnsyncedModel) {
113
+ dispatcher({ loading, data, origin, error });
114
+ }
115
+
116
+ if (error) {
117
+ disposers.forEach(fn => fn());
118
+ }
119
+ });
120
+ };
121
+
122
+ disposers.push(onChannelUnreadUpdatedLocal(realtimeRouter));
123
+
124
+ onFetch();
125
+
126
+ return () => {
127
+ disposers.forEach(fn => fn());
128
+ };
129
+ };
@@ -1,2 +1,3 @@
1
1
  export * from './getChannel';
2
2
  export * from './getChannels';
3
+ export * from './getTotalChannelsUnread';
@@ -58,23 +58,34 @@ const updateChannelUnread = ({
58
58
  }) => {
59
59
  for (let i = 0; i < channels.length; i += 1) {
60
60
  const cacheKey = ['channelUnread', 'get', channels[i].channelId];
61
- const { readToSegment, lastMentionedSegment } = channelUsers.find(
61
+ const channelUser = channelUsers.find(
62
62
  channelUser =>
63
63
  channelUser.channelId === channels[i].channelId && channelUser.userId === currentUserId,
64
- ) || {
65
- readToSegment: 0,
66
- lastMentionedSegment: 0,
67
- };
64
+ );
65
+
66
+ let unreadCount = 0;
67
+ let readToSegment = null;
68
+ let lastMentionedSegment = null;
69
+ let isMentioned = false;
70
+
71
+ if (channelUser) {
72
+ readToSegment = channelUser.readToSegment;
73
+ lastMentionedSegment = channelUser.lastMentionedSegment;
74
+ unreadCount = Math.max(channels[i].messageCount - readToSegment, 0);
75
+ isMentioned = lastMentionedSegment > readToSegment;
76
+ }
68
77
 
69
- pushToCache(cacheKey, {
78
+ const cacheChannelUnread: Amity.ChannelUnread = {
70
79
  channelId: channels[i].channelId,
71
80
  lastSegment: channels[i].messageCount,
72
81
  readToSegment,
73
82
  lastMentionedSegment,
74
- unreadCount: channels[i].messageCount - readToSegment,
75
- isMentioned: lastMentionedSegment > readToSegment,
76
- isDeleted: channels[i].isDeleted,
77
- });
83
+ unreadCount,
84
+ isMentioned,
85
+ isDeleted: channels[i].isDeleted || false,
86
+ };
87
+
88
+ pushToCache(cacheKey, cacheChannelUnread);
78
89
  }
79
90
  };
80
91
 
@@ -50,7 +50,7 @@ export const createClient = (
50
50
  rteEnabled = true,
51
51
  }: {
52
52
  debugSession?: string;
53
- apiEndpoint?: { http?: string; mqtt?: string };
53
+ apiEndpoint?: { http?: string; mqtt?: string; upload?: string };
54
54
  prefixDeviceIdKey?: string;
55
55
  rteEnabled?: boolean;
56
56
  } = {},
@@ -63,9 +63,11 @@ export const createClient = (
63
63
  });
64
64
 
65
65
  const httpEndpoint = apiEndpoint?.http ?? computeUrl('http', apiRegion);
66
+ const uploadEndpoint = apiEndpoint?.upload ?? computeUrl('upload', apiRegion);
66
67
  const mqttEndpoint = apiEndpoint?.mqtt ?? computeUrl('mqtt', apiRegion);
67
68
 
68
69
  const http = createHttpTransport(httpEndpoint);
70
+ const upload = createHttpTransport(uploadEndpoint);
69
71
 
70
72
  let ws;
71
73
  let mqtt;
@@ -105,6 +107,7 @@ export const createClient = (
105
107
  http,
106
108
  ws,
107
109
  mqtt,
110
+ upload,
108
111
  emitter,
109
112
 
110
113
  /*
@@ -158,7 +158,11 @@ export class MessageReadReceiptSyncEngine {
158
158
  const cacheKey = ['channelUnread', 'get', channelId];
159
159
  const channelUnread = pullFromCache<Amity.ChannelUnread>(cacheKey)?.data;
160
160
 
161
- if (channelUnread && segment > channelUnread.readToSegment) {
161
+ if (
162
+ typeof channelUnread?.readToSegment === 'number' &&
163
+ channelUnread &&
164
+ segment > channelUnread.readToSegment
165
+ ) {
162
166
  channelUnread.readToSegment = segment;
163
167
  channelUnread.unreadCount = Math.max(channelUnread.lastSegment - segment, 0);
164
168
 
@@ -6,6 +6,7 @@ export const API_REGIONS = {
6
6
 
7
7
  const URLS = {
8
8
  http: 'https://apix.{region}.amity.co',
9
+ upload: 'https://upload.{region}.amity.co',
9
10
  mqtt: 'wss://sse.{region}.amity.co:443/mqtt',
10
11
  } as const;
11
12
 
@@ -30,6 +30,14 @@ export const setClientToken = async (params: Parameters<typeof getToken>[0]) =>
30
30
  isUserDeleted: false,
31
31
  };
32
32
 
33
+ client.upload.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
34
+
35
+ client.upload.defaults.metadata = {
36
+ tokenExpiry: expiresAt,
37
+ isGlobalBanned: false,
38
+ isUserDeleted: false,
39
+ };
40
+
33
41
  // manually setup the token for ws transport
34
42
  if (client.ws) client.ws.io.opts.query = { token: accessToken };
35
43
 
@@ -53,6 +53,36 @@ export const createCommentEventSubscriber = (
53
53
  }
54
54
  }
55
55
  }
56
+ }
57
+
58
+ if (['comment.deleted'].includes(event)) {
59
+ // NOTE: skip deleting comment to parent comment children if it's the same user since we use the local event to update instead.
60
+ if (event === 'comment.deleted' && comment.data.userId === client.userId) return;
61
+
62
+ if (comments[0].parentId) {
63
+ const parentComment = pullFromCache<Amity.InternalComment>([
64
+ 'comment',
65
+ 'get',
66
+ comments[0].parentId,
67
+ ]);
68
+
69
+ if (parentComment?.data) {
70
+ // Remove deleted comment in parent childComment if still exists
71
+ if (parentComment.data.children.includes(comments[0].commentId)) {
72
+ const newParentComment = {
73
+ ...parentComment.data,
74
+ childrenNumber: parentComment.data.childrenNumber - 1,
75
+ children: [
76
+ ...new Set([
77
+ ...parentComment.data.children.filter(id => id !== comments[0].commentId),
78
+ ]),
79
+ ],
80
+ };
81
+
82
+ pushToCache(['comment', 'get', comments[0].parentId], newParentComment);
83
+ }
84
+ }
85
+ }
56
86
 
57
87
  const queries = queryCache<Amity.InternalComment[]>(['comment', 'query'])?.filter(
58
88
  ({ key }) => (key[2] as Amity.QueryComments)?.referenceId === comment.data.referenceId,
@@ -135,6 +165,49 @@ export const createLocalCommentEventSubscriber = (
135
165
  queries?.map(({ key, data }) => upsertInCache(key, data as any, { cachedAt: -1 }));
136
166
  }
137
167
 
168
+ if (['local.comment.deleted'].includes(event)) {
169
+ if (comments[0].parentId) {
170
+ const parentComment = pullFromCache<Amity.InternalComment>([
171
+ 'comment',
172
+ 'get',
173
+ comments[0].parentId,
174
+ ]);
175
+
176
+ if (parentComment?.data) {
177
+ // Remove deleted comment in parent childComment if still exists
178
+ if (parentComment.data.children.includes(comments[0].commentId)) {
179
+ const newParentComment = {
180
+ ...parentComment.data,
181
+ childrenNumber: parentComment.data.childrenNumber - 1,
182
+ children: [
183
+ ...new Set([
184
+ ...parentComment.data.children.filter(id => id !== comments[0].commentId),
185
+ ]),
186
+ ],
187
+ };
188
+ pushToCache(['comment', 'get', comments[0].parentId], newParentComment);
189
+
190
+ setTimeout(() => {
191
+ // NOTE: This is workaround solution for emitting event not work properly.
192
+ fireEvent('comment.updated', {
193
+ comments: [newParentComment],
194
+ commentChildren: [],
195
+ files: [],
196
+ users: [],
197
+ communityUsers: [],
198
+ });
199
+ }, 200);
200
+ }
201
+ }
202
+ }
203
+
204
+ const queries = queryCache<Amity.InternalComment[]>(['comment', 'query'])?.filter(
205
+ ({ key }) => (key[2] as Amity.QueryComments)?.referenceId === comment.data.referenceId,
206
+ );
207
+
208
+ queries?.map(({ key, data }) => upsertInCache(key, data as any, { cachedAt: -1 }));
209
+ }
210
+
138
211
  callback(LinkedObject.comment(comment.data));
139
212
  }
140
213
  }
@@ -42,7 +42,7 @@ export const uploadFile = async <T extends Amity.FileType = any>(
42
42
  ? (formData as any).getHeaders()
43
43
  : { 'content-type': 'multipart/form-data' };
44
44
 
45
- const { data } = await client.http.post<Amity.CreateFilePayload<T>>('/api/v4/files', formData, {
45
+ const { data } = await client.upload.post<Amity.CreateFilePayload<T>>('/api/v4/files', formData, {
46
46
  headers,
47
47
  onUploadProgress({ loaded, total = 100 }) {
48
48
  onProgress && onProgress(Math.round((loaded * 100) / total));
@@ -42,7 +42,7 @@ export const uploadImage = async (
42
42
  ? (formData as any).getHeaders()
43
43
  : { 'content-type': 'multipart/form-data' };
44
44
 
45
- const { data } = await client.http.post<Amity.CreateFilePayload<'image'>>(
45
+ const { data } = await client.upload.post<Amity.CreateFilePayload<'image'>>(
46
46
  '/api/v4/images',
47
47
  formData,
48
48
  {
@@ -48,7 +48,7 @@ export const uploadVideo = async (
48
48
  ? (formData as any).getHeaders()
49
49
  : { 'content-type': 'multipart/form-data' };
50
50
 
51
- const { data } = await client.http.post<Amity.CreateFilePayload<'video'>>(
51
+ const { data } = await client.upload.post<Amity.CreateFilePayload<'video'>>(
52
52
  '/api/v4/videos',
53
53
  formData,
54
54
  {
@@ -1,5 +1,5 @@
1
1
  import { getActiveClient } from '~/client/api/activeClient';
2
- import { createEventSubscriber } from '~/core/events';
2
+ import { createEventSubscriber, fireEvent } from '~/core/events';
3
3
  import { ingestInCache } from '~/cache/api/ingestInCache';
4
4
  import { updateSubChannelUnreadFromMessage } from '~/marker/utils/updateSubChannelUnreadFromMessage';
5
5
  import { reCalculateChannelUnreadInfo } from '~/marker/utils/reCalculateChannelUnreadInfo';
@@ -48,7 +48,14 @@ export const onMessageCreatedMqtt = (
48
48
  'get',
49
49
  message.channelId,
50
50
  ])?.data;
51
- if (!channelUnread || channelUnread.lastSegment >= message.segment) return;
51
+
52
+ if (
53
+ !channelUnread ||
54
+ channelUnread.lastSegment >= message.segment ||
55
+ typeof channelUnread.readToSegment !== 'number' ||
56
+ typeof channelUnread.lastMentionedSegment !== 'number'
57
+ )
58
+ return;
52
59
 
53
60
  const lastSegment = message.segment;
54
61
  const isMentionedInMessage = message.mentionedUsers?.some(mention => {
@@ -60,17 +67,20 @@ export const onMessageCreatedMqtt = (
60
67
  );
61
68
  });
62
69
 
63
- const lastMentionSegment = isMentionedInMessage
70
+ const lastMentionedSegment = isMentionedInMessage
64
71
  ? message.segment
65
- : channelUnread.lastMentionSegment;
72
+ : channelUnread.lastMentionedSegment;
66
73
 
67
- pushToCache(['channelUnread', 'get', message.channelId], {
74
+ const updatedChannelUnread: Amity.ChannelUnread = {
68
75
  ...channelUnread,
69
76
  lastSegment,
70
- unreadCount: lastSegment - channelUnread.readToSegment,
71
- lastMentionSegment,
72
- isMentioned: !(channelUnread.readToSegment >= lastMentionSegment),
73
- });
77
+ unreadCount: Math.max(lastSegment - channelUnread.readToSegment, 0),
78
+ lastMentionedSegment,
79
+ isMentioned: !(channelUnread.readToSegment >= lastMentionedSegment),
80
+ };
81
+
82
+ pushToCache(['channelUnread', 'get', message.channelId], updatedChannelUnread);
83
+ fireEvent('local.channelUnread.updated', updatedChannelUnread);
74
84
  });
75
85
  }
76
86
 
@@ -13,7 +13,6 @@ import {
13
13
  } from '../events';
14
14
  import { onMessageFetched } from '../events/onMessageFetched';
15
15
  import { LinkedObject } from '~/utils/linkedObject';
16
- import { date } from '~/utils/tests';
17
16
 
18
17
  /* begin_public_function
19
18
  id: message.get