@amityco/ts-sdk 0.0.1-beta.30 → 0.0.1-beta.32

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 (233) hide show
  1. package/dist/@types/core/cache.d.ts +3 -0
  2. package/dist/@types/core/cache.d.ts.map +1 -1
  3. package/dist/@types/core/errors.d.ts +2 -1
  4. package/dist/@types/core/errors.d.ts.map +1 -1
  5. package/dist/@types/core/events.d.ts +2 -0
  6. package/dist/@types/core/events.d.ts.map +1 -1
  7. package/dist/@types/core/live.d.ts +8 -4
  8. package/dist/@types/core/live.d.ts.map +1 -1
  9. package/dist/@types/core/query.d.ts +1 -1
  10. package/dist/@types/core/query.d.ts.map +1 -1
  11. package/dist/@types/domains/channel.d.ts +1 -0
  12. package/dist/@types/domains/channel.d.ts.map +1 -1
  13. package/dist/@types/domains/message.d.ts +13 -6
  14. package/dist/@types/domains/message.d.ts.map +1 -1
  15. package/dist/@types/domains/post.d.ts +10 -3
  16. package/dist/@types/domains/post.d.ts.map +1 -1
  17. package/dist/@types/domains/user.d.ts +1 -0
  18. package/dist/@types/domains/user.d.ts.map +1 -1
  19. package/dist/@types/index.d.ts +2 -2
  20. package/dist/@types/index.d.ts.map +1 -1
  21. package/dist/cache/api/backupCache.d.ts.map +1 -1
  22. package/dist/cache/api/isInTombstone.d.ts +18 -0
  23. package/dist/cache/api/isInTombstone.d.ts.map +1 -0
  24. package/dist/cache/api/pushToTombstone.d.ts +17 -0
  25. package/dist/cache/api/pushToTombstone.d.ts.map +1 -0
  26. package/dist/cache/api/tests/pushToCache.test.d.ts +2 -0
  27. package/dist/cache/api/tests/pushToCache.test.d.ts.map +1 -0
  28. package/dist/cache/utils/index.d.ts +17 -0
  29. package/dist/cache/utils/index.d.ts.map +1 -1
  30. package/dist/channel/api/addChannelMembers.d.ts.map +1 -1
  31. package/dist/channel/api/getChannel.d.ts.map +1 -1
  32. package/dist/channel/api/queryChannelMembers.d.ts +1 -1
  33. package/dist/channel/api/queryChannelMembers.d.ts.map +1 -1
  34. package/dist/channel/api/queryChannels.d.ts +1 -1
  35. package/dist/channel/api/queryChannels.d.ts.map +1 -1
  36. package/dist/channel/api/tests/addChannelMembers.test.d.ts +2 -0
  37. package/dist/channel/api/tests/addChannelMembers.test.d.ts.map +1 -0
  38. package/dist/channel/api/tests/getChannel.test.d.ts +2 -0
  39. package/dist/channel/api/tests/getChannel.test.d.ts.map +1 -0
  40. package/dist/channel/api/tests/queryChannelMembers.test.d.ts +2 -0
  41. package/dist/channel/api/tests/queryChannelMembers.test.d.ts.map +1 -0
  42. package/dist/channel/api/tests/queryChannels.test.d.ts +2 -0
  43. package/dist/channel/api/tests/queryChannels.test.d.ts.map +1 -0
  44. package/dist/channel/events/index.d.ts +2 -0
  45. package/dist/channel/events/index.d.ts.map +1 -1
  46. package/dist/channel/events/onChannelMemberAdded.d.ts.map +1 -1
  47. package/dist/channel/events/onChannelMemberBanned.d.ts +17 -0
  48. package/dist/channel/events/onChannelMemberBanned.d.ts.map +1 -0
  49. package/dist/channel/events/onChannelMemberUnbanned.d.ts +17 -0
  50. package/dist/channel/events/onChannelMemberUnbanned.d.ts.map +1 -0
  51. package/dist/channel/events/tests/onChannelMemberBanned.test.d.ts +2 -0
  52. package/dist/channel/events/tests/onChannelMemberBanned.test.d.ts.map +1 -0
  53. package/dist/channel/events/tests/onChannelMemberUnbanned.test.d.ts +2 -0
  54. package/dist/channel/events/tests/onChannelMemberUnbanned.test.d.ts.map +1 -0
  55. package/dist/channel/observers/liveChannelMembers.d.ts.map +1 -1
  56. package/dist/channel/observers/liveChannels.d.ts.map +1 -1
  57. package/dist/channel/{tests/utils → utils/tests}/prepareChannelPayload.test.d.ts +0 -0
  58. package/dist/channel/utils/tests/prepareChannelPayload.test.d.ts.map +1 -0
  59. package/dist/core/events.d.ts +3 -3
  60. package/dist/core/events.d.ts.map +1 -1
  61. package/dist/core/query/filtering.d.ts +11 -0
  62. package/dist/core/query/filtering.d.ts.map +1 -1
  63. package/dist/core/query/query.d.ts.map +1 -1
  64. package/dist/core/transports/http.d.ts +5 -0
  65. package/dist/core/transports/http.d.ts.map +1 -1
  66. package/dist/core/transports/test/http.test.d.ts +2 -0
  67. package/dist/core/transports/test/http.test.d.ts.map +1 -0
  68. package/dist/index.cjs.js +980 -534
  69. package/dist/index.esm.js +962 -526
  70. package/dist/index.umd.js +4 -4
  71. package/dist/message/api/createMessage.d.ts +4 -1
  72. package/dist/message/api/createMessage.d.ts.map +1 -1
  73. package/dist/message/api/deleteMessage.d.ts.map +1 -1
  74. package/dist/message/api/getMessage.d.ts +1 -1
  75. package/dist/message/api/getMessage.d.ts.map +1 -1
  76. package/dist/message/api/getMessages.d.ts.map +1 -1
  77. package/dist/message/api/queryMessages.d.ts.map +1 -1
  78. package/dist/message/api/tests/createMessage.test.d.ts +2 -0
  79. package/dist/message/api/tests/createMessage.test.d.ts.map +1 -0
  80. package/dist/message/api/tests/deleteMessage.test.d.ts +2 -0
  81. package/dist/message/api/tests/deleteMessage.test.d.ts.map +1 -0
  82. package/dist/message/api/tests/getMessage.test.d.ts +2 -0
  83. package/dist/message/api/tests/getMessage.test.d.ts.map +1 -0
  84. package/dist/message/api/tests/getMessages.test.d.ts +2 -0
  85. package/dist/message/api/tests/getMessages.test.d.ts.map +1 -0
  86. package/dist/message/api/tests/queryMessages.test.d.ts +2 -0
  87. package/dist/message/api/tests/queryMessages.test.d.ts.map +1 -0
  88. package/dist/message/api/tests/updateMessage.test.d.ts +2 -0
  89. package/dist/message/api/tests/updateMessage.test.d.ts.map +1 -0
  90. package/dist/message/api/updateMessage.d.ts.map +1 -1
  91. package/dist/message/events/index.d.ts +1 -0
  92. package/dist/message/events/index.d.ts.map +1 -1
  93. package/dist/message/events/onMessageFetched.d.ts +17 -0
  94. package/dist/message/events/onMessageFetched.d.ts.map +1 -0
  95. package/dist/message/observers/index.d.ts +2 -1
  96. package/dist/message/observers/index.d.ts.map +1 -1
  97. package/dist/message/observers/liveMessage.d.ts +18 -0
  98. package/dist/message/observers/liveMessage.d.ts.map +1 -0
  99. package/dist/message/observers/liveMessages.d.ts.map +1 -1
  100. package/dist/message/observers/observeMessage.d.ts.map +1 -1
  101. package/dist/message/tests/observers/liveMessage.test.d.ts +2 -0
  102. package/dist/message/tests/observers/liveMessage.test.d.ts.map +1 -0
  103. package/dist/message/{observers/tests → tests/observers}/liveMessages.test.d.ts +0 -0
  104. package/dist/message/tests/observers/liveMessages.test.d.ts.map +1 -0
  105. package/dist/post/api/deletePost.d.ts.map +1 -1
  106. package/dist/post/api/getPost.d.ts.map +1 -1
  107. package/dist/post/api/updatePost.d.ts +6 -1
  108. package/dist/post/api/updatePost.d.ts.map +1 -1
  109. package/dist/reaction/api/addReaction.d.ts.map +1 -1
  110. package/dist/reaction/api/removeReaction.d.ts.map +1 -1
  111. package/dist/reaction/api/tests/addReaction.test.d.ts +2 -0
  112. package/dist/reaction/api/tests/addReaction.test.d.ts.map +1 -0
  113. package/dist/reaction/api/tests/removeReaction.test.d.ts +2 -0
  114. package/dist/reaction/api/tests/removeReaction.test.d.ts.map +1 -0
  115. package/dist/user/api/getUser.d.ts +1 -1
  116. package/dist/user/api/getUser.d.ts.map +1 -1
  117. package/dist/user/api/getUsers.d.ts.map +1 -1
  118. package/dist/user/api/queryUsers.d.ts.map +1 -1
  119. package/dist/user/api/tests/queryUsers.test.d.ts +2 -0
  120. package/dist/user/api/tests/queryUsers.test.d.ts.map +1 -0
  121. package/dist/user/api/updateUser.d.ts.map +1 -1
  122. package/dist/user/events/index.d.ts +1 -0
  123. package/dist/user/events/index.d.ts.map +1 -1
  124. package/dist/user/events/onUserFetched.d.ts +17 -0
  125. package/dist/user/events/onUserFetched.d.ts.map +1 -0
  126. package/dist/user/observers/index.d.ts +1 -0
  127. package/dist/user/observers/index.d.ts.map +1 -1
  128. package/dist/user/observers/liveUser.d.ts +18 -0
  129. package/dist/user/observers/liveUser.d.ts.map +1 -0
  130. package/dist/user/observers/observeUser.d.ts.map +1 -1
  131. package/dist/user/tests/api/getUser.test.d.ts +2 -0
  132. package/dist/user/tests/api/getUser.test.d.ts.map +1 -0
  133. package/dist/user/tests/observers/liveUser.test.d.ts +2 -0
  134. package/dist/user/tests/observers/liveUser.test.d.ts.map +1 -0
  135. package/dist/utils/constants.d.ts +3 -0
  136. package/dist/utils/constants.d.ts.map +1 -1
  137. package/dist/utils/tests/dummy/channel.d.ts +15 -0
  138. package/dist/utils/tests/dummy/channel.d.ts.map +1 -1
  139. package/dist/utils/tests/dummy/message.d.ts +12 -1
  140. package/dist/utils/tests/dummy/message.d.ts.map +1 -1
  141. package/dist/utils/tests/dummy/post.d.ts +12 -1
  142. package/dist/utils/tests/dummy/post.d.ts.map +1 -1
  143. package/dist/utils/tests/dummy/user.d.ts +10 -0
  144. package/dist/utils/tests/dummy/user.d.ts.map +1 -1
  145. package/package.json +2 -1
  146. package/src/@types/core/cache.ts +4 -0
  147. package/src/@types/core/errors.ts +1 -0
  148. package/src/@types/core/events.ts +2 -0
  149. package/src/@types/core/live.ts +10 -4
  150. package/src/@types/core/query.ts +1 -1
  151. package/src/@types/domains/channel.ts +6 -0
  152. package/src/@types/domains/message.ts +13 -10
  153. package/src/@types/domains/post.ts +10 -3
  154. package/src/@types/domains/user.ts +8 -0
  155. package/src/@types/index.ts +2 -2
  156. package/src/cache/api/backupCache.ts +0 -1
  157. package/src/cache/api/index.ts +2 -1
  158. package/src/cache/api/isInTombstone.ts +41 -0
  159. package/src/cache/api/pushToTombstone.ts +33 -0
  160. package/src/cache/api/tests/pushToCache.test.ts +57 -0
  161. package/src/cache/utils/index.ts +22 -0
  162. package/src/channel/api/addChannelMembers.ts +9 -1
  163. package/src/channel/api/getChannel.ts +20 -5
  164. package/src/channel/api/queryChannelMembers.ts +53 -5
  165. package/src/channel/api/queryChannels.ts +31 -4
  166. package/src/channel/api/tests/addChannelMembers.test.ts +95 -0
  167. package/src/channel/api/tests/getChannel.test.ts +108 -0
  168. package/src/channel/api/tests/queryChannelMembers.test.ts +115 -0
  169. package/src/channel/api/tests/queryChannels.test.ts +121 -0
  170. package/src/channel/events/index.ts +5 -0
  171. package/src/channel/events/onChannelMemberAdded.ts +1 -1
  172. package/src/channel/events/onChannelMemberBanned.ts +36 -0
  173. package/src/channel/events/onChannelMemberUnbanned.ts +36 -0
  174. package/src/channel/events/tests/onChannelMemberBanned.test.ts +58 -0
  175. package/src/channel/events/tests/onChannelMemberUnbanned.test.ts +58 -0
  176. package/src/channel/observers/liveChannelMembers.ts +28 -4
  177. package/src/channel/observers/liveChannels.ts +7 -1
  178. package/src/channel/observers/tests/liveChannelMembers.test.ts +53 -11
  179. package/src/channel/observers/tests/liveChannels.test.ts +50 -21
  180. package/src/channel/{tests/utils → utils/tests}/prepareChannelPayload.test.ts +1 -1
  181. package/src/core/events.ts +2 -1
  182. package/src/core/query/filtering.ts +49 -0
  183. package/src/core/query/query.ts +9 -6
  184. package/src/core/tests/query/query.test.ts +12 -2
  185. package/src/core/transports/http.ts +3 -17
  186. package/src/core/transports/test/http.test.ts +83 -0
  187. package/src/message/api/createMessage.ts +15 -9
  188. package/src/message/api/deleteMessage.ts +4 -4
  189. package/src/message/api/getMessage.ts +32 -4
  190. package/src/message/api/getMessages.ts +6 -1
  191. package/src/message/api/queryMessages.ts +3 -0
  192. package/src/message/api/tests/createMessage.test.ts +99 -0
  193. package/src/message/api/tests/deleteMessage.test.ts +110 -0
  194. package/src/message/api/tests/getMessage.test.ts +121 -0
  195. package/src/message/api/tests/getMessages.test.ts +108 -0
  196. package/src/message/api/tests/queryMessages.test.ts +115 -0
  197. package/src/message/api/tests/updateMessage.test.ts +100 -0
  198. package/src/message/api/updateMessage.ts +2 -0
  199. package/src/message/events/index.ts +1 -0
  200. package/src/message/events/onMessageFetched.ts +30 -0
  201. package/src/message/observers/index.ts +2 -1
  202. package/src/message/observers/liveMessage.ts +100 -0
  203. package/src/message/observers/liveMessages.ts +3 -1
  204. package/src/message/observers/observeMessage.ts +4 -0
  205. package/src/message/tests/observers/liveMessage.test.ts +248 -0
  206. package/src/message/{observers/tests → tests/observers}/liveMessages.test.ts +29 -3
  207. package/src/post/api/deletePost.ts +4 -3
  208. package/src/post/api/getPost.ts +8 -4
  209. package/src/post/api/updatePost.ts +6 -1
  210. package/src/post/tests/api/queryPosts.test.ts +117 -18
  211. package/src/reaction/api/addReaction.ts +1 -4
  212. package/src/reaction/api/removeReaction.ts +1 -4
  213. package/src/reaction/api/tests/addReaction.test.ts +37 -0
  214. package/src/reaction/api/tests/removeReaction.test.ts +43 -0
  215. package/src/user/api/getUser.ts +32 -5
  216. package/src/user/api/getUsers.ts +3 -0
  217. package/src/user/api/queryUsers.ts +27 -5
  218. package/src/user/api/tests/queryUsers.test.ts +124 -0
  219. package/src/user/api/updateUser.ts +3 -0
  220. package/src/user/events/index.ts +1 -0
  221. package/src/user/events/onUserFetched.ts +19 -0
  222. package/src/user/observers/index.ts +1 -0
  223. package/src/user/observers/liveUser.ts +89 -0
  224. package/src/user/observers/observeUser.ts +2 -0
  225. package/src/user/tests/api/getUser.test.ts +112 -0
  226. package/src/user/tests/observers/liveUser.test.ts +224 -0
  227. package/src/utils/constants.ts +5 -0
  228. package/src/utils/tests/dummy/channel.ts +43 -0
  229. package/src/utils/tests/dummy/message.ts +16 -2
  230. package/src/utils/tests/dummy/post.ts +26 -3
  231. package/src/utils/tests/dummy/user.ts +11 -0
  232. package/dist/channel/tests/utils/prepareChannelPayload.test.d.ts.map +0 -1
  233. package/dist/message/observers/tests/liveMessages.test.d.ts.map +0 -1
