agora-appbuilder-core 2.3.0-beta.12 → 2.3.0-beta.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agora-appbuilder-core",
3
- "version": "2.3.0-beta.12",
3
+ "version": "2.3.0-beta.13",
4
4
  "description": "React Native template for RTE app builder",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -23,12 +23,14 @@
23
23
  "electron-updater": "4.3.9",
24
24
  "exponential-backoff": "3.1.0",
25
25
  "graphql": "15.5.0",
26
+ "nanoid": "4.0.0",
26
27
  "nosleep.js": "0.12.0",
27
28
  "react": "16.13.1",
28
29
  "react-dom": "16.13.1",
29
30
  "react-is": "18.0.0",
30
31
  "react-native": "0.63.3",
31
32
  "react-native-agora": "3.4.2",
33
+ "react-native-get-random-values": "1.8.0",
32
34
  "react-native-hyperlink": "0.0.19",
33
35
  "react-native-inappbrowser-reborn": "3.5.1",
34
36
  "react-native-keep-awake": "4.0.0",
@@ -12940,6 +12942,11 @@
12940
12942
  "node": ">= 0.10"
12941
12943
  }
12942
12944
  },
12945
+ "node_modules/fast-base64-decode": {
12946
+ "version": "1.0.0",
12947
+ "resolved": "https://registry.npmjs.org/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz",
12948
+ "integrity": "sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q=="
12949
+ },
12943
12950
  "node_modules/fast-deep-equal": {
12944
12951
  "version": "3.1.3",
12945
12952
  "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -22140,6 +22147,17 @@
22140
22147
  "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==",
22141
22148
  "optional": true
22142
22149
  },
22150
+ "node_modules/nanoid": {
22151
+ "version": "4.0.0",
22152
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.0.tgz",
22153
+ "integrity": "sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==",
22154
+ "bin": {
22155
+ "nanoid": "bin/nanoid.js"
22156
+ },
22157
+ "engines": {
22158
+ "node": "^14 || ^16 || >=18"
22159
+ }
22160
+ },
22143
22161
  "node_modules/nanomatch": {
22144
22162
  "version": "1.2.13",
22145
22163
  "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
@@ -23984,6 +24002,17 @@
23984
24002
  "react-native": "*"
23985
24003
  }
23986
24004
  },
24005
+ "node_modules/react-native-get-random-values": {
24006
+ "version": "1.8.0",
24007
+ "resolved": "https://registry.npmjs.org/react-native-get-random-values/-/react-native-get-random-values-1.8.0.tgz",
24008
+ "integrity": "sha512-H/zghhun0T+UIJLmig3+ZuBCvF66rdbiWUfRSNS6kv5oDSpa1ZiVyvRWtuPesQpT8dXj+Bv7WJRQOUP+5TB1sA==",
24009
+ "dependencies": {
24010
+ "fast-base64-decode": "^1.0.0"
24011
+ },
24012
+ "peerDependencies": {
24013
+ "react-native": ">=0.56"
24014
+ }
24015
+ },
23987
24016
  "node_modules/react-native-hyperlink": {
23988
24017
  "version": "0.0.19",
23989
24018
  "resolved": "https://registry.npmjs.org/react-native-hyperlink/-/react-native-hyperlink-0.0.19.tgz",
@@ -39247,6 +39276,11 @@
39247
39276
  "time-stamp": "^1.0.0"
39248
39277
  }
39249
39278
  },
39279
+ "fast-base64-decode": {
39280
+ "version": "1.0.0",
39281
+ "resolved": "https://registry.npmjs.org/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz",
39282
+ "integrity": "sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q=="
39283
+ },
39250
39284
  "fast-deep-equal": {
39251
39285
  "version": "3.1.3",
39252
39286
  "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -46424,6 +46458,11 @@
46424
46458
  "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==",
46425
46459
  "optional": true
46426
46460
  },