package/dist/index.esm.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { encode, decode } from 'js-base64';
2
- import mitt from 'mitt';
3
2
  import AsyncStorage from '@react-native-async-storage/async-storage';
3
+ import mitt from 'mitt';
4
4
  import debug from 'debug';
5
5
  import axios from 'axios';
6
+ import qs from 'qs';
6
7
  import HttpAgent, { HttpsAgent } from 'agentkeepalive';
7
8
  import io from 'socket.io-client';
8
9
  import nativeIdGenerator from 'react-native-uuid';
@@ -61,6 +62,24 @@ const VideoSize = Object.freeze({
61
62
  ORIGINAL: 'original',
62
63
  });
63
64
 
65
+ const MessageContentType = Object.freeze({
66
+ TEXT: 'text',
67
+ IMAGE: 'image',
68
+ FILE: 'file',
69
+ VIDEO: 'video',
70
+ AUDIO: 'audio',
71
+ CUSTOM: 'custom',
72
+ });
73
+
74
+ const PostContentType = Object.freeze({
75
+ TEXT: 'text',
76
+ IMAGE: 'image',
77
+ FILE: 'file',
78
+ VIDEO: 'video',
79
+ LIVESTREAM: 'liveStream',
80
+ POLL: 'poll',
81
+ });
82
+
64
83
  var _a$1;
65
84
  /* eslint-disable no-undef */
66
85
  const theGlobal =
@@ -336,6 +355,78 @@ const PAYLOAD2MODEL = {
336
355
  followCounts: 'followCount',
337
356
  };
338
357
 
358
+ // cache constants
359
+ const CACHE_KEY_GET = 'get';
360
+ const CACHE_KEY_TOMBSTONE = 'dead';
361
+ const CACHE_LIFESPAN = 1 * 60 * 1000; // 1 minute
362
+ const CACHE_LIFESPAN_TOMBSTONE = 3 * 60 * 1000; // 3 minutes
363
+ /**
364
+ * Shallow-clones an object and sort its keys
365
+ *
366
+ * @param source a plain object to clone
367
+ * @returns a clone of source, with keys sorted with default javascript sorting
368
+ *
369
+ * @category Cache
370
+ * @hidden
371
+ */
372
+ const normalize = (source) => Object.keys(source)
373
+ .sort()
374
+ .reduce((acc, key) => (Object.assign(Object.assign({}, acc), { [key]: source[key] })), {});
375
+ /**
376
+ * Encodes a given {@link Amity.CacheKey} to a plain string
377
+ *
378
+ * @param key the key to encode
379
+ * @returns an encoded string
380
+ *
381
+ * @category Cache
382
+ * @hidden
383
+ */
384
+ const encodeKey = (key) => JSON.stringify(key, (_, val) => (typeof val === 'object' ? normalize(val) : val));
385
+ /**
386
+ * Decodes a string back into a {@link Amity.CacheKey}
387
+
388
+ *
389
+ * @param key the string key to decode
390
+ * @returns a plain Amity.CacheKey object.
391
+ *
392
+ * @category Cache
393
+ * @hidden
394
+ */
395
+ const decodeKey = (key) => JSON.parse(key);
396
+ /**
397
+ * Performs a recursive partial deep equal check on two objects.
398
+ *
399
+ * @param predicate the reference object containing the partial information
400
+ * @param candidate the object to check against
401
+ * @returns true if the candidate contains all the values of the predicate
402
+ *
403
+ * @category Cache
404
+ */
405
+ const partialMatch = (predicate, candidate) => {
406
+ if (predicate === candidate)
407
+ return true;
408
+ // if one or the other is nullish, there can't be equality
409
+ if (!predicate || !candidate)
410
+ return false;
411
+ // we only perform recursive check on objects
412
+ if (typeof predicate !== 'object') {
413
+ return false;
414
+ }
415
+ // recursively call for partial match based on the predicate
416
+ return Object.keys(predicate).every(key => partialMatch(predicate[key], candidate[key]));
417
+ };
418
+ /**
419
+ * checks if passed error code is included in
420
+ * Tombstone errors list
421
+ *
422
+ * @param errorCode as {@link Amity.ServerError}
423
+ * @returns success boolean if the given errorCode
424
+ * is included in Tombstone errors list
425
+ *
426
+ * @category Cache
427
+ */
428
+ const checkIfShouldGoesToTombstone = (errorCode) => [400400 /* Amity.ServerError.ITEM_NOT_FOUND */, 400300 /* Amity.ServerError.FORBIDDEN */].includes(errorCode);
429
+
339
430
  /* eslint-disable no-use-before-define */
340
431
  /**
341
432
  * Type guard to check and cast that a given async function is has the ".locally" property
@@ -394,7 +485,7 @@ const isLocal = (model) => {
394
485
  *
395
486
  * @hidden
396
487
  */