46461
+ "nanoid": {
46462
+ "version": "4.0.0",
46463
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.0.tgz",
46464
+ "integrity": "sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg=="
46465
+ },
46427
46466
  "nanomatch": {
46428
46467
  "version": "1.2.13",
46429
46468
  "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
@@ -48158,6 +48197,14 @@
48158
48197
  "integrity": "sha512-zY2BIDCG9vEGQMW9oggsom9XHZJqETG/nafLsZx4z52TzHw9bipNypL/jXpE50Ofg+bS9YKYJaSZGy8M9dgKSA==",
48159
48198
  "requires": {}
48160
48199
  },
48200
+ "react-native-get-random-values": {
48201
+ "version": "1.8.0",
48202
+ "resolved": "https://registry.npmjs.org/react-native-get-random-values/-/react-native-get-random-values-1.8.0.tgz",
48203
+ "integrity": "sha512-H/zghhun0T+UIJLmig3+ZuBCvF66rdbiWUfRSNS6kv5oDSpa1ZiVyvRWtuPesQpT8dXj+Bv7WJRQOUP+5TB1sA==",
48204
+ "requires": {
48205
+ "fast-base64-decode": "^1.0.0"
48206
+ }
48207
+ },
48161
48208
  "react-native-hyperlink": {
48162
48209
  "version": "0.0.19",
48163
48210
  "resolved": "https://registry.npmjs.org/react-native-hyperlink/-/react-native-hyperlink-0.0.19.tgz",
@@ -38,6 +38,8 @@ export {
38
38
  default as useSendMessage,
39
39
  MESSAGE_TYPE,
40
40
  } from '../src/utils/useSendMessage';
41
+ export {default as useEditMessage} from '../src/utils/useEditMessage';
42
+ export {default as useDeleteMessage} from '../src/utils/useDeleteMessage';
41
43
  export {controlMessageEnum} from '../src/components/ChatContext';
42
44
  export {
43
45
  default as useSendControlMessage,
@@ -62,12 +62,14 @@
62
62
  "electron-updater": "4.3.9",
63
63
  "exponential-backoff": "3.1.0",
64
64
  "graphql": "15.5.0",
65
+ "nanoid": "4.0.0",
65
66
  "nosleep.js": "0.12.0",
66
67
  "react": "16.13.1",
67
68
  "react-dom": "16.13.1",
68
69
  "react-is": "18.0.0",
69
70
  "react-native": "0.63.3",
70
71
  "react-native-agora": "3.4.2",
72
+ "react-native-get-random-values": "1.8.0",
71
73
  "react-native-hyperlink": "0.0.19",
72
74
  "react-native-inappbrowser-reborn": "3.5.1",
73
75
  "react-native-keep-awake": "4.0.0",
@@ -16,18 +16,25 @@ import {createContext, SetStateAction} from 'react';
16
16
  export interface ChatBubbleProps {
17
17
  isLocal: boolean;
18
18
  message: string;
19
- timestamp: string;
19
+ createdTimestamp: string;
20
+ updatedTimestamp?: string;
20
21
  uid: UidType;
22
+ msgId: string;
23
+ isDeleted: boolean;
21
24
  render?: (
22
25
  isLocal: boolean,
23
26
  message: string,
24
- timestamp: string,
27
+ createdTimestamp: string,
25
28
  uid: UidType,
29
+ msgId: string,
30
+ isDeleted: boolean,
31
+ updatedTimestamp?: string,
26
32
  ) => JSX.Element;
27
33
  }
28
34
 
29
35
  export interface messageStoreInterface {
30
- ts: string;
36
+ createdTimestamp: string;
37
+ updatedTimestamp?: string;
31
38
  uid: UidType;
32
39
  msg: string;
33
40
  }
@@ -21,28 +21,42 @@ import {useChatNotification} from '../chat-notification/useChatNotification';
21
21
  import Toast from '../../../react-native-toast-message';
22
22
  import {timeNow} from '../../rtm/utils';
23
23
  import {useSidePanel} from '../../utils/useSidePanel';
24
+ import getUniqueID from '../../utils/getUniqueID';
25
+
26
+ enum ChatMessageActionEnum {
27
+ Create = 'Create_Chat_Message',
28
+ Update = 'Update_Chat_Message',
29
+ Delete = 'Delete_Chat_Message',
30
+ }
24
31
 
25
32
  interface ChatMessagesProviderProps {
26
33
  children: React.ReactNode;
27
34
  }
28
35
  interface messageInterface {
29
- ts: number;
36
+ createdTimestamp: number;
37
+ updatedTimestamp?: number;
30
38
  msg: string;
39
+ msgId: string;
40
+ isDeleted: boolean;
31
41
  }
32
42
  interface messageStoreInterface extends messageInterface {
33
43
  uid: UidType;
34
44
  }
35
45
 
36
46
  interface ChatMessagesInterface {
37
- messageStore: messageStoreInterface | any;
38
- privateMessageStore: any;
47
+ messageStore: messageStoreInterface[];
48
+ privateMessageStore: {[key: string]: messageStoreInterface[]};
39
49
  sendChatMessage: (msg: string, toUid?: UidType) => void;
50
+ editChatMessage: (msgId: string, msg: string, toUid?: UidType) => void;
51
+ deleteChatMessage: (msgId: string, toUid?: UidType) => void;
40
52
  }
41
53
 
42
54
  const ChatMessagesContext = React.createContext<ChatMessagesInterface>({
43
55
  messageStore: [],
44
56
  privateMessageStore: {},
45
57
  sendChatMessage: () => {},
58
+ editChatMessage: () => {},
59
+ deleteChatMessage: () => {},
46
60
  });
47
61
 
48
62
  const ChatMessagesProvider = (props: ChatMessagesProviderProps) => {
@@ -53,7 +67,9 @@ const ChatMessagesProvider = (props: ChatMessagesProviderProps) => {
53
67
  const {setUnreadGroupMessageCount, setUnreadIndividualMessageCount} =
54
68
  useChatNotification();
55
69
  const [messageStore, setMessageStore] = useState<messageStoreInterface[]>([]);
56
- const [privateMessageStore, setPrivateMessageStore] = useState({});
70
+ const [privateMessageStore, setPrivateMessageStore] = useState<{
71
+ [key: string]: messageStoreInterface[];
72
+ }>({});
57
73
 
58
74
  const renderListRef = useRef({renderList: renderList});
59
75
  const groupActiveRef = useRef<boolean>();
@@ -91,54 +107,168 @@ const ChatMessagesProvider = (props: ChatMessagesProviderProps) => {
91
107
  });
92
108
  };
93
109
  CustomEvents.on(EventNames.PUBLIC_CHAT_MESSAGE, (data) => {
94
- showMessageNotification(data.payload.value, data.sender);
95
- addMessageToStore(parseInt(data.sender), {
96
- msg: data.payload.value,
97
- ts: data.ts,
98
- });
99
- /**
100
- * if chat group window is not active.
101
- * then we will increment the unread count
102
- */
103
- if (!groupActiveRef.current) {
104
- setUnreadGroupMessageCount((prevState) => {
105
- return prevState + 1;
106
- });
110
+ const messageData = JSON.parse(data.payload.value);
111
+ switch (data?.payload?.action) {
112
+ case ChatMessageActionEnum.Create:
113
+ showMessageNotification(messageData.msg, data.sender);
114
+ addMessageToStore(parseInt(data.sender), {
115
+ msg: messageData.msg,
116
+ createdTimestamp: messageData.createdTimestamp,
117
+ isDeleted: messageData.isDeleted,
118
+ msgId: messageData.msgId,
119
+ });
120
+ /**
121
+ * if chat group window is not active.
122
+ * then we will increment the unread count
123
+ */
124
+ if (!groupActiveRef.current) {
125
+ setUnreadGroupMessageCount((prevState) => {
126
+ return prevState + 1;
127
+ });
128
+ }
129
+ break;
130
+ case ChatMessageActionEnum.Update:
131
+ setMessageStore((prevState) => {
132
+ const newState = prevState.map((item) => {
133
+ if (
134
+ item.msgId === messageData.msgId &&
135
+ item.uid === parseInt(data.sender)
136
+ ) {
137
+ return {
138
+ ...item,
139
+ msg: messageData.msg,
140
+ updatedTimestamp: messageData.updatedTimestamp,
141
+ };
142
+ } else {
143
+ return item;
144
+ }
145
+ });
146
+ return newState;
147
+ });
148
+ break;
149
+ case ChatMessageActionEnum.Delete:
150
+ setMessageStore((prevState) => {
151
+ const newState = prevState.map((item) => {
152
+ if (
153
+ item.msgId === messageData.msgId &&
154
+ item.uid === parseInt(data.sender)
155
+ ) {
156
+ return {
157
+ ...item,
158
+ isDeleted: true,
159
+ updatedTimestamp: messageData.updatedTimestamp,
160
+ };
161
+ } else {
162
+ return item;
163
+ }
164
+ });
165
+ return newState;
166
+ });
167
+ break;
168
+ default:
169
+ break;
107
170
  }
108
171
  });
109
172
  CustomEvents.on(EventNames.PRIVATE_CHAT_MESSAGE, (data) => {
110
- showMessageNotification(data.payload.value, data.sender);
111
- addMessageToPrivateStore(
112
- parseInt(data.sender),
113
- {
114
- msg: data.payload.value,
115
- ts: data.ts,
116
- },
117
- false,
118
- );
119
- /**
120
- * if user's private window is active.
121
- * then we will not increment the unread count
122
- */
123
-
124
- if (!(individualActiveRef.current === parseInt(data.sender))) {
125
- setUnreadIndividualMessageCount((prevState) => {
126
- const prevCount =
127
- prevState && prevState[parseInt(data.sender)]
128
- ? prevState[parseInt(data.sender)]
129
- : 0;
130
- return {
131
- ...prevState,
132
- [parseInt(data.sender)]: prevCount + 1,
133
- };
134
- });
173
+ const messageData = JSON.parse(data.payload.value);
174
+
175
+ switch (data?.payload?.action) {
176
+ case ChatMessageActionEnum.Create:
177
+ showMessageNotification(messageData.msg, data.sender);
178
+ addMessageToPrivateStore(
179
+ parseInt(data.sender),
180
+ {
181
+ msg: messageData.msg,
182
+ createdTimestamp: messageData.createdTimestamp,
183
+ msgId: messageData.msgId,
184
+ isDeleted: messageData.isDeleted,
185
+ },
186
+ false,
187
+ );
188
+ /**
189
+ * if user's private window is active.
190
+ * then we will not increment the unread count
191
+ */
192
+
193
+ if (!(individualActiveRef.current === parseInt(data.sender))) {
194
+ setUnreadIndividualMessageCount((prevState) => {
195
+ const prevCount =
196
+ prevState && prevState[parseInt(data.sender)]
197
+ ? prevState[parseInt(data.sender)]
198
+ : 0;
199
+ return {
200
+ ...prevState,
201
+ [parseInt(data.sender)]: prevCount + 1,
202
+ };
203
+ });
204
+ }
205
+ break;
206
+ case ChatMessageActionEnum.Update:
207
+ setPrivateMessageStore((prevState) => {
208
+ const privateChatOfUid = prevState[parseInt(data.sender)];
209
+ const updatedData = privateChatOfUid.map((item) => {
210
+ if (
211
+ item.msgId === messageData.msgId &&
212
+ item.uid === parseInt(data.sender)
213
+ ) {
214
+ return {
215
+ ...item,
216
+ msg: messageData.msg,
217
+ updatedTimestamp: messageData.updatedTimestamp,
218
+ };
219
+ } else {
220
+ return item;
221
+ }
222
+ });
223
+ const newState = {
224
+ ...prevState,
225
+ [parseInt(data.sender)]: updatedData,
226
+ };
227
+ return newState;
228
+ });
229
+ break;
230
+ case ChatMessageActionEnum.Delete:
231
+ setPrivateMessageStore((prevState) => {
232
+ const privateChatOfUid = prevState[parseInt(data.sender)];
233
+ const updatedData = privateChatOfUid.map((item) => {
234
+ if (
235
+ item.msgId === messageData.msgId &&
236
+ item.uid === parseInt(data.sender)
237
+ ) {
238
+ return {
239
+ ...item,
240
+ isDeleted: true,
241
+ updatedTimestamp: messageData.updatedTimestamp,
242
+ };
243
+ } else {
244
+ return item;
245
+ }
246
+ });
247
+ const newState = {
248
+ ...prevState,
249
+ [parseInt(data.sender)]: updatedData,
250
+ };
251
+ return newState;
252
+ });
253
+ break;
254
+ default:
255
+ break;
135
256
  }
136
257
  });
137
258
  }, []);
138
259
 
139
260
  const addMessageToStore = (uid: UidType, body: messageInterface) => {
140
261
  setMessageStore((m: messageStoreInterface[]) => {
141
- return [...m, {ts: body.ts, uid, msg: body.msg}];
262
+ return [
263
+ ...m,
264
+ {
265
+ createdTimestamp: body.createdTimestamp,
266
+ uid,
267
+ msg: body.msg,
268
+ msgId: body.msgId,
269
+ isDeleted: body.isDeleted,
270
+ },
271
+ ];
142
272
  });
143
273
  };
144
274
 
@@ -147,16 +277,30 @@ const ChatMessagesProvider = (props: ChatMessagesProviderProps) => {
147
277
  body: messageInterface,
148
278
  local: boolean,
149
279
  ) => {
150
- setPrivateMessageStore((state: any) => {
280
+ setPrivateMessageStore((state) => {
151
281
  let newState = {...state};
152
282
  newState[uid] !== undefined
153
283
  ? (newState[uid] = [
154
284
  ...newState[uid],
155
- {ts: body.ts, uid: local ? localUid : uid, msg: body.msg},
285
+ {
286
+ createdTimestamp: body.createdTimestamp,
287
+ uid: local ? localUid : uid,
288
+ msg: body.msg,
289
+ msgId: body.msgId,
290
+ isDeleted: body.isDeleted,
291
+ },
156
292
  ])
157
293
  : (newState = {
158
294
  ...newState,
159
- [uid]: [{ts: body.ts, uid: local ? localUid : uid, msg: body.msg}],
295
+ [uid]: [
296
+ {
297
+ createdTimestamp: body.createdTimestamp,
298
+ uid: local ? localUid : uid,
299
+ msg: body.msg,
300
+ msgId: body.msgId,
301
+ isDeleted: body.isDeleted,
302
+ },
303
+ ],
160
304
  });
161
305
  return {...newState};
162
306
  });
@@ -165,29 +309,148 @@ const ChatMessagesProvider = (props: ChatMessagesProviderProps) => {
165
309
  const sendChatMessage = (msg: string, toUid?: UidType) => {
166
310
  if (typeof msg == 'string' && msg.trim() === '') return;
167
311
  if (toUid) {
312
+ const messageData = {
313
+ msg,
314
+ createdTimestamp: timeNow(),
315
+ msgId: getUniqueID(),
316
+ isDeleted: false,
317
+ };
168
318
  CustomEvents.send(
169
319
  EventNames.PRIVATE_CHAT_MESSAGE,
170
320
  {
171
- value: msg,
321
+ value: JSON.stringify(messageData),
322
+ action: ChatMessageActionEnum.Create,
172
323
  },
173
324
  toUid,
174
325
  );
175
- addMessageToPrivateStore(
176
- toUid,
177
- {
178
- msg: msg,
179
- ts: timeNow(),
180
- },
181
- true,
182
- );
326
+ addMessageToPrivateStore(toUid, messageData, true);
183
327
  } else {
328
+ const messageData = {
329
+ msg,
330
+ msgId: getUniqueID(),
331
+ isDeleted: false,
332
+ createdTimestamp: timeNow(),
333
+ };
184
334
  CustomEvents.send(EventNames.PUBLIC_CHAT_MESSAGE, {
185
- value: msg,
186
- });
187
- addMessageToStore(localUid, {
188
- msg: msg,
189
- ts: timeNow(),
335
+ value: JSON.stringify(messageData),
336
+ action: ChatMessageActionEnum.Create,
190
337
  });
338
+ addMessageToStore(localUid, messageData);
339
+ }
340
+ };
341
+
342
+ const editChatMessage = (msgId: string, msg: string, toUid?: UidType) => {
343
+ if (typeof msg == 'string' && msg.trim() === '') return;
344
+ if (toUid) {
345
+ const checkData = privateMessageStore[toUid].filter(
346
+ (item) => item.msgId === msgId && item.uid === localUid,
347
+ );
348
+ if (checkData && checkData.length) {
349
+ const editMsgData = {msg, updatedTimestamp: timeNow()};
350
+ CustomEvents.send(
351
+ EventNames.PRIVATE_CHAT_MESSAGE,
352
+ {
353
+ value: JSON.stringify({msgId, ...editMsgData}),
354
+ action: ChatMessageActionEnum.Update,
355
+ },
356
+ toUid,
357
+ );
358
+ setPrivateMessageStore((prevState) => {
359
+ const privateChatOfUid = prevState[toUid];
360
+ const updatedData = privateChatOfUid.map((item) => {
361
+ if (item.msgId === msgId) {
362
+ return {...item, ...editMsgData};
363
+ } else {
364
+ return item;
365
+ }
366
+ });
367
+ const newState = {...prevState, [toUid]: updatedData};
368
+ return newState;
369
+ });
370
+ } else {
371
+ console.log("You don't have permission to edit");
372
+ }
373
+ } else {
374
+ //check if user has permission to edit
375
+ const checkData = messageStore.filter(
376
+ (item) => item.msgId === msgId && item.uid === localUid,
377
+ );
378
+ if (checkData && checkData.length) {
379
+ const editMsgData = {msg, updatedTimestamp: timeNow()};
380
+ CustomEvents.send(EventNames.PUBLIC_CHAT_MESSAGE, {
381
+ value: JSON.stringify({msgId, ...editMsgData}),
382
+ action: ChatMessageActionEnum.Update,
383
+ });
384
+ setMessageStore((prevState) => {
385
+ const newState = prevState.map((item) => {
386
+ if (item.msgId === msgId) {
387
+ return {...item, ...editMsgData};
388
+ } else {
389
+ return item;
390
+ }
391
+ });
392
+ return newState;
393
+ });
394
+ } else {
395
+ console.log("You don't have permission to edit");
396
+ }
397
+ }
398
+ };
399
+
400
+ const deleteChatMessage = (msgId: string, toUid?: UidType) => {
401
+ if (toUid) {
402
+ const checkData = privateMessageStore[toUid].filter(
403
+ (item) => item.msgId === msgId && item.uid === localUid,
404
+ );
405
+ if (checkData && checkData.length) {
406
+ const deleteMsgData = {updatedTimestamp: timeNow()};
407
+ CustomEvents.send(
408
+ EventNames.PRIVATE_CHAT_MESSAGE,
409
+ {
410
+ value: JSON.stringify({msgId, ...deleteMsgData}),
411
+ action: ChatMessageActionEnum.Delete,
412
+ },
413
+ toUid,
414
+ );
415
+ setPrivateMessageStore((prevState) => {
416
+ const privateChatOfUid = prevState[toUid];
417
+ const updatedData = privateChatOfUid.map((item) => {
418
+ if (item.msgId === msgId) {
419
+ return {...item, isDeleted: true, ...deleteMsgData};
420
+ } else {
421
+ return item;
422
+ }
423
+ });
424
+ const newState = {...prevState, [toUid]: updatedData};
425
+ return newState;
426
+ });
427
+ } else {
428
+ console.log("You don't have permission to delete");
429
+ }
430
+ } else {
431
+ //check if user has permission to delete
432
+ const checkData = messageStore.filter(
433
+ (item) => item.msgId === msgId && item.uid === localUid,
434
+ );
435
+ if (checkData && checkData.length) {
436
+ const deleteMsgData = {updatedTimestamp: timeNow()};
437
+ CustomEvents.send(EventNames.PUBLIC_CHAT_MESSAGE, {
438
+ value: JSON.stringify({msgId, ...deleteMsgData}),
439
+ action: ChatMessageActionEnum.Delete,
440
+ });
441
+ setMessageStore((prevState) => {
442
+ const newState = prevState.map((item) => {
443
+ if (item.msgId === msgId) {
444
+ return {...item, isDeleted: true, ...deleteMsgData};
445
+ } else {
446
+ return item;
447
+ }
448
+ });
449
+ return newState;
450
+ });
451
+ } else {
452
+ console.log("You don't have permission to delete");
453
+ }
191
454
  }
192
455
  };
193
456
 
@@ -197,6 +460,8 @@ const ChatMessagesProvider = (props: ChatMessagesProviderProps) => {
197
460
  messageStore,
198
461
  privateMessageStore,
199
462
  sendChatMessage,
463
+ editChatMessage,
464
+ deleteChatMessage,
200
465
  }}>
201
466
  {props.children}
202
467
  </ChatMessagesContext.Provider>
@@ -21,11 +21,19 @@ import useUserList from '../utils/useUserList';
21
21
  const ChatBubble = (props: ChatBubbleProps) => {
22
22
  const {renderList} = useUserList();
23
23
  const {primaryColor} = useContext(ColorContext);
24
- let {isLocal, message, timestamp, uid} = props;
24
+ let {
25
+ isLocal,
26
+ message,
27
+ createdTimestamp,
28
+ uid,
29
+ isDeleted,
30
+ msgId,
31
+ updatedTimestamp,
32
+ } = props;
25
33
  let time =
26
- new Date(parseInt(timestamp)).getHours() +
34
+ new Date(parseInt(createdTimestamp)).getHours() +
27
35
  ':' +
28
- new Date(parseInt(timestamp)).getMinutes();
36
+ new Date(parseInt(createdTimestamp)).getMinutes();
29
37
  const handleUrl = (url: string) => {
30
38
  if (isWeb) {
31
39
  window.open(url, '_blank');
@@ -37,7 +45,15 @@ const ChatBubble = (props: ChatBubbleProps) => {
37
45
  //const remoteUserDefaultLabel = useString('remoteUserDefaultLabel')();
38
46
  const remoteUserDefaultLabel = 'User';
39
47
  return props?.render ? (
40
- props.render(isLocal, message, timestamp, uid)
48
+ props.render(
49
+ isLocal,
50
+ message,
51
+ createdTimestamp,
52
+ uid,
53
+ msgId,
54
+ isDeleted,
55
+ updatedTimestamp,
56
+ )
41
57
  ) : (
42
58
  <View>
43
59
  <View style={isLocal ? style.chatSenderViewLocal : style.chatSenderView}>
@@ -129,9 +129,12 @@ const ChatContainer = (props?: {
129
129
  <ChatBubbleComponent
130
130
  isLocal={localUid === message.uid}
131
131
  message={message.msg}
132
- timestamp={message.ts}
132
+ createdTimestamp={message.createdTimestamp}
133
+ updatedTimestamp={message.updatedTimestamp}
133
134
  uid={message.uid}
134
135
  key={message.ts}
136
+ msgId={message.msgId}
137
+ isDeleted={message.isDeleted}
135
138
  />
136
139
  </>
137
140
  ))
@@ -140,9 +143,12 @@ const ChatContainer = (props?: {
140
143
  <ChatBubbleComponent
141
144
  isLocal={localUid === message.uid}
142
145
  message={message.msg}
143
- timestamp={message.ts}
146
+ createdTimestamp={message.createdTimestamp}
147
+ updatedTimestamp={message.updatedTimestamp}
144
148
  uid={message.uid}
145
149
  key={message.ts}
150
+ msgId={message.msgId}
151
+ isDeleted={message.isDeleted}
146
152
  />
147
153
  ))
148
154
  ) : (
@@ -0,0 +1,6 @@
1
+ import 'react-native-get-random-values';
2
+ import {nanoid} from 'nanoid';
3
+
4
+ export default function getUniqueID() {
5
+ return nanoid();
6
+ }
@@ -0,0 +1,5 @@
1
+ import {nanoid} from 'nanoid';
2
+
3
+ export default function getUniqueID() {
4
+ return nanoid();
5
+ }
@@ -0,0 +1,36 @@
1
+ /*
2
+ ********************************************
3
+ Copyright © 2021 Agora Lab, Inc., all rights reserved.
4
+ AppBuilder and all associated components, source code, APIs, services, and documentation
5
+ (the “Materials”) are owned by Agora Lab, Inc. and its licensors. The Materials may not be
6
+ accessed, used, modified, or distributed for any purpose without a license from Agora Lab, Inc.
7
+ Use without a license or in violation of any license terms and conditions (including use for
8
+ any purpose competitive to Agora Lab, Inc.’s business) is strictly prohibited. For more
9
+ information visit https://appbuilder.agora.io.
10
+ *********************************************
11
+ */
12
+ import {UidType} from '../../agora-rn-uikit';
13
+ import {useChatMessages} from '../components/chat-messages/useChatMessages';
14
+ import {MESSAGE_TYPE} from './useSendMessage';
15
+
16
+ function useDeleteMessage() {
17
+ const {deleteChatMessage} = useChatMessages();
18
+ return (type: MESSAGE_TYPE, msgId: string, uid?: UidType) => {
19
+ switch (type) {
20
+ case MESSAGE_TYPE.group:
21
+ deleteChatMessage(msgId);
22
+ break;
23
+ case MESSAGE_TYPE.private:
24
+ if (uid) {
25
+ deleteChatMessage(msgId, uid);
26
+ } else {
27
+ console.error('To delete the private message, UID should be passed');
28
+ }
29
+ break;
30
+ default:
31
+ break;
32
+ }
33
+ };
34
+ }
35
+
36
+ export default useDeleteMessage;
@@ -0,0 +1,41 @@
1
+ /*
2
+ ********************************************
3
+ Copyright © 2021 Agora Lab, Inc., all rights reserved.
4
+ AppBuilder and all associated components, source code, APIs, services, and documentation
5
+ (the “Materials”) are owned by Agora Lab, Inc. and its licensors. The Materials may not be
6
+ accessed, used, modified, or distributed for any purpose without a license from Agora Lab, Inc.
7
+ Use without a license or in violation of any license terms and conditions (including use for
8
+ any purpose competitive to Agora Lab, Inc.’s business) is strictly prohibited. For more
9
+ information visit https://appbuilder.agora.io.
10
+ *********************************************
11
+ */
12
+ import {UidType} from '../../agora-rn-uikit';
13
+ import {useChatMessages} from '../components/chat-messages/useChatMessages';
14
+ import {MESSAGE_TYPE} from './useSendMessage';
15
+
16
+ function useEditMessage() {
17
+ const {editChatMessage} = useChatMessages();
18
+ return (
19
+ type: MESSAGE_TYPE,
20
+ msgId: string,
21
+ message: string,
22
+ uid?: UidType,
23
+ ) => {
24
+ switch (type) {
25
+ case MESSAGE_TYPE.group:
26
+ editChatMessage(msgId, message);
27
+ break;
28
+ case MESSAGE_TYPE.private:
29
+ if (uid) {
30
+ editChatMessage(msgId, message, uid);
31
+ } else {
32
+ console.error('To edit the private message, UID should be passed');
33
+ }
34
+ break;
35
+ default:
36
+ break;
37
+ }
38
+ };
39
+ }
40
+
41
+ export default useEditMessage;
@@ -9,24 +9,23 @@
9
9
  information visit https://appbuilder.agora.io.
10
10
  *********************************************
11
11
  */
12
- import {useContext} from 'react';
13
- import ChatContext from '../components/ChatContext';
14
12
  import {UidType} from '../../agora-rn-uikit';
13
+ import {useChatMessages} from '../components/chat-messages/useChatMessages';
15
14
 
16
15
  export enum MESSAGE_TYPE {
17
16
  group,
18
17
  private,
19
18
  }
20
19
  function useSendMessage() {
21
- const {sendMessage, sendMessageToUid} = useContext(ChatContext);
20
+ const {sendChatMessage} = useChatMessages();
22
21
  return (type: MESSAGE_TYPE, message: string, uid?: UidType) => {
23
22
  switch (type) {
24
23
  case MESSAGE_TYPE.group:
25
- sendMessage(message);
24
+ sendChatMessage(message);
26
25
  break;
27
26
  case MESSAGE_TYPE.private:
28
27
  if (uid) {
29
- sendMessageToUid(message, uid);
28
+ sendChatMessage(message, uid);
30
29
  } else {
31
30
  console.error('To send the private message, UID should be passed');
32
31
  }