397
- const isFresh = (model, lifeSpan = 0) => {
488
+ const isFresh = (model, lifeSpan = CACHE_LIFESPAN) => {
398
489
  var _a;
399
490
  return Date.now() - ((_a = model === null || model === void 0 ? void 0 : model.cachedAt) !== null && _a !== void 0 ? _a : 0) <= lifeSpan;
400
491
  };
@@ -428,12 +519,10 @@ const createQuery = (func, ...args) => ({ func, args });
428
519
  *
429
520
  * @category Query
430
521
  */
431
- const queryOptions = (policy, lifeSpan = 2 * 60 * 1000) => {
522
+ const queryOptions = (policy, lifeSpan = CACHE_LIFESPAN) => {
432
523
  if (policy === 'cache_only')
433
524
  return { lifeSpan: Infinity };
434
- if (policy === 'server_only')
435
- return { lifeSpan: 0 };
436
- return { lifeSpan };
525
+ return { lifeSpan: lifeSpan < CACHE_LIFESPAN ? CACHE_LIFESPAN : lifeSpan };
437
526
  };
438
527
  /**
439
528
  * ```js
@@ -454,8 +543,9 @@ const queryOptions = (policy, lifeSpan = 2 * 60 * 1000) => {
454
543
  */
455
544
  const runQuery = ({ func, args }, callback, options = queryOptions('cache_then_server')) => {
456
545
  let local;
546
+ const { lifeSpan } = queryOptions('cache_then_server', options.lifeSpan);
457
547
  // offline first
458
- if (isOffline(func) && options.lifeSpan !== 0) {
548
+ if (isOffline(func)) {
459
549
  try {
460
550
  local = isMutator(func) ? func.optimistically(...args) : func.locally(...args);
461
551
  }
@@ -466,7 +556,7 @@ const runQuery = ({ func, args }, callback, options = queryOptions('cache_then_s
466
556
  error,
467
557
  }));
468
558
  }
469
- const shouldAbort = isCachable(local) && isFresh(local, options.lifeSpan);
559
+ const shouldAbort = isCachable(local) && isFresh(local, lifeSpan);
470
560
  callback === null || callback === void 0 ? void 0 : callback(createSnapshot(local, {
471
561
  origin: 'local',
472
562
  loading: !(isFetcher(func) && shouldAbort),
@@ -533,54 +623,6 @@ function createSnapshot(data, options) {
533
623
  return Object.assign(Object.assign({}, options), { data });
534
624
  }
535
625
 
536
- // Note:
537
- // this file should contain a suite of filtering utilities to help the
538
- // local version of the query functions.
539
- /**
540
- * Filter a given collection with strict equality against a param
541
- *
542
- * @param collection the collection to filter
543
- * @param key the key of the collection's items to challenge
544
- * @param value the expected value
545
- * @returns a filtered collection with items only matching the criteria
546
- *
547
- * @hidden
548
- */
549
- const filterByPropEquality = (collection, key, value) => (value !== undefined ? collection.filter(item => item[key] === value) : collection);
550
- const filterByPropInclusion = (collection, key, value) => (value !== undefined ? collection.filter(item => value.includes(item[key])) : collection);
551
-
552
- // Note:
553
- // this file should contain a suite of sorting utilities to help the
554
- // local version of the query functions.
555
- /**
556
- * Alphabetic sorting of objects having a displayName
557
- */
558
- const sortByDisplayName = ({ displayName: a }, { displayName: b }) => {
559
- if (a === b)
560
- return 0;
561
- return a < b ? -1 : 1;
562
- };
563
- /**
564
- * Sorting a collection by their apparition order (oldest first)
565
- */
566
- const sortByChannelSegment = ({ channelSegment: a }, { channelSegment: b }) => a - b;
567
- /**
568
- * Sorting a collection by their apparition order (oldest first)
569
- */
570
- const sortBySegmentNumber = ({ segmentNumber: a }, { segmentNumber: b }) => a - b;
571
- /**
572
- * Sorting a collection by its oldest items
573
- */
574
- const sortByFirstCreated = ({ createdAt: a }, { createdAt: b }) => new Date(a).valueOf() - new Date(b).valueOf();
575
- /**
576
- * Sorting a collection by its newest items
577
- */
578
- const sortByLastCreated = ({ createdAt: a }, { createdAt: b }) => new Date(b).valueOf() - new Date(a).valueOf();
579
- /**
580
- * Sorting a collection by the items with most recent activity
581
- */
582
- const sortByLastActivity = ({ lastActivity: a }, { lastActivity: b }) => new Date(b).valueOf() - new Date(a).valueOf();
583
-
584
626
  /* eslint-disable max-classes-per-file */
585
627
  /**
586
628
  * Generic ASC error
@@ -670,257 +712,62 @@ const setActiveClient = (client) => {
670
712
  activeClient = client;
671
713
  };
672
714
 
673
- /* eslint-disable @typescript-eslint/ban-types */
674
- let tasks = [];
675
- let timer;
676
715
  /**
677
- * Add a function to run just before the next tick
716
+ * ```js
717
+ * import { enableCache } from '@amityco/ts-sdk'
718
+ * enableCache()
719
+ * ```
678
720
  *
679
- * @param task function to schedule for later execution
721
+ * Adds a new {@link Amity.Cache} object to
722
+ * an {@link Amity.Client} instance
723
+ *
724
+ * @param prevState a previous state of cache instance (useful for SSR)
725
+ * @param persistIf a function to determine if an entry inserted in cache
726
+ * is destined to be also saved in the persistent storage when
727
+ * calling {@link backupCache}
728
+ *
729
+ * @category Cache API
680
730
  */
681
- const scheduleTask = (task) => {
682
- clearTimeout(timer);
683
- tasks.push(task);
684
- timer = setTimeout(() => {
685
- tasks.forEach(fn => fn());
686
- tasks = [];
687
- }, 0);
731
+ const enableCache = (prevState = {}, persistIf) => {
732
+ const client = getActiveClient();
733
+ if (client.cache)
734
+ return;
735
+ client.log('cache/api/enableCache');
736
+ client.cache = { data: prevState, persistIf };
688
737
  };
689
738
 
690
- /* eslint-disable @typescript-eslint/no-unused-vars */
691
- // need to sync up the list
692
- const WS_EVENTS = [
693
- 'disconnected',
694
- 'error',
695
- 'connect_error',
696
- 'reconnect_error',
697
- 'reconnect_failed',
698
- 'v3.channel.didCreate',
699
- 'v3.channel.didUpdate',
700
- 'v3.channel.didDelete',
701
- 'v3.channel.didJoin',
702
- 'v3.channel.didLeave',
703
- 'v3.channel.didAddUsers',
704
- 'v3.channel.didRemoveUsers',
705
- 'v3.channel.didBan',
706
- 'v3.channel.didUnban',
707
- 'v3.message.didCreate',
708
- 'v3.message.didUpdate',
709
- 'v3.message.didDelete',
710
- ];
711
- const MQTT_EVENTS = [
712
- 'connect',
713
- 'message',
714
- 'disconnect',
715
- 'error',
716
- 'close',
717
- 'end',
718
- 'reconnect',
719
- ];
720
- /** @hidden */
721
- const createEventEmitter = () => {
722
- return mitt();
723
- };
724
739
  /**
725
- * Wraps the websocket events into the client's event emitter for
726
- * abstraction of transport.
740
+ * ```js
741
+ * import { disableCache } from '@amityco/ts-sdk'
742
+ * disableCache()
743
+ * ```
727
744
  *
728
- * @param ws A websocket connection to listen from
729
- * @param emitter An event emitter to wire the events to
745
+ * Wipes the existing {@link Amity.Cache} object attached to
746
+ * an {@link Amity.Client} instance
730
747
  *
731
- * @category Transport
732
- * @hidden
748
+ * @category Cache API
733
749
  */
734
- const proxyWebsocketEvents = (ws, emitter) => {
735
- WS_EVENTS.forEach(event => {
736
- ws.on(event, (param) => emitter.emit(event, param));
737
- });
738
- };
739
- const proxyMqttEvents = (mqttClient, emitter) => {
740
- MQTT_EVENTS.forEach(event => {
741
- mqttClient.on(event, (...params) => emitter.emit(event, params.length === 1 ? params[0] : params));
742
- });
743
- // @ts-ignore
744
- mqttClient.on('message', (topic, payload) => {
745
- const message = JSON.parse(payload.toString());
746
- emitter.emit(message.eventType, message.data);
747
- });
748
- };
750
+ const disableCache = () => {
751
+ const client = getActiveClient();
752
+ if (!client.cache)
753
+ return;
754
+ client.log('cache/api/disableCache');
755
+ // we do this so that testing if cache is enabled
756
+ // is only `if (client.cache)
757
+ delete client.cache;
758
+ };
759
+
749
760
  /**
750
- * Standardize the subscription of SSE through web sockets
761
+ * ```js
762
+ * import { restoreCache } from '@amityco/ts-sdk'
763
+ * const success = await restoreCache()
764
+ * ```
751
765
  *
752
- * @param client The current client for which to subscribe the event to
753
- * @param namespace A unique name for the logger
754
- * @param event The websocket event name
755
- * @param fn A wrapper for the callback.
756
- * @returns A dispose function to unsubscribe to the event
766
+ * Reads a previously saved {@link Amity.Cache} from a persistent storage,
767
+ * and inserts it into the current {@link Amity.Cache} instance.
757
768
  *
758
- * @category Transport
759
- * @hidden
760
- */
761
- const createEventSubscriber = (client, namespace, event, fn) => {
762
- const { log, emitter } = client;
763
- const timestamp = Date.now();
764
- log(`${namespace}(tmpid: ${timestamp}) > listen`);
765
- const handler = (...payload) => {
766
- log(`${namespace}(tmpid: ${timestamp}) > trigger`, payload);
767
- fn(...payload);
768
- };
769
- emitter.on(event, handler);
770
- return () => {
771
- log(`${namespace}(tmpid: ${timestamp}) > dispose`);
772
- emitter.off(event, handler);
773
- };
774
- };
775
- /**
776
- * Wrapper around dispatch event
777
- *
778
- * @hidden
779
- */
780
- const fireEvent = (event, payload) => {
781
- const { emitter } = getActiveClient();
782
- scheduleTask(() => {
783
- emitter.emit(event, payload);
784
- });
785
- };
786
-
787
- let activeUser = null;
788
- /**
789
- * for internal use
790
- */
791
- const getActiveUser = () => {
792
- if (!activeUser) {
793
- throw new ASCError('Connect client first', 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "fatal" /* Amity.ErrorLevel.FATAL */);
794
- }
795
- return activeUser;
796
- };
797
- const setActiveUser = (user) => {
798
- activeUser = {
799
- _id: user._id,
800
- userId: user.userId,
801
- path: user.path,
802
- };
803
- };
804
-
805
- var SubscriptionLevels;
806
- (function (SubscriptionLevels) {
807
- SubscriptionLevels["COMMUNITY"] = "community";
808
- SubscriptionLevels["POST"] = "post";
809
- SubscriptionLevels["COMMENT"] = "comment";
810
- SubscriptionLevels["POST_AND_COMMENT"] = "post_and_comment";
811
- SubscriptionLevels["USER"] = "user";
812
- })(SubscriptionLevels || (SubscriptionLevels = {}));
813
- const getCommunityUserTopic = (path, level) => {
814
- switch (level) {
815
- case 'post':
816
- return `${path}/post/+`;
817
- case 'comment':
818
- return `${path}/post/+/comment/+`;
819
- case 'post_and_comment':
820
- return `${path}/post/#`;
821
- default:
822
- return path;
823
- }
824
- };
825
- const getCommunityTopic = ({ path }, level = SubscriptionLevels.COMMUNITY) => getCommunityUserTopic(path, level);
826
- const getUserTopic = ({ path }, level = SubscriptionLevels.USER) => getCommunityUserTopic(level === SubscriptionLevels.USER ? path : path.replace(/^(\w*)/, '$1/social'), level);
827
- const getPostTopic = ({ path }, level = SubscriptionLevels.POST) => {
828
- switch (level) {
829
- case 'comment':
830
- return `${path}/comment/+`;
831
- default:
832
- return path;
833
- }
834
- };
835
- const getCommentTopic = ({ path }) => {
836
- return path;
837
- };
838
- const getMyFollowersTopic = () => {
839
- const user = getActiveUser();
840
- const [networkId] = user.path.split('/user/');
841
- return `${networkId}/membership/${user._id}/+/+`;
842
- };
843
- const getMyFollowingsTopic = () => {
844
- const user = getActiveUser();
845
- const [networkId] = user.path.split('/user/');
846
- return `${networkId}/membership/+/${user._id}/+`;
847
- };
848
- /** @hidden */
849
- const getNetworkTopic = () => {
850
- const user = getActiveUser();
851
- const [networkId] = user.path.split('/user/');
852
- return networkId;
853
- };
854
- let mqttAccessToken;
855
- let mqttUserId;
856
- function subscribeTopic(topic, callback) {
857
- const { mqtt, emitter, accessToken } = getActiveClient();
858
- const user = getActiveUser();
859
- if (mqttAccessToken !== accessToken || mqttUserId !== user._id) {
860
- mqttAccessToken = accessToken;
861
- mqttUserId = user._id;
862
- mqtt.connect({ accessToken: mqttAccessToken, userId: mqttUserId });
863
- proxyMqttEvents(mqtt, emitter);
864
- }
865
- return mqtt.subscribe(topic, callback);
866
- }
867
-
868
- /**
869
- * ```js
870
- * import { enableCache } from '@amityco/ts-sdk'
871
- * enableCache()
872
- * ```
873
- *
874
- * Adds a new {@link Amity.Cache} object to
875
- * an {@link Amity.Client} instance
876
- *
877
- * @param prevState a previous state of cache instance (useful for SSR)
878
- * @param persistIf a function to determine if an entry inserted in cache
879
- * is destined to be also saved in the persistent storage when
880
- * calling {@link backupCache}
881
- *
882
- * @category Cache API
883
- */
884
- const enableCache = (prevState = {}, persistIf) => {
885
- const client = getActiveClient();
886
- if (client.cache)
887
- return;
888
- client.log('cache/api/enableCache');
889
- client.cache = { data: prevState, persistIf };
890
- };
891
-
892
- /**
893
- * ```js
894
- * import { disableCache } from '@amityco/ts-sdk'
895
- * disableCache()
896
- * ```
897
- *
898
- * Wipes the existing {@link Amity.Cache} object attached to
899
- * an {@link Amity.Client} instance
900
- *
901
- * @category Cache API
902
- */
903
- const disableCache = () => {
904
- const client = getActiveClient();
905
- if (!client.cache)
906
- return;
907
- client.log('cache/api/disableCache');
908
- // we do this so that testing if cache is enabled
909
- // is only `if (client.cache)
910
- delete client.cache;
911
- };
912
-
913
- /**
914
- * ```js
915
- * import { restoreCache } from '@amityco/ts-sdk'
916
- * const success = await restoreCache()
917
- * ```
918
- *
919
- * Reads a previously saved {@link Amity.Cache} from a persistent storage,
920
- * and inserts it into the current {@link Amity.Cache} instance.
921
- *
922
- * The strategy for persistent storage will depend on the runtime,
923
- * which is supported by @react-native-async-storage/async-storage.
769
+ * The strategy for persistent storage will depend on the runtime,
770
+ * which is supported by @react-native-async-storage/async-storage.
924
771
  *
925
772
  * The current userId will be appended to the given storageKey to ensures
926
773
  * the cached data concerns only the current user.
@@ -949,7 +796,6 @@ const restoreCache = async (storageKey = 'amitySdk') => {
949
796
  return true;
950
797
  };
951
798
 
952
- /* eslint-disable @typescript-eslint/no-unused-vars */
953
799
  /**
954
800
  * ```js
955
801
  * import { backupCache } from '@amityco/ts-sdk'
@@ -1016,62 +862,6 @@ const wipeCache = async (storageKey = 'amitySdk') => {
1016
862
  return true;
1017
863
  };
1018
864
 
1019
- /**
1020
- * Shallow-clones an object and sort its keys
1021
- *
1022
- * @param source a plain object to clone
1023
- * @returns a clone of source, with keys sorted with default javascript sorting
1024
- *
1025
- * @category Cache
1026
- * @hidden
1027
- */
1028
- const normalize = (source) => Object.keys(source)
1029
- .sort()
1030
- .reduce((acc, key) => (Object.assign(Object.assign({}, acc), { [key]: source[key] })), {});
1031
- /**
1032
- * Encodes a given {@link Amity.CacheKey} to a plain string
1033
- *
1034
- * @param key the key to encode
1035
- * @returns an encoded string
1036
- *
1037
- * @category Cache
1038
- * @hidden
1039
- */
1040
- const encodeKey = (key) => JSON.stringify(key, (_, val) => (typeof val === 'object' ? normalize(val) : val));
1041
- /**
1042
- * Decodes a string back into a {@link Amity.CacheKey}
1043
-
1044
- *
1045
- * @param key the string key to decode
1046
- * @returns a plain Amity.CacheKey object.
1047
- *
1048
- * @category Cache
1049
- * @hidden
1050
- */
1051
- const decodeKey = (key) => JSON.parse(key);
1052
- /**
1053
- * Performs a recursive partial deep equal check on two objects.
1054
- *
1055
- * @param predicate the reference object containing the partial information
1056
- * @param candidate the object to check against
1057
- * @returns true if the candidate contains all the values of the predicate
1058
- *
1059
- * @category Cache
1060
- */
1061
- const partialMatch = (predicate, candidate) => {
1062
- if (predicate === candidate)
1063
- return true;
1064
- // if one or the other is nullish, there can't be equality
1065
- if (!predicate || !candidate)
1066
- return false;
1067
- // we only perform recursive check on objects
1068
- if (typeof predicate !== 'object') {
1069
- return false;
1070
- }
1071
- // recursively call for partial match based on the predicate
1072
- return Object.keys(predicate).every(key => partialMatch(predicate[key], candidate[key]));
1073
- };
1074
-
1075
865
  /**
1076
866
  * ```js
1077
867
  * import { queryCache } from '@amityco/ts-sdk'
@@ -1188,66 +978,348 @@ const mergeInCache = (key, mutation, options) => {
1188
978
  };
1189
979
 
1190
980
  /**
1191
- * ```js
1192
- * import { upsertInCache } from '@amityco/ts-sdk'
1193
- * upsertInCache<Amity.User>(["user", "foobar"], user)
1194
- * ```
1195
- *
1196
- * Insert or update any provided value as {@link Amity.CacheEntry} for
1197
- * the matching {@link Amity.CacheKey}
1198
- *
1199
- * @param key the key to save the object to
1200
- * @param data the object to save
1201
- * @param options customisation object around cache behavior (default gives a 2mn lifespan)
1202
- * @returns a success boolean if the object was saved in cache
981
+ * ```js
982
+ * import { upsertInCache } from '@amityco/ts-sdk'
983
+ * upsertInCache<Amity.User>(["user", "foobar"], user)
984
+ * ```
985
+ *
986
+ * Insert or update any provided value as {@link Amity.CacheEntry} for
987
+ * the matching {@link Amity.CacheKey}
988
+ *
989
+ * @param key the key to save the object to
990
+ * @param data the object to save
991
+ * @param options customisation object around cache behavior (default gives a 2mn lifespan)
992
+ * @returns a success boolean if the object was saved in cache
993
+ *
994
+ * @category Cache API
995
+ * @hidden
996
+ */
997
+ const upsertInCache = (key, data, options = { cachedAt: Date.now() }) => {
998
+ const { log, cache } = getActiveClient();
999
+ if (!cache)
1000
+ return false;
1001
+ log('cache/api/upsertInCache', { key, data, options });
1002
+ const cached = pullFromCache(key);
1003
+ return cached ? mergeInCache(key, data, options) : pushToCache(key, data, options);
1004
+ };
1005
+
1006
+ /**
1007
+ * ```js
1008
+ * import { dropFromCache } from '@amityco/ts-sdk'
1009
+ * const success = dropFromCache(['user', 'foobar'])
1010
+ * ```
1011
+ *
1012
+ * Removes an existing {@link Amity.CacheEntry} from the {@link Amity.Client}'s
1013
+ * {@link Amity.Cache} from a given {@link Amity.CacheKey}
1014
+ *
1015
+ * @param key The key of the object to delete
1016
+ * @param exact If false, the function will delete all keys satisfying the given key
1017
+ * @returns A success boolean if the object was deleted
1018
+ *
1019
+ * @category Cache API
1020
+ */
1021
+ const dropFromCache = (key, exact = false) => {
1022
+ const { log, cache } = getActiveClient();
1023
+ if (!cache)
1024
+ return false;
1025
+ log('cache/api/dropFromCache', { key, exact });
1026
+ if (!exact) {
1027
+ return Object.keys(cache.data)
1028
+ .map(stringKey => decodeKey(stringKey))
1029
+ .filter(candidate => partialMatch(key, candidate))
1030
+ .map(filteredKey => dropFromCache(filteredKey, true))
1031
+ .every(returned => returned);
1032
+ }
1033
+ const encodedKey = encodeKey(key);
1034
+ if (encodedKey in cache.data) {
1035
+ delete cache.data[encodedKey];
1036
+ return true;
1037
+ }
1038
+ return false;
1039
+ };
1040
+
1041
+ // Note:
1042
+ // this file should contain a suite of filtering utilities to help the
1043
+ // local version of the query functions.
1044
+ /**
1045
+ * Filter a given collection with strict equality against a param
1046
+ *
1047
+ * @param collection the collection to filter
1048
+ * @param key the key of the collection's items to challenge
1049
+ * @param value the expected value
1050
+ * @returns a filtered collection with items only matching the criteria
1051
+ *
1052
+ * @hidden
1053
+ */
1054
+ const filterByPropEquality = (collection, key, value) => (value !== undefined ? collection.filter(item => item[key] === value) : collection);
1055
+ const filterByPropInclusion = (collection, key, value) => (value !== undefined ? collection.filter(item => value.includes(item[key])) : collection);
1056
+ /**
1057
+ * Filter a channel collection by membership of the userId
1058
+ *
1059
+ * @param collection the channel collection to filter
1060
+ * @param membership the membership to be filtered by
1061
+ * @param userId user id to be filtered by
1062
+ * @returns a filtered collection with items only matching the criteria
1063
+ *
1064
+ * @hidden
1065
+ */
1066
+ const filterByChannelMembership = (collection, membership, userId) => {
1067
+ if (membership === 'all') {
1068
+ return collection;
1069
+ }
1070
+ return collection.filter(c => {
1071
+ var _a;
1072
+ // get resolver for the channel by user
1073
+ const channelUserCacheKey = getResolver('channelUsers')({
1074
+ channelId: c.channelId,
1075
+ userId,
1076
+ });
1077
+ const channelUser = (_a = pullFromCache([
1078
+ 'channelUsers',
1079
+ 'get',
1080
+ channelUserCacheKey,
1081
+ ])) === null || _a === void 0 ? void 0 : _a.data;
1082
+ // if channel user is defined for user, user is a member
1083
+ if (membership === 'member' && channelUser) {
1084
+ return true;
1085
+ }
1086
+ // if channel user by user id not found, user is not a member of that
1087
+ // channel
1088
+ if (membership === 'notMember' && !channelUser) {
1089
+ return true;
1090
+ }
1091
+ return false;
1092
+ });
1093
+ };
1094
+
1095
+ // Note:
1096
+ // this file should contain a suite of sorting utilities to help the
1097
+ // local version of the query functions.
1098
+ /**
1099
+ * Alphabetic sorting of objects having a displayName
1100
+ */
1101
+ const sortByDisplayName = ({ displayName: a }, { displayName: b }) => {
1102
+ if (a === b)
1103
+ return 0;
1104
+ return a < b ? -1 : 1;
1105
+ };
1106
+ /**
1107
+ * Sorting a collection by their apparition order (oldest first)
1108
+ */
1109
+ const sortByChannelSegment = ({ channelSegment: a }, { channelSegment: b }) => a - b;
1110
+ /**
1111
+ * Sorting a collection by their apparition order (oldest first)
1112
+ */
1113
+ const sortBySegmentNumber = ({ segmentNumber: a }, { segmentNumber: b }) => a - b;
1114
+ /**
1115
+ * Sorting a collection by its oldest items
1116
+ */
1117
+ const sortByFirstCreated = ({ createdAt: a }, { createdAt: b }) => new Date(a).valueOf() - new Date(b).valueOf();
1118
+ /**
1119
+ * Sorting a collection by its newest items
1120
+ */
1121
+ const sortByLastCreated = ({ createdAt: a }, { createdAt: b }) => new Date(b).valueOf() - new Date(a).valueOf();
1122
+ /**
1123
+ * Sorting a collection by the items with most recent activity
1124
+ */
1125
+ const sortByLastActivity = ({ lastActivity: a }, { lastActivity: b }) => new Date(b).valueOf() - new Date(a).valueOf();
1126
+
1127
+ /* eslint-disable @typescript-eslint/ban-types */
1128
+ let tasks = [];
1129
+ let timer;
1130
+ /**
1131
+ * Add a function to run just before the next tick
1132
+ *
1133
+ * @param task function to schedule for later execution
1134
+ */
1135
+ const scheduleTask = (task) => {
1136
+ clearTimeout(timer);
1137
+ tasks.push(task);
1138
+ timer = setTimeout(() => {
1139
+ tasks.forEach(fn => fn());
1140
+ tasks = [];
1141
+ }, 0);
1142
+ };
1143
+
1144
+ /* eslint-disable @typescript-eslint/no-unused-vars */
1145
+ // need to sync up the list
1146
+ const WS_EVENTS = [
1147
+ 'disconnected',
1148
+ 'error',
1149
+ 'connect_error',
1150
+ 'reconnect_error',
1151
+ 'reconnect_failed',
1152
+ 'v3.channel.didCreate',
1153
+ 'v3.channel.didUpdate',
1154
+ 'v3.channel.didDelete',
1155
+ 'v3.channel.didJoin',
1156
+ 'v3.channel.didLeave',
1157
+ 'v3.channel.didAddUsers',
1158
+ 'v3.channel.didRemoveUsers',
1159
+ 'v3.channel.didBan',
1160
+ 'v3.channel.didUnban',
1161
+ 'v3.message.didFetch',
1162
+ 'v3.message.didCreate',
1163
+ 'v3.message.didUpdate',
1164
+ 'v3.message.didDelete',
1165
+ ];
1166
+ const MQTT_EVENTS = [
1167
+ 'connect',
1168
+ 'message',
1169
+ 'disconnect',
1170
+ 'error',
1171
+ 'close',
1172
+ 'end',
1173
+ 'reconnect',
1174
+ ];
1175
+ /** @hidden */
1176
+ const createEventEmitter = () => {
1177
+ return mitt();
1178
+ };
1179
+ /**
1180
+ * Wraps the websocket events into the client's event emitter for
1181
+ * abstraction of transport.
1182
+ *
1183
+ * @param ws A websocket connection to listen from
1184
+ * @param emitter An event emitter to wire the events to
1185
+ *
1186
+ * @category Transport
1187
+ * @hidden
1188
+ */
1189
+ const proxyWebsocketEvents = (ws, emitter) => {
1190
+ WS_EVENTS.forEach(event => {
1191
+ ws.on(event, (param) => emitter.emit(event, param));
1192
+ });
1193
+ };
1194
+ const proxyMqttEvents = (mqttClient, emitter) => {
1195
+ MQTT_EVENTS.forEach(event => {
1196
+ mqttClient.on(event, (...params) => emitter.emit(event, params.length === 1 ? params[0] : params));
1197
+ });
1198
+ // @ts-ignore
1199
+ mqttClient.on('message', (topic, payload) => {
1200
+ const message = JSON.parse(payload.toString());
1201
+ emitter.emit(message.eventType, message.data);
1202
+ });
1203
+ };
1204
+ /**
1205
+ * Standardize the subscription of SSE through web sockets
1206
+ *
1207
+ * @param client The current client for which to subscribe the event to
1208
+ * @param namespace A unique name for the logger
1209
+ * @param event The websocket event name
1210
+ * @param fn A wrapper for the callback.
1211
+ * @returns A dispose function to unsubscribe to the event
1212
+ *
1213
+ * @category Transport
1214
+ * @hidden
1215
+ */
1216
+ const createEventSubscriber = (client, namespace, event, fn) => {
1217
+ const { log, emitter } = client;
1218
+ const timestamp = Date.now();
1219
+ log(`${namespace}(tmpid: ${timestamp}) > listen`);
1220
+ const handler = (...payload) => {
1221
+ log(`${namespace}(tmpid: ${timestamp}) > trigger`, payload);
1222
+ fn(...payload);
1223
+ };
1224
+ emitter.on(event, handler);
1225
+ return () => {
1226
+ log(`${namespace}(tmpid: ${timestamp}) > dispose`);
1227
+ emitter.off(event, handler);
1228
+ };
1229
+ };
1230
+ /**
1231
+ * Wrapper around dispatch event
1203
1232
  *
1204
- * @category Cache API
1205
1233
  * @hidden
1206
1234
  */
1207
- const upsertInCache = (key, data, options = { cachedAt: Date.now() }) => {
1208
- const { log, cache } = getActiveClient();
1209
- if (!cache)
1210
- return false;
1211
- log('cache/api/upsertInCache', { key, data, options });
1212
- const cached = pullFromCache(key);
1213
- return cached ? mergeInCache(key, data, options) : pushToCache(key, data, options);
1235
+ const fireEvent = (event, payload) => {
1236
+ const { emitter } = getActiveClient();
1237
+ scheduleTask(() => {
1238
+ emitter.emit(event, payload);
1239
+ });
1214
1240
  };
1215
1241
 
1242
+ let activeUser = null;
1216
1243
  /**
1217
- * ```js
1218
- * import { dropFromCache } from '@amityco/ts-sdk'
1219
- * const success = dropFromCache(['user', 'foobar'])
1220
- * ```
1221
- *
1222
- * Removes an existing {@link Amity.CacheEntry} from the {@link Amity.Client}'s
1223
- * {@link Amity.Cache} from a given {@link Amity.CacheKey}
1224
- *
1225
- * @param key The key of the object to delete
1226
- * @param exact If false, the function will delete all keys satisfying the given key
1227
- * @returns A success boolean if the object was deleted
1228
- *
1229
- * @category Cache API
1244
+ * for internal use
1230
1245
  */
1231
- const dropFromCache = (key, exact = false) => {
1232
- const { log, cache } = getActiveClient();
1233
- if (!cache)
1234
- return false;
1235
- log('cache/api/dropFromCache', { key, exact });
1236
- if (!exact) {
1237
- return Object.keys(cache.data)
1238
- .map(stringKey => decodeKey(stringKey))
1239
- .filter(candidate => partialMatch(key, candidate))
1240
- .map(filteredKey => dropFromCache(filteredKey, true))
1241
- .every(returned => returned);
1242
- }
1243
- const encodedKey = encodeKey(key);
1244
- if (encodedKey in cache.data) {
1245
- delete cache.data[encodedKey];
1246
- return true;
1246
+ const getActiveUser = () => {
1247
+ if (!activeUser) {
1248
+ throw new ASCError('Connect client first', 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "fatal" /* Amity.ErrorLevel.FATAL */);
1247
1249
  }
1248
- return false;
1250
+ return activeUser;
1251
+ };
1252
+ const setActiveUser = (user) => {
1253
+ activeUser = {
1254
+ _id: user._id,
1255
+ userId: user.userId,
1256
+ path: user.path,
1257
+ };
1249
1258
  };
1250
1259
 
1260
+ var SubscriptionLevels;
1261
+ (function (SubscriptionLevels) {
1262
+ SubscriptionLevels["COMMUNITY"] = "community";
1263
+ SubscriptionLevels["POST"] = "post";
1264
+ SubscriptionLevels["COMMENT"] = "comment";
1265
+ SubscriptionLevels["POST_AND_COMMENT"] = "post_and_comment";
1266
+ SubscriptionLevels["USER"] = "user";
1267
+ })(SubscriptionLevels || (SubscriptionLevels = {}));
1268
+ const getCommunityUserTopic = (path, level) => {
1269
+ switch (level) {
1270
+ case 'post':
1271
+ return `${path}/post/+`;
1272
+ case 'comment':
1273
+ return `${path}/post/+/comment/+`;
1274
+ case 'post_and_comment':
1275
+ return `${path}/post/#`;
1276
+ default:
1277
+ return path;
1278
+ }
1279
+ };
1280
+ const getCommunityTopic = ({ path }, level = SubscriptionLevels.COMMUNITY) => getCommunityUserTopic(path, level);
1281
+ const getUserTopic = ({ path }, level = SubscriptionLevels.USER) => getCommunityUserTopic(level === SubscriptionLevels.USER ? path : path.replace(/^(\w*)/, '$1/social'), level);
1282
+ const getPostTopic = ({ path }, level = SubscriptionLevels.POST) => {
1283
+ switch (level) {
1284
+ case 'comment':
1285
+ return `${path}/comment/+`;
1286
+ default:
1287
+ return path;
1288
+ }
1289
+ };
1290
+ const getCommentTopic = ({ path }) => {
1291
+ return path;
1292
+ };
1293
+ const getMyFollowersTopic = () => {
1294
+ const user = getActiveUser();
1295
+ const [networkId] = user.path.split('/user/');
1296
+ return `${networkId}/membership/${user._id}/+/+`;
1297
+ };
1298
+ const getMyFollowingsTopic = () => {
1299
+ const user = getActiveUser();
1300
+ const [networkId] = user.path.split('/user/');
1301
+ return `${networkId}/membership/+/${user._id}/+`;
1302
+ };
1303
+ /** @hidden */
1304
+ const getNetworkTopic = () => {
1305
+ const user = getActiveUser();
1306
+ const [networkId] = user.path.split('/user/');
1307
+ return networkId;
1308
+ };
1309
+ let mqttAccessToken;
1310
+ let mqttUserId;
1311
+ function subscribeTopic(topic, callback) {
1312
+ const { mqtt, emitter, accessToken } = getActiveClient();
1313
+ const user = getActiveUser();
1314
+ if (mqttAccessToken !== accessToken || mqttUserId !== user._id) {
1315
+ mqttAccessToken = accessToken;
1316
+ mqttUserId = user._id;
1317
+ mqtt.connect({ accessToken: mqttAccessToken, userId: mqttUserId });
1318
+ proxyMqttEvents(mqtt, emitter);
1319
+ }
1320
+ return mqtt.subscribe(topic, callback);
1321
+ }
1322
+
1251
1323
  /**
1252
1324
  * Creates a pre-configured logger instance
1253
1325
  *
@@ -1310,21 +1382,8 @@ const unwrapPayload = (response) => {
1310
1382
  * Axios param serializer (for querystring)
1311
1383
  * @hidden
1312
1384
  */
1313
- const stringify = (params, prefix) => {
1314
- return Object.entries(params)
1315
- .filter(([, value]) => value !== undefined)
1316
- .map(([name, value]) => {
1317
- const key = prefix ? `${prefix}[${name}]` : name;
1318
- if (Array.isArray(value)) {
1319
- return value.map(item => `${key}[]=${item}`).join('&');
1320
- }
1321
- if (typeof value !== 'object') {
1322
- return `${key}=${value}`;
1323
- }
1324
- // @ts-ignore
1325
- return stringify(value, prefix !== null && prefix !== void 0 ? prefix : name);
1326
- })
1327
- .join('&');
1385
+ const stringify = (params) => {
1386
+ return qs.stringify(params, { arrayFormat: 'brackets' });
1328
1387
  };
1329
1388
  /**
1330
1389
  * Creates a pre-configured axios instance
@@ -20564,6 +20623,61 @@ const isConnected = () => {
20564
20623
  client.ws.connected);
20565
20624
  };
20566
20625
 
20626
+ /**
20627
+ * ```js
20628
+ * import { isInTombstone } from '@amityco/ts-sdk'
20629
+ * const user = isInTombstone(["message", "messageId"])
20630
+ * ```
20631
+ *
20632
+ * Checks if the {@link Amity.TombstoneCacheOptions} exists
20633
+ * in cache and it's not expired means it's in tombstone
20634
+ * and we throw an Error
20635
+ *
20636
+ * @param model the model to check
20637
+ * @param modelId the object id to check
20638
+ * @returns the matching cache entry, or undefined.
20639
+ *
20640
+ * @category Cache API
20641
+ */
20642
+ const isInTombstone = (model, modelId) => {
20643
+ const { log, cache } = getActiveClient();
20644
+ const key = [model, CACHE_KEY_TOMBSTONE, modelId];
20645
+ if (!cache)
20646
+ return;
20647
+ log('cache/api/isInTombstone', key);
20648
+ const isInTombstone = pullFromCache(key);
20649
+ const { lifeSpan } = queryOptions('cache_then_server', CACHE_LIFESPAN_TOMBSTONE);
20650
+ if (isInTombstone && isFresh(isInTombstone.data, lifeSpan)) {
20651
+ throw new ASCApiError('Item not found!', 400400 /* Amity.ServerError.ITEM_NOT_FOUND */, "error" /* Amity.ErrorLevel.ERROR */);
20652
+ }
20653
+ };
20654
+
20655
+ /**
20656
+ * ```js
20657
+ * import { pushToTombstone } from '@amityco/ts-sdk'
20658
+ * pushToTombstone(["message", "messageId"], { cachedAt: Date.now() })
20659
+ * ```
20660
+ *
20661
+ * Removes an existing {@link Amity.CacheEntry} from the {@link Amity.Client}'s
20662
+ * {@link Amity.Cache} from a given {@link Amity.CacheKey}
20663
+ *
20664
+ * @param model the model to remove and push to Tombstone
20665
+ * @param modelId the related model id
20666
+ *
20667
+ * @category Cache API
20668
+ * @hidden
20669
+ */
20670
+ const pushToTombstone = (model, modelId) => {
20671
+ const { log, cache } = getActiveClient();
20672
+ const cacheKey = [model, CACHE_KEY_TOMBSTONE, modelId];
20673
+ const data = { cachedAt: Date.now() };
20674
+ if (!cache)
20675
+ return;
20676
+ log('cache/api/pushToTombstone', { cacheKey, data });
20677
+ dropFromCache([model, CACHE_KEY_GET, modelId], true);
20678
+ pushToCache(cacheKey, data);
20679
+ };
20680
+
20567
20681
  /**
20568
20682
  * ```js
20569
20683
  * import { getUser } from '@amityco/ts-sdk'
@@ -20578,15 +20692,29 @@ const isConnected = () => {
20578
20692
  * @category User API
20579
20693
  * @async
20580
20694
  */
20581
- const getUser = async (userId) => {
20695
+ const getUser = async (userId, isLive = false) => {
20582
20696
  const client = getActiveClient();
20583
20697
  client.log('user/getUser', userId);
20584
- // API-FIX: endpoint should not be /list, parameters should be querystring.
20585
- const { data } = await client.http.get(`/api/v3/users/${encodeURIComponent(userId)}`);
20698
+ if (!isLive) {
20699
+ console.log('getUser will be deprecated on 25th November 2022, please use liveUser instead.');
20700
+ }
20701
+ isInTombstone('user', userId);
20702
+ let data;
20703
+ try {
20704
+ const response = await client.http.get(`/api/v3/users/${encodeURIComponent(userId)}`);
20705
+ data = response.data;
20706
+ }
20707
+ catch (error) {
20708
+ if (checkIfShouldGoesToTombstone(error === null || error === void 0 ? void 0 : error.code)) {
20709
+ pushToTombstone('user', userId);
20710
+ }
20711
+ throw error;
20712
+ }
20586
20713
  const cachedAt = client.cache && Date.now();
20587
20714
  if (client.cache)
20588
20715
  ingestInCache(data, { cachedAt });
20589
20716
  const { users } = data;
20717
+ fireEvent('user.fetched', data);
20590
20718
  return {
20591
20719
  data: users.find(user => user.userId === userId),
20592
20720
  cachedAt,
@@ -20636,6 +20764,7 @@ const getUsers = async (userIds) => {
20636
20764
  const cachedAt = client.cache && Date.now();
20637
20765
  if (client.cache)
20638
20766
  ingestInCache(data, { cachedAt });
20767
+ fireEvent('user.fetched', data);
20639
20768
  return {
20640
20769
  data: data.users,
20641
20770
  cachedAt,
@@ -20718,8 +20847,7 @@ function __rest(s, e) {
20718
20847
  const queryUsers = async (query = {}) => {
20719
20848
  const client = getActiveClient();
20720
20849
  client.log('user/queryUsers', query);
20721
- const { page, displayName, filter = 'all', sortBy = 'displayName' } = query, params = __rest(query, ["page", "displayName", "filter", "sortBy"]);
20722
- // API-FIX: parameters should be querystring.
20850
+ const { page = { limit: 10 }, displayName, filter = 'all', sortBy = 'displayName' } = query, params = __rest(query, ["page", "displayName", "filter", "sortBy"]);
20723
20851
  const { data } = await client.http.get(`/api/v3/users`, {
20724
20852
  params: Object.assign(Object.assign({}, params), { keyword: displayName, filter,
20725
20853
  sortBy, options: {
@@ -20732,9 +20860,14 @@ const queryUsers = async (query = {}) => {
20732
20860
  const cachedAt = client.cache && Date.now();
20733
20861
  if (client.cache) {
20734
20862
  ingestInCache(payload, { cachedAt });
20735
- const cacheKey = ['user', 'query', query];
20863
+ /*
20864
+ * using query as cache key over params because if the keyword, filter, sort
20865
+ * change the API will NOT cache results, when it should
20866
+ */
20867
+ const cacheKey = ['user', 'query', Object.assign(Object.assign({}, query), { options: Object.assign({}, page) })];
20736
20868
  pushToCache(cacheKey, { users: users.map(getResolver('user')), paging });
20737
20869
  }
20870
+ fireEvent('user.fetched', data);
20738
20871
  const prevPage = toPage(paging.previous);
20739
20872
  const nextPage = toPage(paging.next);
20740
20873
  return { data: users, cachedAt, prevPage, nextPage };
@@ -20759,8 +20892,13 @@ queryUsers.locally = (query = {}) => {
20759
20892
  client.log('user/queryUsers.locally', query);
20760
20893
  if (!client.cache)
20761
20894
  return;
20762
- const queryKey = ['user', 'query', query];
20763
- const { data, cachedAt } = (_a = pullFromCache(queryKey)) !== null && _a !== void 0 ? _a : {};
20895
+ const { page = { limit: 10 } } = query !== null && query !== void 0 ? query : {};
20896
+ const cacheKey = [
20897
+ 'user',
20898
+ 'query',
20899
+ Object.assign(Object.assign({}, query), { options: Object.assign({}, page) }),
20900
+ ];
20901
+ const { data, cachedAt } = (_a = pullFromCache(cacheKey)) !== null && _a !== void 0 ? _a : {};
20764
20902
  const users = (_b = data === null || data === void 0 ? void 0 : data.users.map(userId => pullFromCache(['user', 'get', userId])).filter(Boolean).map(({ data }) => data)) !== null && _b !== void 0 ? _b : [];
20765
20903
  const prevPage = toPage(data === null || data === void 0 ? void 0 : data.paging.previous);
20766
20904
  const nextPage = toPage(data === null || data === void 0 ? void 0 : data.paging.next);
@@ -20798,12 +20936,30 @@ const updateUser = async (userId, patch) => {
20798
20936
  if (client.cache)
20799
20937
  ingestInCache(data, { cachedAt });
20800
20938
  const { users } = data;
20939
+ fireEvent('user.updated', data);
20801
20940
  return {
20802
20941
  data: users.find(user => user.userId === userId),
20803
20942
  cachedAt,
20804
20943
  };
20805
20944
  };
20806
20945
 
20946
+ /**
20947
+ * ```js
20948
+ * import { onUserFetched } from '@amityco/ts-sdk'
20949
+ * const dispose = onUserFetched(user => {
20950
+ * // ...
20951
+ * })
20952
+ * ```
20953
+ *
20954
+ * Fired when a {@link Amity.User} has been fetched
20955
+ *
20956
+ * @param callback The function to call when the event was fired
20957
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
20958
+ *
20959
+ * @category User Events
20960
+ */
20961
+ const onUserFetched = (callback) => createUserEventSubscriber('user.fetched', callback);
20962
+
20807
20963
  /**
20808
20964
  * ```js
20809
20965
  * import { onUserUpdated } from '@amityco/ts-sdk'
@@ -20872,6 +21028,68 @@ const onUserUnflagged = (callback) => createUserEventSubscriber('user.unflagged'
20872
21028
  */
20873
21029
  const onUserFlagCleared = (callback) => createUserEventSubscriber('user.flagCleared', callback);
20874
21030
 
21031
+ const COLLECTION_DEFAULT_PAGINATION_LIMIT = 5;
21032
+ const COLLECTION_DEFAULT_CACHING_POLICY = 'cache_then_server';
21033
+ const ENABLE_CACHE_MESSAGE = 'For using Live Collection feature you need to enable Cache!';
21034
+ const LIVE_OBJECT_ENABLE_CACHE_MESSAGE = 'For using Live Object feature you need to enable Cache!';
21035
+ const UNSYNCED_OBJECT_CACHED_AT_MESSAGE = 'Observing unsynced object is not supported by Live Object.';
21036
+ const UNSYNCED_OBJECT_CACHED_AT_VALUE = -5;
21037
+
21038
+ /**
21039
+ * ```js
21040
+ * import { liveUser } from '@amityco/ts-sdk'
21041
+ *
21042
+ * let users = []
21043
+ * const unsub = liveUser(userId, response => merge(users, response.data))
21044
+ * ```
21045
+ *
21046
+ * Observe all mutation on a given {@link Amity.User}
21047
+ *
21048
+ * @param userId the ID of the user to observe
21049
+ * @param callback the function to call when new data are available
21050
+ * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the users
21051
+ *
21052
+ * @category Message Live Object
21053
+ */
21054
+ const liveUser = (userId, callback) => {
21055
+ const { log, cache } = getActiveClient();
21056
+ let user;
21057
+ if (!cache) {
21058
+ console.log(LIVE_OBJECT_ENABLE_CACHE_MESSAGE);
21059
+ }
21060
+ const timestamp = Date.now();
21061
+ log(`liveUser(tmpid: ${timestamp}) > listen`);
21062
+ const disposers = [];
21063
+ const dispatcher = (data) => {
21064
+ var _a;
21065
+ // we check updatedAt to see if the current snapshot is new so
21066
+ // we won't send old data back, only fresh new data
21067
+ if (user && ((_a = data.data) === null || _a === void 0 ? void 0 : _a.updatedAt) === (user === null || user === void 0 ? void 0 : user.updatedAt) && data.origin !== 'server')
21068
+ return;
21069
+ callback(data);
21070
+ user = data.data;
21071
+ };
21072
+ const realtimeRouter = (user, action) => {
21073
+ dispatcher({ loading: false, data: user, origin: 'event' });
21074
+ };
21075
+ const onFetch = () => {
21076
+ const query = createQuery(getUser, userId, true);
21077
+ runQuery(query, ({ loading, data, error, origin }) => {
21078
+ dispatcher({ loading, data, origin, error });
21079
+ if (error) {
21080
+ // Terminate observe functionality on error
21081
+ disposers.forEach(fn => fn());
21082
+ }
21083
+ });
21084
+ };
21085
+ disposers.push(onUserFetched(user => realtimeRouter(user)), onUserUpdated(user => realtimeRouter(user)), onUserDeleted(user => realtimeRouter(user)), onUserFlagged(user => realtimeRouter(user)), onUserUnflagged(user => realtimeRouter(user)), onUserFlagCleared(user => realtimeRouter(user)));
21086
+ onFetch();
21087
+ return () => {
21088
+ log(`liveUser(tmpid: ${timestamp}) > dispose`);
21089
+ disposers.forEach(fn => fn());
21090
+ };
21091
+ };
21092
+
20875
21093
  /**
20876
21094
  * ```js
20877
21095
  * import { observeUser } from '@amityco/ts-sdk'
@@ -20891,6 +21109,7 @@ const observeUser = (userId, callback) => {
20891
21109
  const { log } = getActiveClient();
20892
21110
  const timestamp = Date.now();
20893
21111
  log(`observeUser(tmpid: ${timestamp}) > listen`);
21112
+ console.log('observeUser will be deprecated on 25th November 2022, please use liveUser instead.');
20894
21113
  // wrapper function to make sure
20895
21114
  const router = (result, action) => {
20896
21115
  var _a, _b, _c;
@@ -21300,15 +21519,30 @@ const dispatchReactable = (referenceType, model) => {
21300
21519
  * @category Message API
21301
21520
  * @async
21302
21521
  */
21303
- const getMessage = async (messageId) => {
21522
+ const getMessage = async (messageId, isLive = false) => {
21304
21523
  const client = getActiveClient();
21305
21524
  client.log('message/getMessage', messageId);
21306
- // API-FIX: endpoint should not be /list, parameters should be querystring.
21307
- const { data } = await client.http.get(`/api/v3/messages/${encodeURIComponent(messageId)}`);
21525
+ if (!isLive) {
21526
+ console.log('getMessage will be deprecated on 15th November 2022, please use liveMessage instead.');
21527
+ }
21528
+ isInTombstone('message', messageId);
21529
+ let data;
21530
+ try {
21531
+ // API-FIX: endpoint should not be /list, parameters should be querystring.
21532
+ const response = await client.http.get(`/api/v3/messages/${encodeURIComponent(messageId)}`);
21533
+ data = response.data;
21534
+ }
21535
+ catch (error) {
21536
+ if (checkIfShouldGoesToTombstone(error === null || error === void 0 ? void 0 : error.code)) {
21537
+ pushToTombstone('message', messageId);
21538
+ }
21539
+ throw error;
21540
+ }
21308
21541
  const cachedAt = client.cache && Date.now();
21309
21542
  if (client.cache)
21310
21543
  ingestInCache(data, { cachedAt });
21311
21544
  const { messages } = data;
21545
+ fireEvent('v3.message.didFetch', { messages });
21312
21546
  return {
21313
21547
  data: messages.find(message => message.messageId === messageId),
21314
21548
  cachedAt,
@@ -21365,8 +21599,10 @@ const getMessages = async (messageIds) => {
21365
21599
  const cachedAt = client.cache && Date.now();
21366
21600
  if (client.cache)
21367
21601
  ingestInCache(data, { cachedAt });
21602
+ const { messages } = data;
21603
+ fireEvent('v3.message.didFetch', { messages });
21368
21604
  return {
21369
- data: data.messages,
21605
+ data: messages,
21370
21606
  cachedAt,
21371
21607
  };
21372
21608
  };
@@ -21443,6 +21679,7 @@ const queryMessages = async (query) => {
21443
21679
  ];
21444
21680
  pushToCache(cacheKey, { messages: messages.map(getResolver('message')), paging });
21445
21681
  }
21682
+ fireEvent('v3.message.didFetch', { messages });
21446
21683
  const nextPage = toPage(paging.next);
21447
21684
  const prevPage = toPage(paging.previous);
21448
21685
  return { data: messages, cachedAt, prevPage, nextPage };
@@ -21508,8 +21745,8 @@ const createMessage = async (bundle) => {
21508
21745
  const cachedAt = client.cache && Date.now();
21509
21746
  if (client.cache)
21510
21747
  ingestInCache(data, { cachedAt });
21511
- // no need for event, fired by server
21512
21748
  const { messages } = data;
21749
+ fireEvent('v3.message.didCreate', { messages });
21513
21750
  return {
21514
21751
  data: messages[0],
21515
21752
  cachedAt,
@@ -21551,11 +21788,12 @@ createMessage.optimistically = (bundle) => {
21551
21788
  // as reused to update created and updated time, which should be the same
21552
21789
  const createdTime = new Date().toISOString();
21553
21790
  const message = Object.assign({ userId: client.userId, channelSegment: ((_b = channel === null || channel === void 0 ? void 0 : channel.data.messageCount) !== null && _b !== void 0 ? _b : 0) + 1, createdAt: createdTime, updatedAt: createdTime }, bundleWithMessageId);
21554
- pushToCache(['message', 'get', message.messageId], message, { cachedAt: -1 });
21791
+ const cachedAt = UNSYNCED_OBJECT_CACHED_AT_VALUE;
21792
+ pushToCache(['message', 'get', message.messageId], message, { cachedAt });
21555
21793
  fireEvent('v3.message.didCreate', { messages: [message] });
21556
21794
  return {
21795
+ cachedAt,
21557
21796
  data: message,
21558
- cachedAt: -1,
21559
21797
  };
21560
21798
  };
21561
21799
 
@@ -21584,6 +21822,7 @@ const updateMessage = async (messageId, patch) => {
21584
21822
  if (client.cache)
21585
21823
  ingestInCache(data, { cachedAt });
21586
21824
  const { messages } = data;
21825
+ fireEvent('v3.message.didUpdate', { messages });
21587
21826
  return {
21588
21827
  data: messages.find(message => message.messageId === messageId),
21589
21828
  cachedAt,
@@ -21643,7 +21882,7 @@ const deleteMessage = async (messageId) => {
21643
21882
  // API-FIX: This endpoint has not been implemented yet.
21644
21883
  await client.http.delete(`/api/v3/messages/${encodeURIComponent(messageId)}`);
21645
21884
  const deleted = await getMessage(messageId);
21646
- // no need for event, fired by server
21885
+ fireEvent('v3.message.didDelete', { messages: [deleted.data] });
21647
21886
  return deleted.data;
21648
21887
  };
21649
21888
  /**
@@ -21830,6 +22069,7 @@ getPosts.locally = (postIds) => {
21830
22069
  const getPost = async (postId) => {
21831
22070
  const client = getActiveClient();
21832
22071
  client.log('post/getPost', postId);
22072
+ isInTombstone('post', postId);
21833
22073
  let data;
21834
22074
  try {
21835
22075
  // API-FIX: endpoint should not be /list, parameters should be querystring.
@@ -21837,8 +22077,8 @@ const getPost = async (postId) => {
21837
22077
  data = response.data;
21838
22078
  }
21839
22079
  catch (error) {
21840
- if ((error === null || error === void 0 ? void 0 : error.code) === 400400 /* Amity.ServerError.ITEM_NOT_FOUND */) {
21841
- dropFromCache(['post', 'get', postId], true);
22080
+ if (checkIfShouldGoesToTombstone(error === null || error === void 0 ? void 0 : error.code)) {
22081
+ pushToTombstone('post', postId);
21842
22082
  }
21843
22083
  throw error;
21844
22084
  }
@@ -22618,7 +22858,7 @@ const deletePost = async (postId, permanent = false) => {
22618
22858
  fireEvent('post.deleted', { posts: [deleted] });
22619
22859
  if (permanent) {
22620
22860
  setTimeout(() => {
22621
- dropFromCache(['post', 'get', postId], true);
22861
+ pushToTombstone('post', postId);
22622
22862
  }, 0);
22623
22863
  }
22624
22864
  else {
@@ -23014,17 +23254,6 @@ const deleteComment = async (commentId, permanent = false) => {
23014
23254
  return deleted;
23015
23255
  };
23016
23256
 
23017
- /** @hidden */
23018
- const fetchReference = async (referenceType, referenceId) => {
23019
- if (referenceType === 'message')
23020
- return getMessage(referenceId);
23021
- if (referenceType === 'post')
23022
- return getPost(referenceId);
23023
- if (referenceType === 'comment')
23024
- return getComment(referenceId);
23025
- throw new Error(`Incorrect referenceType: ${referenceType}`);
23026
- };
23027
-
23028
23257
  /** @hidden */
23029
23258
  const preparePayloadForCache = (event, payload) => {
23030
23259
  var _a, _b;
@@ -23112,7 +23341,6 @@ const prepareMessagePayloadForCache = (payload) => {
23112
23341
  * @async
23113
23342
  * */
23114
23343
  const addReaction = async (referenceType, referenceId, reactionName) => {
23115
- var _a, _b;
23116
23344
  const client = getActiveClient();
23117
23345
  client.log('reaction/createReaction', {
23118
23346
  referenceId,
@@ -23124,9 +23352,7 @@ const addReaction = async (referenceType, referenceId, reactionName) => {
23124
23352
  referenceType,
23125
23353
  reactionName,
23126
23354
  });
23127
- const model = await fetchReference(referenceType, referenceId);
23128
- dispatchReactable(referenceType, model.data);
23129
- return (_b = (_a = model.data.myReactions) === null || _a === void 0 ? void 0 : _a.includes(reactionName)) !== null && _b !== void 0 ? _b : false;
23355
+ return true;
23130
23356
  };
23131
23357
  /**
23132
23358
  * ```js
@@ -23183,7 +23409,6 @@ addReaction.optimistically = (referenceType, referenceId, reactionName) => {
23183
23409
  * @async
23184
23410
  * */
23185
23411
  const removeReaction = async (referenceType, referenceId, reactionName) => {
23186
- var _a, _b;
23187
23412
  const client = getActiveClient();
23188
23413
  client.log('reaction/removeReaction', {
23189
23414
  referenceId,
@@ -23193,9 +23418,7 @@ const removeReaction = async (referenceType, referenceId, reactionName) => {
23193
23418
  await client.http.delete(`/api/v2/reactions`, {
23194
23419
  data: { referenceId, referenceType, reactionName },
23195
23420
  });
23196
- const model = await fetchReference(referenceType, referenceId);
23197
- dispatchReactable(referenceType, model.data);
23198
- return (_b = !((_a = model.data.myReactions) === null || _a === void 0 ? void 0 : _a.includes(reactionName))) !== null && _b !== void 0 ? _b : false;
23421
+ return true;
23199
23422
  };
23200
23423
  /**
23201
23424
  * ```js
@@ -23872,10 +24095,6 @@ const onReactorRemoved = (referenceType, referenceId, callback) => {
23872
24095
  return createEventSubscriber(client, 'comment.removeReaction', 'comment.removeReaction', filter);
23873
24096
  };
23874
24097
 
23875
- const COLLECTION_DEFAULT_PAGINATION_LIMIT = 5;
23876
- const COLLECTION_DEFAULT_CACHING_POLICY = 'cache_then_server';
23877
- const ENABLE_CACHE_MESSAGE = 'For using Live Collection feature you need to enable Cache!';
23878
-
23879
24098
  /**
23880
24099
  * ```js
23881
24100
  * import { liveReactions } from '@amityco/ts-sdk'
@@ -24152,9 +24371,24 @@ const queryChannels = async (query) => {
24152
24371
  * @category Channel API
24153
24372
  */
24154
24373
  queryChannels.locally = (query) => {
24374
+ var _a, _b;
24155
24375
  const client = getActiveClient();
24156
24376
  client.log('channel/queryChannels.locally', query);
24157
- // TODO
24377
+ const _c = query !== null && query !== void 0 ? query : {}, { page = { limit: 10 } } = _c, params = __rest(_c, ["page"]);
24378
+ const cacheKey = ['channel', 'query', Object.assign(Object.assign({}, params), { options: Object.assign({}, page) })];
24379
+ const { data, cachedAt } = (_a = pullFromCache(cacheKey)) !== null && _a !== void 0 ? _a : {};
24380
+ if (!(data === null || data === void 0 ? void 0 : data.channels.length))
24381
+ return;
24382
+ const channels = data.channels
24383
+ // ck -> cacheKey, abbrevated to avoid confusion from the above cache key
24384
+ .map(ck => pullFromCache(['channel', 'get', ck]))
24385
+ .filter(Boolean)
24386
+ .map(({ data }) => data);
24387
+ const prevPage = toPage(data === null || data === void 0 ? void 0 : data.paging.previous);
24388
+ const nextPage = toPage(data === null || data === void 0 ? void 0 : data.paging.next);
24389
+ return channels.length === ((_b = data === null || data === void 0 ? void 0 : data.channels) === null || _b === void 0 ? void 0 : _b.length)
24390
+ ? { data: channels, cachedAt, prevPage, nextPage }
24391
+ : undefined;
24158
24392
  };
24159
24393
 
24160
24394
  /**
@@ -24237,9 +24471,18 @@ getChannels.locally = (channelIds) => {
24237
24471
  const getChannel = async (channelId) => {
24238
24472
  const client = getActiveClient();
24239
24473
  client.log('channel/getChannel', channelId);
24240
- // API-FIX: endpoint should not be /list, parameters should be querystring.
24241
- const { data: payload } = await client.http.get(`/api/v3/channels/${encodeURIComponent(channelId)}`);
24242
- const data = prepareChannelPayload(payload);
24474
+ isInTombstone('channel', channelId);
24475
+ let data;
24476
+ try {
24477
+ const { data: payload } = await client.http.get(`/api/v3/channels/${encodeURIComponent(channelId)}`);
24478
+ data = prepareChannelPayload(payload);
24479
+ }
24480
+ catch (error) {
24481
+ if (checkIfShouldGoesToTombstone(error === null || error === void 0 ? void 0 : error.code)) {
24482
+ pushToTombstone('channel', channelId);
24483
+ }
24484
+ throw error;
24485
+ }
24243
24486
  const cachedAt = client.cache && Date.now();
24244
24487
  if (client.cache)
24245
24488
  ingestInCache(data, { cachedAt });
@@ -24431,8 +24674,8 @@ const leaveChannel = async (channelId) => {
24431
24674
  const queryChannelMembers = async (query) => {
24432
24675
  const client = getActiveClient();
24433
24676
  client.log('channel/queryChannelMembers', query);
24434
- const _a = query !== null && query !== void 0 ? query : {}, { page, channelId } = _a, params = __rest(_a, ["page", "channelId"]);
24435
- const { data: queryPayload } = await client.http.get(`/api/v4/channels/${encodeURIComponent(channelId)}/users`, {
24677
+ const _a = query !== null && query !== void 0 ? query : {}, { page = { limit: 10 } } = _a, params = __rest(_a, ["page"]);
24678
+ const { data: queryPayload } = await client.http.get(`/api/v4/channels/${encodeURIComponent(params.channelId)}/users`, {
24436
24679
  params: Object.assign(Object.assign({}, params), { options: {
24437
24680
  token: toToken(page, 'skiplimit'),
24438
24681
  } }),
@@ -24443,6 +24686,15 @@ const queryChannelMembers = async (query) => {
24443
24686
  const cachedAt = client.cache && Date.now();
24444
24687
  if (client.cache) {
24445
24688
  ingestInCache(data);
24689
+ const cacheKey = [
24690
+ 'channelUsers',
24691
+ 'query',
24692
+ Object.assign(Object.assign({}, params), { options: Object.assign({}, page) }),
24693
+ ];
24694
+ pushToCache(cacheKey, {
24695
+ channelUsers: channelUsers.map(({ channelId, userId }) => getResolver('channelUsers')({ channelId, userId })),
24696
+ paging,
24697
+ });
24446
24698
  }
24447
24699
  const nextPage = toPage(paging.next);
24448
24700
  const prevPage = toPage(paging.previous);
@@ -24462,9 +24714,30 @@ const queryChannelMembers = async (query) => {
24462
24714
  * @category Post API
24463
24715
  */
24464
24716
  queryChannelMembers.locally = (query) => {
24717
+ var _a, _b;
24465
24718
  const client = getActiveClient();
24466
- client.log('channel/queryChannelMembers', query);
24467
- // TODO
24719
+ client.log('channel/queryChannelMembers.locally', query);
24720
+ if (!client.cache)
24721
+ return;
24722
+ const { page = { limit: 10 } } = query, params = __rest(query, ["page"]);
24723
+ const cacheKey = [
24724
+ 'channelUsers',
24725
+ 'query',
24726
+ Object.assign(Object.assign({}, params), { options: Object.assign({}, page) }),
24727
+ ];
24728
+ const { data, cachedAt } = (_a = pullFromCache(cacheKey)) !== null && _a !== void 0 ? _a : {};
24729
+ if (!(data === null || data === void 0 ? void 0 : data.channelUsers.length))
24730
+ return;
24731
+ const channelUsers = data.channelUsers
24732
+ // ck -> cacheKey, abbrevated to avoid confusion from the above cache key
24733
+ .map(ck => pullFromCache(['channelUsers', 'get', ck]))
24734
+ .filter(Boolean)
24735
+ .map(({ data }) => data);
24736
+ const prevPage = toPage(data === null || data === void 0 ? void 0 : data.paging.previous);
24737
+ const nextPage = toPage(data === null || data === void 0 ? void 0 : data.paging.next);
24738
+ return channelUsers.length === ((_b = data === null || data === void 0 ? void 0 : data.channelUsers) === null || _b === void 0 ? void 0 : _b.length)
24739
+ ? { data: channelUsers, cachedAt, prevPage, nextPage }
24740
+ : undefined;
24468
24741
  };
24469
24742
 
24470
24743
  /**
@@ -24489,7 +24762,10 @@ const addChannelMembers = async (channelId, userIds) => {
24489
24762
  const data = prepareChannelPayload(payload);
24490
24763
  if (client.cache)
24491
24764
  ingestInCache(data);
24492
- fireEvent('v3.channel.didAddUsers', data);
24765
+ /*
24766
+ * should fire event for each user that was added
24767
+ */
24768
+ userIds.forEach(id => fireEvent('v3.channel.didAddUsers', Object.assign(Object.assign({}, data), { channelUsers: data.channelUsers.filter(u => u.userId === id) })));
24493
24769
  const { channelUsers } = data;
24494
24770
  return !!channelUsers.find(channelUser => channelUser.channelId === channelId && channelUser.membership === 'member');
24495
24771
  };
@@ -24812,6 +25088,56 @@ const onChannelMemberRemoved = (callback) => {
24812
25088
  return createEventSubscriber(client, 'onChannelMemberRemoved', 'v3.channel.didRemoveUsers', filter);
24813
25089
  };
24814
25090
 
25091
+ /**
25092
+ * ```js
25093
+ * import { onChannelMemberBanned } from '@amityco/ts-sdk'
25094
+ * const dispose = onChannelMemberBanned((channel, member) => {
25095
+ * // ...
25096
+ * })
25097
+ * ```
25098
+ *
25099
+ * Fired when any {@link Amity.User} has been banned from any {@link Amity.Channel}
25100
+ *
25101
+ * @param callback The function to call when the event was fired
25102
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
25103
+ *
25104
+ * @category Channel Events
25105
+ */
25106
+ const onChannelMemberBanned = (callback) => {
25107
+ const client = getActiveClient();
25108
+ const filter = (payload) => {
25109
+ const data = prepareChannelPayload(payload);
25110
+ ingestInCache(data);
25111
+ callback(data.channels[0], data.channelUsers.find(cu => cu.membership === 'banned'));
25112
+ };
25113
+ return createEventSubscriber(client, 'onChannelMemberBanned', 'v3.channel.didBan', filter);
25114
+ };
25115
+
25116
+ /**
25117
+ * ```js
25118
+ * import { onChannelMemberUnbanned } from '@amityco/ts-sdk'
25119
+ * const dispose = onChannelMemberUnbanned((channel, member) => {
25120
+ * // ...
25121
+ * })
25122
+ * ```
25123
+ *
25124
+ * Fired when any {@link Amity.User} has been unbanned from any {@link Amity.Channel}
25125
+ *
25126
+ * @param callback The function to call when the event was fired
25127
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
25128
+ *
25129
+ * @category Channel Events
25130
+ */
25131
+ const onChannelMemberUnbanned = (callback) => {
25132
+ const client = getActiveClient();
25133
+ const filter = (payload) => {
25134
+ const data = prepareChannelPayload(payload);
25135
+ ingestInCache(data);
25136
+ callback(data.channels[0], data.channelUsers.find(cu => cu.membership === 'none'));
25137
+ };
25138
+ return createEventSubscriber(client, 'onChannelMemberBanned', 'v3.channel.didUnban', filter);
25139
+ };
25140
+
24815
25141
  /**
24816
25142
  * ```js
24817
25143
  * import { observeChannel } from '@amityco/ts-sdk'
@@ -24906,7 +25232,7 @@ const observeChannels = (callback) => {
24906
25232
  * @category Channel Live Collection
24907
25233
  */
24908
25234
  const liveChannels = (params, callback, config) => {
24909
- const { log, cache } = getActiveClient();
25235
+ const { log, cache, userId } = getActiveClient();
24910
25236
  if (!cache) {
24911
25237
  console.log(ENABLE_CACHE_MESSAGE);
24912
25238
  }
@@ -24934,6 +25260,10 @@ const liveChannels = (params, callback, config) => {
24934
25260
  if (params.excludeTags) {
24935
25261
  channels = channels.filter(c => { var _a; return !((_a = c.tags) === null || _a === void 0 ? void 0 : _a.some(t => { var _a; return (_a = params.excludeTags) === null || _a === void 0 ? void 0 : _a.includes(t); })); });
24936
25262
  }
25263
+ // userId is required to be able to filter channel by current active user
25264
+ if (params.membership && userId) {
25265
+ channels = filterByChannelMembership(channels, params.membership, userId);
25266
+ }
24937
25267
  const sortBy = params.sortBy || 'lastCreated';
24938
25268
  if (sortBy === 'lastCreated' || sortBy === 'firstCreated') {
24939
25269
  channels = channels.sort(sortBy === 'lastCreated' ? sortByLastCreated : sortByFirstCreated);
@@ -25043,7 +25373,20 @@ const liveChannelMembers = (params, callback, config) => {
25043
25373
  .filter(Boolean)
25044
25374
  .map(({ data }) => data)) !== null && _a !== void 0 ? _a : [];
25045
25375
  channelMembers = filterByPropEquality(channelMembers, 'roles', params.roles);
25046
- channelMembers = filterByPropInclusion(channelMembers, 'membership', params.memberships);
25376
+ if (params.memberships) {
25377
+ /*
25378
+ * even though membership includes muted as a possible value
25379
+ * when querying the server.
25380
+ * Muted is specified under seperarte property namely isMuted
25381
+ * Hence why I've seperately checked for it's equality
25382
+ */
25383
+ if (params.memberships.some(m => ['member', 'banned'].includes(m))) {
25384
+ channelMembers = filterByPropInclusion(channelMembers, 'membership', params.memberships);
25385
+ }
25386
+ if (params.memberships.includes('muted')) {
25387
+ channelMembers = filterByPropEquality(channelMembers, 'isMuted', true);
25388
+ }
25389
+ }
25047
25390
  if (params.search) {
25048
25391
  const containsMatcher = new RegExp(params.search);
25049
25392
  channelMembers = channelMembers.filter(m => m.userId.match(containsMatcher));
@@ -25071,7 +25414,7 @@ const liveChannelMembers = (params, callback, config) => {
25071
25414
  if (action === 'onMemberAdded') {
25072
25415
  collection.data = [...new Set([channelMemberCacheId, ...collection.data])];
25073
25416
  }
25074
- else {
25417
+ if (action === 'onMemberRemoved') {
25075
25418
  collection.data = collection.data.filter(m => m !== channelMemberCacheId);
25076
25419
  }
25077
25420
  pushToCache(cacheKey, collection);
@@ -25098,7 +25441,7 @@ const liveChannelMembers = (params, callback, config) => {
25098
25441
  responder(data);
25099
25442
  }, queryOptions(policy));
25100
25443
  };
25101
- disposers.push(onChannelMemberAdded((_, member) => realtimeRouter(member, 'onMemberAdded')), onChannelMemberRemoved((_, member) => realtimeRouter(member, 'onMemberRemoved')));
25444
+ disposers.push(onChannelMemberAdded((_, member) => realtimeRouter(member, 'onMemberAdded')), onChannelMemberRemoved((_, member) => realtimeRouter(member, 'onMemberRemoved')), onChannelMemberBanned((_, member) => realtimeRouter(member, 'onChannelMemberBanned')), onChannelMemberUnbanned((_, member) => realtimeRouter(member, 'onChannelMemberUnbanned')));
25102
25445
  onFetch();
25103
25446
  return () => {
25104
25447
  log(`liveChannelMembers(tmpid: ${timestamp}) > dispose`);
@@ -25185,82 +25528,92 @@ const onMessageDeleted = (callback) => {
25185
25528
 
25186
25529
  /**
25187
25530
  * ```js
25188
- * import { observeMessage } from '@amityco/ts-sdk'
25189
- *
25190
- * let message = {}
25191
- * const unsub = observeMessage(messageId, updated => message = updated)
25531
+ * import { onMessageFetched } from '@amityco/ts-sdk'
25532
+ * const dispose = onMessageFetched(message => {
25533
+ * // ...
25534
+ * })
25192
25535
  * ```
25193
25536
  *
25194
- * Observe all mutations on a {@link Amity.Message} given a single messageId
25537
+ * Fired when an {@link Amity.Message} has been fetched
25195
25538
  *
25196
- * @param client the client to observe the message with
25197
- * @param messageId the ID of the message to observe
25198
- * @param callback the function to call when new data are available
25199
- * @param policy a query policy option for this observer
25200
- * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the message
25539
+ * @param callback The function to call when the event was fired
25540
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
25201
25541
  *
25202
- * @category Message Observer
25542
+ * @category Message Events
25203
25543
  */
25204
- const observeMessage = (messageId, callback, policy = 'cache_then_server') => {
25205
- const { log } = getActiveClient();
25206
- const timestamp = Date.now();
25207
- log(`observeMessage(tmpid: ${timestamp}) > listen`);
25208
- // wrapper function to make sure
25209
- const router = (result, action) => {
25210
- var _a, _b, _c;
25211
- // filter function
25212
- if (((_a = result.data) === null || _a === void 0 ? void 0 : _a.messageId) !== messageId)
25213
- return;
25214
- if (callback instanceof Function)
25215
- return callback(result);
25216
- if (action !== 'onFetch')
25217
- (_b = callback.onEvent) === null || _b === void 0 ? void 0 : _b.call(callback, action, result);
25218
- (_c = callback[action]) === null || _c === void 0 ? void 0 : _c.call(callback, result);
25219
- };
25220
- const disposers = [];
25221
- disposers.push(onMessageUpdated(data => router({ data, loading: false, origin: 'event' }, 'onUpdate')), onMessageDeleted(data => router({ data, loading: false, origin: 'event' }, 'onDelete')));
25222
- if (policy !== 'no_fetch') {
25223
- runQuery(createQuery(getMessage, messageId), result => result.data && router(result, 'onFetch'), queryOptions(policy));
25224
- }
25225
- return () => {
25226
- log(`observeMessages(tmpid: ${timestamp}) > dispose`);
25227
- disposers.forEach(fn => fn());
25544
+ const onMessageFetched = (callback) => {
25545
+ const client = getActiveClient();
25546
+ const filter = (payload) => {
25547
+ ingestInCache(payload);
25548
+ callback(payload.messages[0]);
25228
25549
  };
25550
+ return createEventSubscriber(client, 'message/onMessageFetched', 'v3.message.didFetch', filter);
25229
25551
  };
25230
25552
 
25231
25553
  /**
25232
25554
  * ```js
25233
- * import { observeMessages } from '@amityco/ts-sdk'
25555
+ * import { liveMessage } from '@amityco/ts-sdk'
25234
25556
  *
25235
25557
  * let messages = []
25236
- * const unsub = observeMessages(channelId, message => merge(messages, message))
25558
+ * const unsub = liveMessage(messageId, response => merge(messages, response.data))
25237
25559
  * ```
25238
25560
  *
25239
- * Observe all mutations on a list of {@link Amity.Message} for a given {@link Amity.Channel} object
25561
+ * Observe all mutation on a given {@link Amity.Message}
25240
25562
  *
25241
- * @param channelId the ID of the channel where to observe the messages
25563
+ * @param messageId the ID of the message to observe
25242
25564
  * @param callback the function to call when new data are available
25243
25565
  * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the messages
25244
25566
  *
25245
- * @category Message Observer
25567
+ * @category Message Live Object
25246
25568
  */
25247
- const observeMessages = (channelId, callback) => {
25248
- const { log } = getActiveClient();
25569
+ const liveMessage = (messageId, callback) => {
25570
+ const { log, cache } = getActiveClient();
25571
+ let message;
25572
+ let isUnsyncedObject = false;
25573
+ if (!cache) {
25574
+ console.log(LIVE_OBJECT_ENABLE_CACHE_MESSAGE);
25575
+ }
25249
25576
  const timestamp = Date.now();
25250
- log(`observeMessages(tmpid: ${timestamp}) > listen`);
25577
+ log(`liveMessage(tmpid: ${timestamp}) > listen`);
25251
25578
  const disposers = [];
25252
- const router = (message, action) => {
25253
- var _a, _b, _c;
25254
- if (((_a = message.data) === null || _a === void 0 ? void 0 : _a.channelId) !== channelId)
25579
+ const dispatcher = (data) => {
25580
+ var _a;
25581
+ // we check updatedAt to see if the current snapshot is new so
25582
+ // we won't send old data back, only fresh new data
25583
+ if (message && ((_a = data.data) === null || _a === void 0 ? void 0 : _a.updatedAt) === (message === null || message === void 0 ? void 0 : message.updatedAt) && data.origin !== 'server')
25255
25584
  return;
25256
- if (callback instanceof Function)
25257
- return callback(message);
25258
- (_b = callback.onEvent) === null || _b === void 0 ? void 0 : _b.call(callback, action, message);
25259
- (_c = callback[action]) === null || _c === void 0 ? void 0 : _c.call(callback, message);
25585
+ callback(data);
25586
+ message = data.data;
25260
25587
  };
25261
- disposers.push(onMessageCreated(data => router({ data, loading: false, origin: 'event' }, 'onCreate')), onMessageUpdated(data => router({ data, loading: false, origin: 'event' }, 'onUpdate')), onMessageDeleted(data => router({ data, loading: false, origin: 'event' }, 'onDelete')));
25588
+ const realtimeRouter = (message, action) => {
25589
+ dispatcher({ loading: false, data: message, origin: 'event' });
25590
+ };
25591
+ const onFetch = () => {
25592
+ const query = createQuery(getMessage, messageId, true);
25593
+ runQuery(query, ({ loading, data, error, origin, cachedAt }) => {
25594
+ if (cachedAt === UNSYNCED_OBJECT_CACHED_AT_VALUE) {
25595
+ dispatcher({
25596
+ data,
25597
+ origin,
25598
+ loading: false,
25599
+ error: new ASCApiError(UNSYNCED_OBJECT_CACHED_AT_MESSAGE, 800800 /* Amity.ClientError.DISALOOW_UNSYNCED_OBJECT */, "error" /* Amity.ErrorLevel.ERROR */),
25600
+ });
25601
+ isUnsyncedObject = true;
25602
+ disposers.forEach(fn => fn());
25603
+ }
25604
+ else if (!isUnsyncedObject) {
25605
+ dispatcher({ loading, data, origin, error });
25606
+ }
25607
+ if (error) {
25608
+ // Terminate observe functionality on error
25609
+ disposers.forEach(fn => fn());
25610
+ }
25611
+ });
25612
+ };
25613
+ disposers.push(onMessageFetched(message => realtimeRouter(message)), onMessageUpdated(message => realtimeRouter(message)), onMessageDeleted(message => realtimeRouter(message)));
25614
+ onFetch();
25262
25615
  return () => {
25263
- log(`observeMessages(tmpid: ${timestamp}) > dispose`);
25616
+ log(`liveMessage(tmpid: ${timestamp}) > dispose`);
25264
25617
  disposers.forEach(fn => fn());
25265
25618
  };
25266
25619
  };
@@ -25349,7 +25702,7 @@ const liveMessages = (params, callback, config) => {
25349
25702
  responder(data);
25350
25703
  }, queryOptions(policy));
25351
25704
  };
25352
- disposers.push(onMessageCreated(message => realtimeRouter(message, 'onCreate')), onMessageDeleted(message => realtimeRouter(message, 'onDelete')), onMessageUpdated(message => realtimeRouter(message, 'onUpdate')));
25705
+ disposers.push(onMessageFetched(message => realtimeRouter(message, 'onFetch')), onMessageCreated(message => realtimeRouter(message, 'onCreate')), onMessageDeleted(message => realtimeRouter(message, 'onDelete')), onMessageUpdated(message => realtimeRouter(message, 'onUpdate')));
25353
25706
  onFetch();
25354
25707
  return () => {
25355
25708
  log(`liveMessages(tmpid: ${timestamp}) > dispose`);
@@ -25357,6 +25710,89 @@ const liveMessages = (params, callback, config) => {
25357
25710
  };
25358
25711
  };
25359
25712
 
25713
+ /**
25714
+ * ```js
25715
+ * import { observeMessage } from '@amityco/ts-sdk'
25716
+ *
25717
+ * let message = {}
25718
+ * const unsub = observeMessage(messageId, updated => message = updated)
25719
+ * ```
25720
+ *
25721
+ * Observe all mutations on a {@link Amity.Message} given a single messageId
25722
+ *
25723
+ * @param client the client to observe the message with
25724
+ * @param messageId the ID of the message to observe
25725
+ * @param callback the function to call when new data are available
25726
+ * @param policy a query policy option for this observer
25727
+ * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the message
25728
+ *
25729
+ * @category Message Observer
25730
+ */
25731
+ const observeMessage = (messageId, callback, policy = 'cache_then_server') => {
25732
+ const { log } = getActiveClient();
25733
+ const timestamp = Date.now();
25734
+ log(`observeMessage(tmpid: ${timestamp}) > listen`);
25735
+ console.log('observeMessage will be deprecated on 15th November 2022, please use liveMessage instead.');
25736
+ // wrapper function to make sure
25737
+ const router = (result, action) => {
25738
+ var _a, _b, _c;
25739
+ // filter function
25740
+ if (((_a = result.data) === null || _a === void 0 ? void 0 : _a.messageId) !== messageId)
25741
+ return;
25742
+ if (callback instanceof Function)
25743
+ return callback(result);
25744
+ if (action !== 'onFetch')
25745
+ (_b = callback.onEvent) === null || _b === void 0 ? void 0 : _b.call(callback, action, result);
25746
+ (_c = callback[action]) === null || _c === void 0 ? void 0 : _c.call(callback, result);
25747
+ };
25748
+ const disposers = [];
25749
+ disposers.push(onMessageUpdated(data => router({ data, loading: false, origin: 'event' }, 'onUpdate')), onMessageDeleted(data => router({ data, loading: false, origin: 'event' }, 'onDelete')));
25750
+ if (policy !== 'no_fetch') {
25751
+ runQuery(createQuery(getMessage, messageId), result => result.data && router(result, 'onFetch'), queryOptions(policy));
25752
+ }
25753
+ return () => {
25754
+ log(`observeMessages(tmpid: ${timestamp}) > dispose`);
25755
+ disposers.forEach(fn => fn());
25756
+ };
25757
+ };
25758
+
25759
+ /**
25760
+ * ```js
25761
+ * import { observeMessages } from '@amityco/ts-sdk'
25762
+ *
25763
+ * let messages = []
25764
+ * const unsub = observeMessages(channelId, message => merge(messages, message))
25765
+ * ```
25766
+ *
25767
+ * Observe all mutations on a list of {@link Amity.Message} for a given {@link Amity.Channel} object
25768
+ *
25769
+ * @param channelId the ID of the channel where to observe the messages
25770
+ * @param callback the function to call when new data are available
25771
+ * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the messages
25772
+ *
25773
+ * @category Message Observer
25774
+ */
25775
+ const observeMessages = (channelId, callback) => {
25776
+ const { log } = getActiveClient();
25777
+ const timestamp = Date.now();
25778
+ log(`observeMessages(tmpid: ${timestamp}) > listen`);
25779
+ const disposers = [];
25780
+ const router = (message, action) => {
25781
+ var _a, _b, _c;
25782
+ if (((_a = message.data) === null || _a === void 0 ? void 0 : _a.channelId) !== channelId)
25783
+ return;
25784
+ if (callback instanceof Function)
25785
+ return callback(message);
25786
+ (_b = callback.onEvent) === null || _b === void 0 ? void 0 : _b.call(callback, action, message);
25787
+ (_c = callback[action]) === null || _c === void 0 ? void 0 : _c.call(callback, message);
25788
+ };
25789
+ disposers.push(onMessageCreated(data => router({ data, loading: false, origin: 'event' }, 'onCreate')), onMessageUpdated(data => router({ data, loading: false, origin: 'event' }, 'onUpdate')), onMessageDeleted(data => router({ data, loading: false, origin: 'event' }, 'onDelete')));
25790
+ return () => {
25791
+ log(`observeMessages(tmpid: ${timestamp}) > dispose`);
25792
+ disposers.forEach(fn => fn());
25793
+ };
25794
+ };
25795
+
25360
25796
  const createCommunityEventSubscriber = (event, callback) => {
25361
25797
  const client = getActiveClient();
25362
25798
  const filter = (payload) => {
@@ -27539,4 +27975,4 @@ const createUserToken = async (apiKey, apiRegion, params) => {
27539
27975
  return { accessToken: data.accessToken };
27540
27976
  };
27541
27977
 
27542
- export { API_REGIONS, CommunityPostSettingMaps, CommunityPostSettings, ContentFeedType, DefaultCommunityPostSetting, FileType, SubscriptionLevels, VERSION$1 as VERSION, VideoResolution, VideoSize, VideoTranscodingStatus, acceptFollower, addChannelMembers, addChannelMembersRole, addCommunityMembers, addCommunityMembersRoles, addReaction, approvePost, backupCache, banChannelMembers, closePoll, connectClient, createChannel, createClient, createComment, createCommunity, createFile, createImage, createMessage, createPoll, createPost, createQuery, createReport, createStream, createUserToken, createVideo, declineFollower, declinePost, deleteChannel, deleteComment, deleteCommunity, deleteFile, deleteMessage, deletePoll, deletePost, deleteReport, deleteStream, disableCache, disconnectClient, disposeStream, dropFromCache, enableCache, fileUrlWithSize, filterByPropEquality, filterByPropInclusion, follow, getActiveClient, getCategory, getChannel, getChannels, getComment, getCommentTopic, getComments, getCommunities, getCommunity, getCommunityTopic, getFeedSettings, getFile, getFollowInfo, getMessage, getMessages, getMyFollowersTopic, getMyFollowingsTopic, getNetworkTopic, getPoll, getPost, getPostTopic, getPosts, getRecommendedCommunities, getRole, getStream, getTopTrendingCommunities, getUser, getUserTopic, getUsers, isAfterBefore, isAfterBeforeRaw, isCachable, isConnected, isFetcher, isFresh, isLocal, isMutator, isOffline, isPaged, isReportedByMe, isSkipLimit, joinChannel, joinCommunity, leaveChannel, leaveCommunity, liveChannelMembers, liveChannels, liveComments, liveFollowers, liveFollowings, liveMessages, livePosts, liveReactions, mergeInCache, observeChannel, observeChannels, observeComment, observeComments, observeCommunity, observeFile, observeFollowInfo, observeFollowers, observeFollowings, observeMessage, observeMessages, observePoll, observePost, observePosts, observeUser, onChannelCreated, onChannelDeleted, onChannelJoined, onChannelLeft, onChannelMemberAdded, onChannelMemberRemoved, onChannelUpdated, onClientBanned, onClientDisconnected, onCommentCreated, onCommentDeleted, onCommentFlagged, onCommentReactionAdded, onCommentReactionRemoved, onCommentUnflagged, onCommentUpdated, onCommunityCreated, onCommunityDeleted, onCommunityJoined, onCommunityLeft, onCommunityUpdated, onCommunityUserAdded, onCommunityUserBanned, onCommunityUserRemoved, onCommunityUserUnbanned, onConnectionError, onFollowInfoUpdated, onFollowRequestAccepted, onFollowRequestCanceled, onFollowRequestDeclined, onFollowerDeleted, onFollowerRequested, onMessageCreated, onMessageDeleted, onMessageUpdated, onPollDeleted, onPollUpdated, onPostApproved, onPostCreated, onPostDeclined, onPostDeleted, onPostFlagged, onPostReactionAdded, onPostReactionRemoved, onPostUnflagged, onPostUpdated, onReactionAdded, onReactionRemoved, onReactorAdded, onReactorRemoved, onStreamRecorded, onStreamStarted, onStreamStopped, onUserDeleted, onUserFlagCleared, onUserFlagged, onUserFollowed, onUserUnflagged, onUserUnfollowed, onUserUpdated, pullFromCache, pushToCache, queryCache, queryCategories, queryChannelMembers, queryChannels, queryComments, queryCommunities, queryCommunityMembers, queryFollowers, queryFollowings, queryGlobalFeed, queryMessages, queryOptions, queryPosts, queryReactions, queryRoles, queryStreams, queryUsers, removeChannelMembers, removeChannelMembersRole, removeCommunityMembers, removeCommunityMembersRoles, removeReaction, restoreCache, runQuery, sortByChannelSegment, sortByDisplayName, sortByFirstCreated, sortByLastActivity, sortByLastCreated, sortBySegmentNumber, subscribeTopic, toPage, toPageRaw, toToken, unbanChannelMembers, unfollow, updateChannel, updateComment, updateCommunity, updateMessage, updatePost, updateStream, updateUser, upsertInCache, votePoll, wipeCache };
27978
+ export { API_REGIONS, CommunityPostSettingMaps, CommunityPostSettings, ContentFeedType, DefaultCommunityPostSetting, FileType, MessageContentType, PostContentType, SubscriptionLevels, VERSION$1 as VERSION, VideoResolution, VideoSize, VideoTranscodingStatus, acceptFollower, addChannelMembers, addChannelMembersRole, addCommunityMembers, addCommunityMembersRoles, addReaction, approvePost, backupCache, banChannelMembers, closePoll, connectClient, createChannel, createClient, createComment, createCommunity, createFile, createImage, createMessage, createPoll, createPost, createQuery, createReport, createStream, createUserToken, createVideo, declineFollower, declinePost, deleteChannel, deleteComment, deleteCommunity, deleteFile, deleteMessage, deletePoll, deletePost, deleteReport, deleteStream, disableCache, disconnectClient, disposeStream, dropFromCache, enableCache, fileUrlWithSize, filterByChannelMembership, filterByPropEquality, filterByPropInclusion, follow, getActiveClient, getCategory, getChannel, getChannels, getComment, getCommentTopic, getComments, getCommunities, getCommunity, getCommunityTopic, getFeedSettings, getFile, getFollowInfo, getMessage, getMessages, getMyFollowersTopic, getMyFollowingsTopic, getNetworkTopic, getPoll, getPost, getPostTopic, getPosts, getRecommendedCommunities, getRole, getStream, getTopTrendingCommunities, getUser, getUserTopic, getUsers, isAfterBefore, isAfterBeforeRaw, isCachable, isConnected, isFetcher, isFresh, isLocal, isMutator, isOffline, isPaged, isReportedByMe, isSkipLimit, joinChannel, joinCommunity, leaveChannel, leaveCommunity, liveChannelMembers, liveChannels, liveComments, liveFollowers, liveFollowings, liveMessage, liveMessages, livePosts, liveReactions, liveUser, mergeInCache, observeChannel, observeChannels, observeComment, observeComments, observeCommunity, observeFile, observeFollowInfo, observeFollowers, observeFollowings, observeMessage, observeMessages, observePoll, observePost, observePosts, observeUser, onChannelCreated, onChannelDeleted, onChannelJoined, onChannelLeft, onChannelMemberAdded, onChannelMemberBanned, onChannelMemberRemoved, onChannelMemberUnbanned, onChannelUpdated, onClientBanned, onClientDisconnected, onCommentCreated, onCommentDeleted, onCommentFlagged, onCommentReactionAdded, onCommentReactionRemoved, onCommentUnflagged, onCommentUpdated, onCommunityCreated, onCommunityDeleted, onCommunityJoined, onCommunityLeft, onCommunityUpdated, onCommunityUserAdded, onCommunityUserBanned, onCommunityUserRemoved, onCommunityUserUnbanned, onConnectionError, onFollowInfoUpdated, onFollowRequestAccepted, onFollowRequestCanceled, onFollowRequestDeclined, onFollowerDeleted, onFollowerRequested, onMessageCreated, onMessageDeleted, onMessageFetched, onMessageUpdated, onPollDeleted, onPollUpdated, onPostApproved, onPostCreated, onPostDeclined, onPostDeleted, onPostFlagged, onPostReactionAdded, onPostReactionRemoved, onPostUnflagged, onPostUpdated, onReactionAdded, onReactionRemoved, onReactorAdded, onReactorRemoved, onStreamRecorded, onStreamStarted, onStreamStopped, onUserDeleted, onUserFetched, onUserFlagCleared, onUserFlagged, onUserFollowed, onUserUnflagged, onUserUnfollowed, onUserUpdated, pullFromCache, pushToCache, queryCache, queryCategories, queryChannelMembers, queryChannels, queryComments, queryCommunities, queryCommunityMembers, queryFollowers, queryFollowings, queryGlobalFeed, queryMessages, queryOptions, queryPosts, queryReactions, queryRoles, queryStreams, queryUsers, removeChannelMembers, removeChannelMembersRole, removeCommunityMembers, removeCommunityMembersRoles, removeReaction, restoreCache, runQuery, sortByChannelSegment, sortByDisplayName, sortByFirstCreated, sortByLastActivity, sortByLastCreated, sortBySegmentNumber, subscribeTopic, toPage, toPageRaw, toToken, unbanChannelMembers, unfollow, updateChannel, updateComment, updateCommunity, updateMessage, updatePost, updateStream, updateUser, upsertInCache, votePoll, wipeCache };