@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.cjs.js CHANGED
@@ -3,20 +3,22 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var jsBase64 = require('js-base64');
6
- var mitt = require('mitt');
7
6
  var AsyncStorage = require('@react-native-async-storage/async-storage');
7
+ var mitt = require('mitt');
8
8
  var debug = require('debug');
9
9
  var axios = require('axios');
10
+ var qs = require('qs');
10
11
  var HttpAgent = require('agentkeepalive');
11
12
  var io = require('socket.io-client');
12
13
  var nativeIdGenerator = require('react-native-uuid');
13
14
 
14
15
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
15
16
 
16
- var mitt__default = /*#__PURE__*/_interopDefaultLegacy(mitt);
17
17
  var AsyncStorage__default = /*#__PURE__*/_interopDefaultLegacy(AsyncStorage);
18
+ var mitt__default = /*#__PURE__*/_interopDefaultLegacy(mitt);
18
19
  var debug__default = /*#__PURE__*/_interopDefaultLegacy(debug);
19
20
  var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
21
+ var qs__default = /*#__PURE__*/_interopDefaultLegacy(qs);
20
22
  var HttpAgent__default = /*#__PURE__*/_interopDefaultLegacy(HttpAgent);
21
23
  var io__default = /*#__PURE__*/_interopDefaultLegacy(io);
22
24
  var nativeIdGenerator__default = /*#__PURE__*/_interopDefaultLegacy(nativeIdGenerator);
@@ -75,6 +77,24 @@ const VideoSize = Object.freeze({
75
77
  ORIGINAL: 'original',
76
78
  });
77
79
 
80
+ const MessageContentType = Object.freeze({
81
+ TEXT: 'text',
82
+ IMAGE: 'image',
83
+ FILE: 'file',
84
+ VIDEO: 'video',
85
+ AUDIO: 'audio',
86
+ CUSTOM: 'custom',
87
+ });
88
+
89
+ const PostContentType = Object.freeze({
90
+ TEXT: 'text',
91
+ IMAGE: 'image',
92
+ FILE: 'file',
93
+ VIDEO: 'video',
94
+ LIVESTREAM: 'liveStream',
95
+ POLL: 'poll',
96
+ });
97
+
78
98
  var _a$1;
79
99
  /* eslint-disable no-undef */
80
100
  const theGlobal =
@@ -350,6 +370,78 @@ const PAYLOAD2MODEL = {
350
370
  followCounts: 'followCount',
351
371
  };
352
372
 
373
+ // cache constants
374
+ const CACHE_KEY_GET = 'get';
375
+ const CACHE_KEY_TOMBSTONE = 'dead';
376
+ const CACHE_LIFESPAN = 1 * 60 * 1000; // 1 minute
377
+ const CACHE_LIFESPAN_TOMBSTONE = 3 * 60 * 1000; // 3 minutes
378
+ /**
379
+ * Shallow-clones an object and sort its keys
380
+ *
381
+ * @param source a plain object to clone
382
+ * @returns a clone of source, with keys sorted with default javascript sorting
383
+ *
384
+ * @category Cache
385
+ * @hidden
386
+ */
387
+ const normalize = (source) => Object.keys(source)
388
+ .sort()
389
+ .reduce((acc, key) => (Object.assign(Object.assign({}, acc), { [key]: source[key] })), {});
390
+ /**
391
+ * Encodes a given {@link Amity.CacheKey} to a plain string
392
+ *
393
+ * @param key the key to encode
394
+ * @returns an encoded string
395
+ *
396
+ * @category Cache
397
+ * @hidden
398
+ */
399
+ const encodeKey = (key) => JSON.stringify(key, (_, val) => (typeof val === 'object' ? normalize(val) : val));
400
+ /**
401
+ * Decodes a string back into a {@link Amity.CacheKey}
402
+
403
+ *
404
+ * @param key the string key to decode
405
+ * @returns a plain Amity.CacheKey object.
406
+ *
407
+ * @category Cache
408
+ * @hidden
409
+ */
410
+ const decodeKey = (key) => JSON.parse(key);
411
+ /**
412
+ * Performs a recursive partial deep equal check on two objects.
413
+ *
414
+ * @param predicate the reference object containing the partial information
415
+ * @param candidate the object to check against
416
+ * @returns true if the candidate contains all the values of the predicate
417
+ *
418
+ * @category Cache
419
+ */
420
+ const partialMatch = (predicate, candidate) => {
421
+ if (predicate === candidate)
422
+ return true;
423
+ // if one or the other is nullish, there can't be equality
424
+ if (!predicate || !candidate)
425
+ return false;
426
+ // we only perform recursive check on objects
427
+ if (typeof predicate !== 'object') {
428
+ return false;
429
+ }
430
+ // recursively call for partial match based on the predicate
431
+ return Object.keys(predicate).every(key => partialMatch(predicate[key], candidate[key]));
432
+ };
433
+ /**
434
+ * checks if passed error code is included in
435
+ * Tombstone errors list
436
+ *
437
+ * @param errorCode as {@link Amity.ServerError}
438
+ * @returns success boolean if the given errorCode
439
+ * is included in Tombstone errors list
440
+ *
441
+ * @category Cache
442
+ */
443
+ const checkIfShouldGoesToTombstone = (errorCode) => [400400 /* Amity.ServerError.ITEM_NOT_FOUND */, 400300 /* Amity.ServerError.FORBIDDEN */].includes(errorCode);
444
+
353
445
  /* eslint-disable no-use-before-define */
354
446
  /**
355
447
  * Type guard to check and cast that a given async function is has the ".locally" property
@@ -408,7 +500,7 @@ const isLocal = (model) => {
408
500
  *
409
501
  * @hidden
410
502
  */
411
- const isFresh = (model, lifeSpan = 0) => {
503
+ const isFresh = (model, lifeSpan = CACHE_LIFESPAN) => {
412
504
  var _a;
413
505
  return Date.now() - ((_a = model === null || model === void 0 ? void 0 : model.cachedAt) !== null && _a !== void 0 ? _a : 0) <= lifeSpan;
414
506
  };
@@ -442,12 +534,10 @@ const createQuery = (func, ...args) => ({ func, args });
442
534
  *
443
535
  * @category Query
444
536
  */
445
- const queryOptions = (policy, lifeSpan = 2 * 60 * 1000) => {
537
+ const queryOptions = (policy, lifeSpan = CACHE_LIFESPAN) => {
446
538
  if (policy === 'cache_only')
447
539
  return { lifeSpan: Infinity };
448
- if (policy === 'server_only')
449
- return { lifeSpan: 0 };
450
- return { lifeSpan };
540
+ return { lifeSpan: lifeSpan < CACHE_LIFESPAN ? CACHE_LIFESPAN : lifeSpan };
451
541
  };
452
542
  /**
453
543
  * ```js
@@ -468,8 +558,9 @@ const queryOptions = (policy, lifeSpan = 2 * 60 * 1000) => {
468
558
  */
469
559
  const runQuery = ({ func, args }, callback, options = queryOptions('cache_then_server')) => {
470
560
  let local;
561
+ const { lifeSpan } = queryOptions('cache_then_server', options.lifeSpan);
471
562
  // offline first
472
- if (isOffline(func) && options.lifeSpan !== 0) {
563
+ if (isOffline(func)) {
473
564
  try {
474
565
  local = isMutator(func) ? func.optimistically(...args) : func.locally(...args);
475
566
  }
@@ -480,7 +571,7 @@ const runQuery = ({ func, args }, callback, options = queryOptions('cache_then_s
480
571
  error,
481
572
  }));
482
573
  }
483
- const shouldAbort = isCachable(local) && isFresh(local, options.lifeSpan);
574
+ const shouldAbort = isCachable(local) && isFresh(local, lifeSpan);
484
575
  callback === null || callback === void 0 ? void 0 : callback(createSnapshot(local, {
485
576
  origin: 'local',
486
577
  loading: !(isFetcher(func) && shouldAbort),
@@ -547,54 +638,6 @@ function createSnapshot(data, options) {
547
638
  return Object.assign(Object.assign({}, options), { data });
548
639
  }
549
640
 
550
- // Note:
551
- // this file should contain a suite of filtering utilities to help the
552
- // local version of the query functions.
553
- /**
554
- * Filter a given collection with strict equality against a param
555
- *
556
- * @param collection the collection to filter
557
- * @param key the key of the collection's items to challenge
558
- * @param value the expected value
559
- * @returns a filtered collection with items only matching the criteria
560
- *
561
- * @hidden
562
- */
563
- const filterByPropEquality = (collection, key, value) => (value !== undefined ? collection.filter(item => item[key] === value) : collection);
564
- const filterByPropInclusion = (collection, key, value) => (value !== undefined ? collection.filter(item => value.includes(item[key])) : collection);
565
-
566
- // Note:
567
- // this file should contain a suite of sorting utilities to help the
568
- // local version of the query functions.
569
- /**
570
- * Alphabetic sorting of objects having a displayName
571
- */
572
- const sortByDisplayName = ({ displayName: a }, { displayName: b }) => {
573
- if (a === b)
574
- return 0;
575
- return a < b ? -1 : 1;
576
- };
577
- /**
578
- * Sorting a collection by their apparition order (oldest first)
579
- */
580
- const sortByChannelSegment = ({ channelSegment: a }, { channelSegment: b }) => a - b;
581
- /**
582
- * Sorting a collection by their apparition order (oldest first)
583
- */
584
- const sortBySegmentNumber = ({ segmentNumber: a }, { segmentNumber: b }) => a - b;
585
- /**
586
- * Sorting a collection by its oldest items
587
- */
588
- const sortByFirstCreated = ({ createdAt: a }, { createdAt: b }) => new Date(a).valueOf() - new Date(b).valueOf();
589
- /**
590
- * Sorting a collection by its newest items
591
- */
592
- const sortByLastCreated = ({ createdAt: a }, { createdAt: b }) => new Date(b).valueOf() - new Date(a).valueOf();
593
- /**
594
- * Sorting a collection by the items with most recent activity
595
- */
596
- const sortByLastActivity = ({ lastActivity: a }, { lastActivity: b }) => new Date(b).valueOf() - new Date(a).valueOf();
597
-
598
641
  /* eslint-disable max-classes-per-file */
599
642
  /**
600
643
  * Generic ASC error
@@ -684,265 +727,70 @@ const setActiveClient = (client) => {
684
727
  activeClient = client;
685
728
  };
686
729
 
687
- /* eslint-disable @typescript-eslint/ban-types */
688
- let tasks = [];
689
- let timer;
690
730
  /**
691
- * Add a function to run just before the next tick
731
+ * ```js
732
+ * import { enableCache } from '@amityco/ts-sdk'
733
+ * enableCache()
734
+ * ```
692
735
  *
693
- * @param task function to schedule for later execution
736
+ * Adds a new {@link Amity.Cache} object to
737
+ * an {@link Amity.Client} instance
738
+ *
739
+ * @param prevState a previous state of cache instance (useful for SSR)
740
+ * @param persistIf a function to determine if an entry inserted in cache
741
+ * is destined to be also saved in the persistent storage when
742
+ * calling {@link backupCache}
743
+ *
744
+ * @category Cache API
694
745
  */
695
- const scheduleTask = (task) => {
696
- clearTimeout(timer);
697
- tasks.push(task);
698
- timer = setTimeout(() => {
699
- tasks.forEach(fn => fn());
700
- tasks = [];
701
- }, 0);
746
+ const enableCache = (prevState = {}, persistIf) => {
747
+ const client = getActiveClient();
748
+ if (client.cache)
749
+ return;
750
+ client.log('cache/api/enableCache');
751
+ client.cache = { data: prevState, persistIf };
702
752
  };
703
753
 
704
- /* eslint-disable @typescript-eslint/no-unused-vars */
705
- // need to sync up the list
706
- const WS_EVENTS = [
707
- 'disconnected',
708
- 'error',
709
- 'connect_error',
710
- 'reconnect_error',
711
- 'reconnect_failed',
712
- 'v3.channel.didCreate',
713
- 'v3.channel.didUpdate',
714
- 'v3.channel.didDelete',
715
- 'v3.channel.didJoin',
716
- 'v3.channel.didLeave',
717
- 'v3.channel.didAddUsers',
718
- 'v3.channel.didRemoveUsers',
719
- 'v3.channel.didBan',
720
- 'v3.channel.didUnban',
721
- 'v3.message.didCreate',
722
- 'v3.message.didUpdate',
723
- 'v3.message.didDelete',
724
- ];
725
- const MQTT_EVENTS = [
726
- 'connect',
727
- 'message',
728
- 'disconnect',
729
- 'error',
730
- 'close',
731
- 'end',
732
- 'reconnect',
733
- ];
734
- /** @hidden */
735
- const createEventEmitter = () => {
736
- return mitt__default["default"]();
737
- };
738
754
  /**
739
- * Wraps the websocket events into the client's event emitter for
740
- * abstraction of transport.
755
+ * ```js
756
+ * import { disableCache } from '@amityco/ts-sdk'
757
+ * disableCache()
758
+ * ```
741
759
  *
742
- * @param ws A websocket connection to listen from
743
- * @param emitter An event emitter to wire the events to
760
+ * Wipes the existing {@link Amity.Cache} object attached to
761
+ * an {@link Amity.Client} instance
744
762
  *
745
- * @category Transport
746
- * @hidden
763
+ * @category Cache API
747
764
  */
748
- const proxyWebsocketEvents = (ws, emitter) => {
749
- WS_EVENTS.forEach(event => {
750
- ws.on(event, (param) => emitter.emit(event, param));
751
- });
752
- };
753
- const proxyMqttEvents = (mqttClient, emitter) => {
754
- MQTT_EVENTS.forEach(event => {
755
- mqttClient.on(event, (...params) => emitter.emit(event, params.length === 1 ? params[0] : params));
756
- });
757
- // @ts-ignore
758
- mqttClient.on('message', (topic, payload) => {
759
- const message = JSON.parse(payload.toString());
760
- emitter.emit(message.eventType, message.data);
761
- });
762
- };
765
+ const disableCache = () => {
766
+ const client = getActiveClient();
767
+ if (!client.cache)
768
+ return;
769
+ client.log('cache/api/disableCache');
770
+ // we do this so that testing if cache is enabled
771
+ // is only `if (client.cache)
772
+ delete client.cache;
773
+ };
774
+
763
775
  /**
764
- * Standardize the subscription of SSE through web sockets
776
+ * ```js
777
+ * import { restoreCache } from '@amityco/ts-sdk'
778
+ * const success = await restoreCache()
779
+ * ```
765
780
  *
766
- * @param client The current client for which to subscribe the event to
767
- * @param namespace A unique name for the logger
768
- * @param event The websocket event name
769
- * @param fn A wrapper for the callback.
770
- * @returns A dispose function to unsubscribe to the event
781
+ * Reads a previously saved {@link Amity.Cache} from a persistent storage,
782
+ * and inserts it into the current {@link Amity.Cache} instance.
771
783
  *
772
- * @category Transport
773
- * @hidden
774
- */
775
- const createEventSubscriber = (client, namespace, event, fn) => {
776
- const { log, emitter } = client;
777
- const timestamp = Date.now();
778
- log(`${namespace}(tmpid: ${timestamp}) > listen`);
779
- const handler = (...payload) => {
780
- log(`${namespace}(tmpid: ${timestamp}) > trigger`, payload);
781
- fn(...payload);
782
- };
783
- emitter.on(event, handler);
784
- return () => {
785
- log(`${namespace}(tmpid: ${timestamp}) > dispose`);
786
- emitter.off(event, handler);
787
- };
788
- };
789
- /**
790
- * Wrapper around dispatch event
791
- *
792
- * @hidden
793
- */
794
- const fireEvent = (event, payload) => {
795
- const { emitter } = getActiveClient();
796
- scheduleTask(() => {
797
- emitter.emit(event, payload);
798
- });
799
- };
800
-
801
- let activeUser = null;
802
- /**
803
- * for internal use
804
- */
805
- const getActiveUser = () => {
806
- if (!activeUser) {
807
- throw new ASCError('Connect client first', 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "fatal" /* Amity.ErrorLevel.FATAL */);
808
- }
809
- return activeUser;
810
- };
811
- const setActiveUser = (user) => {
812
- activeUser = {
813
- _id: user._id,
814
- userId: user.userId,
815
- path: user.path,
816
- };
817
- };
818
-
819
- exports.SubscriptionLevels = void 0;
820
- (function (SubscriptionLevels) {
821
- SubscriptionLevels["COMMUNITY"] = "community";
822
- SubscriptionLevels["POST"] = "post";
823
- SubscriptionLevels["COMMENT"] = "comment";
824
- SubscriptionLevels["POST_AND_COMMENT"] = "post_and_comment";
825
- SubscriptionLevels["USER"] = "user";
826
- })(exports.SubscriptionLevels || (exports.SubscriptionLevels = {}));
827
- const getCommunityUserTopic = (path, level) => {
828
- switch (level) {
829
- case 'post':
830
- return `${path}/post/+`;
831
- case 'comment':
832
- return `${path}/post/+/comment/+`;
833
- case 'post_and_comment':
834
- return `${path}/post/#`;
835
- default:
836
- return path;
837
- }
838
- };
839
- const getCommunityTopic = ({ path }, level = exports.SubscriptionLevels.COMMUNITY) => getCommunityUserTopic(path, level);
840
- const getUserTopic = ({ path }, level = exports.SubscriptionLevels.USER) => getCommunityUserTopic(level === exports.SubscriptionLevels.USER ? path : path.replace(/^(\w*)/, '$1/social'), level);
841
- const getPostTopic = ({ path }, level = exports.SubscriptionLevels.POST) => {
842
- switch (level) {
843
- case 'comment':
844
- return `${path}/comment/+`;
845
- default:
846
- return path;
847
- }
848
- };
849
- const getCommentTopic = ({ path }) => {
850
- return path;
851
- };
852
- const getMyFollowersTopic = () => {
853
- const user = getActiveUser();
854
- const [networkId] = user.path.split('/user/');
855
- return `${networkId}/membership/${user._id}/+/+`;
856
- };
857
- const getMyFollowingsTopic = () => {
858
- const user = getActiveUser();
859
- const [networkId] = user.path.split('/user/');
860
- return `${networkId}/membership/+/${user._id}/+`;
861
- };
862
- /** @hidden */
863
- const getNetworkTopic = () => {
864
- const user = getActiveUser();
865
- const [networkId] = user.path.split('/user/');
866
- return networkId;
867
- };
868
- let mqttAccessToken;
869
- let mqttUserId;
870
- function subscribeTopic(topic, callback) {
871
- const { mqtt, emitter, accessToken } = getActiveClient();
872
- const user = getActiveUser();
873
- if (mqttAccessToken !== accessToken || mqttUserId !== user._id) {
874
- mqttAccessToken = accessToken;
875
- mqttUserId = user._id;
876
- mqtt.connect({ accessToken: mqttAccessToken, userId: mqttUserId });
877
- proxyMqttEvents(mqtt, emitter);
878
- }
879
- return mqtt.subscribe(topic, callback);
880
- }
881
-
882
- /**
883
- * ```js
884
- * import { enableCache } from '@amityco/ts-sdk'
885
- * enableCache()
886
- * ```
887
- *
888
- * Adds a new {@link Amity.Cache} object to
889
- * an {@link Amity.Client} instance
890
- *
891
- * @param prevState a previous state of cache instance (useful for SSR)
892
- * @param persistIf a function to determine if an entry inserted in cache
893
- * is destined to be also saved in the persistent storage when
894
- * calling {@link backupCache}
895
- *
896
- * @category Cache API
897
- */
898
- const enableCache = (prevState = {}, persistIf) => {
899
- const client = getActiveClient();
900
- if (client.cache)
901
- return;
902
- client.log('cache/api/enableCache');
903
- client.cache = { data: prevState, persistIf };
904
- };
905
-
906
- /**
907
- * ```js
908
- * import { disableCache } from '@amityco/ts-sdk'
909
- * disableCache()
910
- * ```
911
- *
912
- * Wipes the existing {@link Amity.Cache} object attached to
913
- * an {@link Amity.Client} instance
914
- *
915
- * @category Cache API
916
- */
917
- const disableCache = () => {
918
- const client = getActiveClient();
919
- if (!client.cache)
920
- return;
921
- client.log('cache/api/disableCache');
922
- // we do this so that testing if cache is enabled
923
- // is only `if (client.cache)
924
- delete client.cache;
925
- };
926
-
927
- /**
928
- * ```js
929
- * import { restoreCache } from '@amityco/ts-sdk'
930
- * const success = await restoreCache()
931
- * ```
932
- *
933
- * Reads a previously saved {@link Amity.Cache} from a persistent storage,
934
- * and inserts it into the current {@link Amity.Cache} instance.
935
- *
936
- * The strategy for persistent storage will depend on the runtime,
937
- * which is supported by @react-native-async-storage/async-storage.
938
- *
939
- * The current userId will be appended to the given storageKey to ensures
940
- * the cached data concerns only the current user.
941
- *
942
- * @param storageKey the name of the persistent storage
943
- * @returns a success boolean if the cache was dumped to persistent storage
944
- *
945
- * @category Cache API
784
+ * The strategy for persistent storage will depend on the runtime,
785
+ * which is supported by @react-native-async-storage/async-storage.
786
+ *
787
+ * The current userId will be appended to the given storageKey to ensures
788
+ * the cached data concerns only the current user.
789
+ *
790
+ * @param storageKey the name of the persistent storage
791
+ * @returns a success boolean if the cache was dumped to persistent storage
792
+ *
793
+ * @category Cache API
946
794
  */
947
795
  const restoreCache = async (storageKey = 'amitySdk') => {
948
796
  var _a;
@@ -963,7 +811,6 @@ const restoreCache = async (storageKey = 'amitySdk') => {
963
811
  return true;
964
812
  };
965
813
 
966
- /* eslint-disable @typescript-eslint/no-unused-vars */
967
814
  /**
968
815
  * ```js
969
816
  * import { backupCache } from '@amityco/ts-sdk'
@@ -1030,62 +877,6 @@ const wipeCache = async (storageKey = 'amitySdk') => {
1030
877
  return true;
1031
878
  };
1032
879
 
1033
- /**
1034
- * Shallow-clones an object and sort its keys
1035
- *
1036
- * @param source a plain object to clone
1037
- * @returns a clone of source, with keys sorted with default javascript sorting
1038
- *
1039
- * @category Cache
1040
- * @hidden
1041
- */
1042
- const normalize = (source) => Object.keys(source)
1043
- .sort()
1044
- .reduce((acc, key) => (Object.assign(Object.assign({}, acc), { [key]: source[key] })), {});
1045
- /**
1046
- * Encodes a given {@link Amity.CacheKey} to a plain string
1047
- *
1048
- * @param key the key to encode
1049
- * @returns an encoded string
1050
- *
1051
- * @category Cache
1052
- * @hidden
1053
- */
1054
- const encodeKey = (key) => JSON.stringify(key, (_, val) => (typeof val === 'object' ? normalize(val) : val));
1055
- /**
1056
- * Decodes a string back into a {@link Amity.CacheKey}
1057
-
1058
- *
1059
- * @param key the string key to decode
1060
- * @returns a plain Amity.CacheKey object.
1061
- *
1062
- * @category Cache
1063
- * @hidden
1064
- */
1065
- const decodeKey = (key) => JSON.parse(key);
1066
- /**
1067
- * Performs a recursive partial deep equal check on two objects.
1068
- *
1069
- * @param predicate the reference object containing the partial information
1070
- * @param candidate the object to check against
1071
- * @returns true if the candidate contains all the values of the predicate
1072
- *
1073
- * @category Cache
1074
- */
1075
- const partialMatch = (predicate, candidate) => {
1076
- if (predicate === candidate)
1077
- return true;
1078
- // if one or the other is nullish, there can't be equality
1079
- if (!predicate || !candidate)
1080
- return false;
1081
- // we only perform recursive check on objects
1082
- if (typeof predicate !== 'object') {
1083
- return false;
1084
- }
1085
- // recursively call for partial match based on the predicate
1086
- return Object.keys(predicate).every(key => partialMatch(predicate[key], candidate[key]));
1087
- };
1088
-
1089
880
  /**
1090
881
  * ```js
1091
882
  * import { queryCache } from '@amityco/ts-sdk'
@@ -1202,66 +993,348 @@ const mergeInCache = (key, mutation, options) => {
1202
993
  };
1203
994
 
1204
995
  /**
1205
- * ```js
1206
- * import { upsertInCache } from '@amityco/ts-sdk'
1207
- * upsertInCache<Amity.User>(["user", "foobar"], user)
1208
- * ```
1209
- *
1210
- * Insert or update any provided value as {@link Amity.CacheEntry} for
1211
- * the matching {@link Amity.CacheKey}
1212
- *
1213
- * @param key the key to save the object to
1214
- * @param data the object to save
1215
- * @param options customisation object around cache behavior (default gives a 2mn lifespan)
1216
- * @returns a success boolean if the object was saved in cache
996
+ * ```js
997
+ * import { upsertInCache } from '@amityco/ts-sdk'
998
+ * upsertInCache<Amity.User>(["user", "foobar"], user)
999
+ * ```
1000
+ *
1001
+ * Insert or update any provided value as {@link Amity.CacheEntry} for
1002
+ * the matching {@link Amity.CacheKey}
1003
+ *
1004
+ * @param key the key to save the object to
1005
+ * @param data the object to save
1006
+ * @param options customisation object around cache behavior (default gives a 2mn lifespan)
1007
+ * @returns a success boolean if the object was saved in cache
1008
+ *
1009
+ * @category Cache API
1010
+ * @hidden
1011
+ */
1012
+ const upsertInCache = (key, data, options = { cachedAt: Date.now() }) => {
1013
+ const { log, cache } = getActiveClient();
1014
+ if (!cache)
1015
+ return false;
1016
+ log('cache/api/upsertInCache', { key, data, options });
1017
+ const cached = pullFromCache(key);
1018
+ return cached ? mergeInCache(key, data, options) : pushToCache(key, data, options);
1019
+ };
1020
+
1021
+ /**
1022
+ * ```js
1023
+ * import { dropFromCache } from '@amityco/ts-sdk'
1024
+ * const success = dropFromCache(['user', 'foobar'])
1025
+ * ```
1026
+ *
1027
+ * Removes an existing {@link Amity.CacheEntry} from the {@link Amity.Client}'s
1028
+ * {@link Amity.Cache} from a given {@link Amity.CacheKey}
1029
+ *
1030
+ * @param key The key of the object to delete
1031
+ * @param exact If false, the function will delete all keys satisfying the given key
1032
+ * @returns A success boolean if the object was deleted
1033
+ *
1034
+ * @category Cache API
1035
+ */
1036
+ const dropFromCache = (key, exact = false) => {
1037
+ const { log, cache } = getActiveClient();
1038
+ if (!cache)
1039
+ return false;
1040
+ log('cache/api/dropFromCache', { key, exact });
1041
+ if (!exact) {
1042
+ return Object.keys(cache.data)
1043
+ .map(stringKey => decodeKey(stringKey))
1044
+ .filter(candidate => partialMatch(key, candidate))
1045
+ .map(filteredKey => dropFromCache(filteredKey, true))
1046
+ .every(returned => returned);
1047
+ }
1048
+ const encodedKey = encodeKey(key);
1049
+ if (encodedKey in cache.data) {
1050
+ delete cache.data[encodedKey];
1051
+ return true;
1052
+ }
1053
+ return false;
1054
+ };
1055
+
1056
+ // Note:
1057
+ // this file should contain a suite of filtering utilities to help the
1058
+ // local version of the query functions.
1059
+ /**
1060
+ * Filter a given collection with strict equality against a param
1061
+ *
1062
+ * @param collection the collection to filter
1063
+ * @param key the key of the collection's items to challenge
1064
+ * @param value the expected value
1065
+ * @returns a filtered collection with items only matching the criteria
1066
+ *
1067
+ * @hidden
1068
+ */
1069
+ const filterByPropEquality = (collection, key, value) => (value !== undefined ? collection.filter(item => item[key] === value) : collection);
1070
+ const filterByPropInclusion = (collection, key, value) => (value !== undefined ? collection.filter(item => value.includes(item[key])) : collection);
1071
+ /**
1072
+ * Filter a channel collection by membership of the userId
1073
+ *
1074
+ * @param collection the channel collection to filter
1075
+ * @param membership the membership to be filtered by
1076
+ * @param userId user id to be filtered by
1077
+ * @returns a filtered collection with items only matching the criteria
1078
+ *
1079
+ * @hidden
1080
+ */
1081
+ const filterByChannelMembership = (collection, membership, userId) => {
1082
+ if (membership === 'all') {
1083
+ return collection;
1084
+ }
1085
+ return collection.filter(c => {
1086
+ var _a;
1087
+ // get resolver for the channel by user
1088
+ const channelUserCacheKey = getResolver('channelUsers')({
1089
+ channelId: c.channelId,
1090
+ userId,
1091
+ });
1092
+ const channelUser = (_a = pullFromCache([
1093
+ 'channelUsers',
1094
+ 'get',
1095
+ channelUserCacheKey,
1096
+ ])) === null || _a === void 0 ? void 0 : _a.data;
1097
+ // if channel user is defined for user, user is a member
1098
+ if (membership === 'member' && channelUser) {
1099
+ return true;
1100
+ }
1101
+ // if channel user by user id not found, user is not a member of that
1102
+ // channel
1103
+ if (membership === 'notMember' && !channelUser) {
1104
+ return true;
1105
+ }
1106
+ return false;
1107
+ });
1108
+ };
1109
+
1110
+ // Note:
1111
+ // this file should contain a suite of sorting utilities to help the
1112
+ // local version of the query functions.
1113
+ /**
1114
+ * Alphabetic sorting of objects having a displayName
1115
+ */
1116
+ const sortByDisplayName = ({ displayName: a }, { displayName: b }) => {
1117
+ if (a === b)
1118
+ return 0;
1119
+ return a < b ? -1 : 1;
1120
+ };
1121
+ /**
1122
+ * Sorting a collection by their apparition order (oldest first)
1123
+ */
1124
+ const sortByChannelSegment = ({ channelSegment: a }, { channelSegment: b }) => a - b;
1125
+ /**
1126
+ * Sorting a collection by their apparition order (oldest first)
1127
+ */
1128
+ const sortBySegmentNumber = ({ segmentNumber: a }, { segmentNumber: b }) => a - b;
1129
+ /**
1130
+ * Sorting a collection by its oldest items
1131
+ */
1132
+ const sortByFirstCreated = ({ createdAt: a }, { createdAt: b }) => new Date(a).valueOf() - new Date(b).valueOf();
1133
+ /**
1134
+ * Sorting a collection by its newest items
1135
+ */
1136
+ const sortByLastCreated = ({ createdAt: a }, { createdAt: b }) => new Date(b).valueOf() - new Date(a).valueOf();
1137
+ /**
1138
+ * Sorting a collection by the items with most recent activity
1139
+ */
1140
+ const sortByLastActivity = ({ lastActivity: a }, { lastActivity: b }) => new Date(b).valueOf() - new Date(a).valueOf();
1141
+
1142
+ /* eslint-disable @typescript-eslint/ban-types */
1143
+ let tasks = [];
1144
+ let timer;
1145
+ /**
1146
+ * Add a function to run just before the next tick
1147
+ *
1148
+ * @param task function to schedule for later execution
1149
+ */
1150
+ const scheduleTask = (task) => {
1151
+ clearTimeout(timer);
1152
+ tasks.push(task);
1153
+ timer = setTimeout(() => {
1154
+ tasks.forEach(fn => fn());
1155
+ tasks = [];
1156
+ }, 0);
1157
+ };
1158
+
1159
+ /* eslint-disable @typescript-eslint/no-unused-vars */
1160
+ // need to sync up the list
1161
+ const WS_EVENTS = [
1162
+ 'disconnected',
1163
+ 'error',
1164
+ 'connect_error',
1165
+ 'reconnect_error',
1166
+ 'reconnect_failed',
1167
+ 'v3.channel.didCreate',
1168
+ 'v3.channel.didUpdate',
1169
+ 'v3.channel.didDelete',
1170
+ 'v3.channel.didJoin',
1171
+ 'v3.channel.didLeave',
1172
+ 'v3.channel.didAddUsers',
1173
+ 'v3.channel.didRemoveUsers',
1174
+ 'v3.channel.didBan',
1175
+ 'v3.channel.didUnban',
1176
+ 'v3.message.didFetch',
1177
+ 'v3.message.didCreate',
1178
+ 'v3.message.didUpdate',
1179
+ 'v3.message.didDelete',
1180
+ ];
1181
+ const MQTT_EVENTS = [
1182
+ 'connect',
1183
+ 'message',
1184
+ 'disconnect',
1185
+ 'error',
1186
+ 'close',
1187
+ 'end',
1188
+ 'reconnect',
1189
+ ];
1190
+ /** @hidden */
1191
+ const createEventEmitter = () => {
1192
+ return mitt__default["default"]();
1193
+ };
1194
+ /**
1195
+ * Wraps the websocket events into the client's event emitter for
1196
+ * abstraction of transport.
1197
+ *
1198
+ * @param ws A websocket connection to listen from
1199
+ * @param emitter An event emitter to wire the events to
1200
+ *
1201
+ * @category Transport
1202
+ * @hidden
1203
+ */
1204
+ const proxyWebsocketEvents = (ws, emitter) => {
1205
+ WS_EVENTS.forEach(event => {
1206
+ ws.on(event, (param) => emitter.emit(event, param));
1207
+ });
1208
+ };
1209
+ const proxyMqttEvents = (mqttClient, emitter) => {
1210
+ MQTT_EVENTS.forEach(event => {
1211
+ mqttClient.on(event, (...params) => emitter.emit(event, params.length === 1 ? params[0] : params));
1212
+ });
1213
+ // @ts-ignore
1214
+ mqttClient.on('message', (topic, payload) => {
1215
+ const message = JSON.parse(payload.toString());
1216
+ emitter.emit(message.eventType, message.data);
1217
+ });
1218
+ };
1219
+ /**
1220
+ * Standardize the subscription of SSE through web sockets
1221
+ *
1222
+ * @param client The current client for which to subscribe the event to
1223
+ * @param namespace A unique name for the logger
1224
+ * @param event The websocket event name
1225
+ * @param fn A wrapper for the callback.
1226
+ * @returns A dispose function to unsubscribe to the event
1227
+ *
1228
+ * @category Transport
1229
+ * @hidden
1230
+ */
1231
+ const createEventSubscriber = (client, namespace, event, fn) => {
1232
+ const { log, emitter } = client;
1233
+ const timestamp = Date.now();
1234
+ log(`${namespace}(tmpid: ${timestamp}) > listen`);
1235
+ const handler = (...payload) => {
1236
+ log(`${namespace}(tmpid: ${timestamp}) > trigger`, payload);
1237
+ fn(...payload);
1238
+ };
1239
+ emitter.on(event, handler);
1240
+ return () => {
1241
+ log(`${namespace}(tmpid: ${timestamp}) > dispose`);
1242
+ emitter.off(event, handler);
1243
+ };
1244
+ };
1245
+ /**
1246
+ * Wrapper around dispatch event
1217
1247
  *
1218
- * @category Cache API
1219
1248
  * @hidden
1220
1249
  */
1221
- const upsertInCache = (key, data, options = { cachedAt: Date.now() }) => {
1222
- const { log, cache } = getActiveClient();
1223
- if (!cache)
1224
- return false;
1225
- log('cache/api/upsertInCache', { key, data, options });
1226
- const cached = pullFromCache(key);
1227
- return cached ? mergeInCache(key, data, options) : pushToCache(key, data, options);
1250
+ const fireEvent = (event, payload) => {
1251
+ const { emitter } = getActiveClient();
1252
+ scheduleTask(() => {
1253
+ emitter.emit(event, payload);
1254
+ });
1228
1255
  };
1229
1256
 
1257
+ let activeUser = null;
1230
1258
  /**
1231
- * ```js
1232
- * import { dropFromCache } from '@amityco/ts-sdk'
1233
- * const success = dropFromCache(['user', 'foobar'])
1234
- * ```
1235
- *
1236
- * Removes an existing {@link Amity.CacheEntry} from the {@link Amity.Client}'s
1237
- * {@link Amity.Cache} from a given {@link Amity.CacheKey}
1238
- *
1239
- * @param key The key of the object to delete
1240
- * @param exact If false, the function will delete all keys satisfying the given key
1241
- * @returns A success boolean if the object was deleted
1242
- *
1243
- * @category Cache API
1259
+ * for internal use
1244
1260
  */
1245
- const dropFromCache = (key, exact = false) => {
1246
- const { log, cache } = getActiveClient();
1247
- if (!cache)
1248
- return false;
1249
- log('cache/api/dropFromCache', { key, exact });
1250
- if (!exact) {
1251
- return Object.keys(cache.data)
1252
- .map(stringKey => decodeKey(stringKey))
1253
- .filter(candidate => partialMatch(key, candidate))
1254
- .map(filteredKey => dropFromCache(filteredKey, true))
1255
- .every(returned => returned);
1256
- }
1257
- const encodedKey = encodeKey(key);
1258
- if (encodedKey in cache.data) {
1259
- delete cache.data[encodedKey];
1260
- return true;
1261
+ const getActiveUser = () => {
1262
+ if (!activeUser) {
1263
+ throw new ASCError('Connect client first', 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "fatal" /* Amity.ErrorLevel.FATAL */);
1261
1264
  }
1262
- return false;
1265
+ return activeUser;
1266
+ };
1267
+ const setActiveUser = (user) => {
1268
+ activeUser = {
1269
+ _id: user._id,
1270
+ userId: user.userId,
1271
+ path: user.path,
1272
+ };
1263
1273
  };
1264
1274
 
1275
+ exports.SubscriptionLevels = void 0;
1276
+ (function (SubscriptionLevels) {
1277
+ SubscriptionLevels["COMMUNITY"] = "community";
1278
+ SubscriptionLevels["POST"] = "post";
1279
+ SubscriptionLevels["COMMENT"] = "comment";
1280
+ SubscriptionLevels["POST_AND_COMMENT"] = "post_and_comment";
1281
+ SubscriptionLevels["USER"] = "user";
1282
+ })(exports.SubscriptionLevels || (exports.SubscriptionLevels = {}));
1283
+ const getCommunityUserTopic = (path, level) => {
1284
+ switch (level) {
1285
+ case 'post':
1286
+ return `${path}/post/+`;
1287
+ case 'comment':
1288
+ return `${path}/post/+/comment/+`;
1289
+ case 'post_and_comment':
1290
+ return `${path}/post/#`;
1291
+ default:
1292
+ return path;
1293
+ }
1294
+ };
1295
+ const getCommunityTopic = ({ path }, level = exports.SubscriptionLevels.COMMUNITY) => getCommunityUserTopic(path, level);
1296
+ const getUserTopic = ({ path }, level = exports.SubscriptionLevels.USER) => getCommunityUserTopic(level === exports.SubscriptionLevels.USER ? path : path.replace(/^(\w*)/, '$1/social'), level);
1297
+ const getPostTopic = ({ path }, level = exports.SubscriptionLevels.POST) => {
1298
+ switch (level) {
1299
+ case 'comment':
1300
+ return `${path}/comment/+`;
1301
+ default:
1302
+ return path;
1303
+ }
1304
+ };
1305
+ const getCommentTopic = ({ path }) => {
1306
+ return path;
1307
+ };
1308
+ const getMyFollowersTopic = () => {
1309
+ const user = getActiveUser();
1310
+ const [networkId] = user.path.split('/user/');
1311
+ return `${networkId}/membership/${user._id}/+/+`;
1312
+ };
1313
+ const getMyFollowingsTopic = () => {
1314
+ const user = getActiveUser();
1315
+ const [networkId] = user.path.split('/user/');
1316
+ return `${networkId}/membership/+/${user._id}/+`;
1317
+ };
1318
+ /** @hidden */
1319
+ const getNetworkTopic = () => {
1320
+ const user = getActiveUser();
1321
+ const [networkId] = user.path.split('/user/');
1322
+ return networkId;
1323
+ };
1324
+ let mqttAccessToken;
1325
+ let mqttUserId;
1326
+ function subscribeTopic(topic, callback) {
1327
+ const { mqtt, emitter, accessToken } = getActiveClient();
1328
+ const user = getActiveUser();
1329
+ if (mqttAccessToken !== accessToken || mqttUserId !== user._id) {
1330
+ mqttAccessToken = accessToken;
1331
+ mqttUserId = user._id;
1332
+ mqtt.connect({ accessToken: mqttAccessToken, userId: mqttUserId });
1333
+ proxyMqttEvents(mqtt, emitter);
1334
+ }
1335
+ return mqtt.subscribe(topic, callback);
1336
+ }
1337
+
1265
1338
  /**
1266
1339
  * Creates a pre-configured logger instance
1267
1340
  *
@@ -1324,21 +1397,8 @@ const unwrapPayload = (response) => {
1324
1397
  * Axios param serializer (for querystring)
1325
1398
  * @hidden
1326
1399
  */
1327
- const stringify = (params, prefix) => {
1328
- return Object.entries(params)
1329
- .filter(([, value]) => value !== undefined)
1330
- .map(([name, value]) => {
1331
- const key = prefix ? `${prefix}[${name}]` : name;
1332
- if (Array.isArray(value)) {
1333
- return value.map(item => `${key}[]=${item}`).join('&');
1334
- }
1335
- if (typeof value !== 'object') {
1336
- return `${key}=${value}`;
1337
- }
1338
- // @ts-ignore
1339
- return stringify(value, prefix !== null && prefix !== void 0 ? prefix : name);
1340
- })
1341
- .join('&');
1400
+ const stringify = (params) => {
1401
+ return qs__default["default"].stringify(params, { arrayFormat: 'brackets' });
1342
1402
  };
1343
1403
  /**
1344
1404
  * Creates a pre-configured axios instance
@@ -20578,6 +20638,61 @@ const isConnected = () => {
20578
20638
  client.ws.connected);
20579
20639
  };
20580
20640
 
20641
+ /**
20642
+ * ```js
20643
+ * import { isInTombstone } from '@amityco/ts-sdk'
20644
+ * const user = isInTombstone(["message", "messageId"])
20645
+ * ```
20646
+ *
20647
+ * Checks if the {@link Amity.TombstoneCacheOptions} exists
20648
+ * in cache and it's not expired means it's in tombstone
20649
+ * and we throw an Error
20650
+ *
20651
+ * @param model the model to check
20652
+ * @param modelId the object id to check
20653
+ * @returns the matching cache entry, or undefined.
20654
+ *
20655
+ * @category Cache API
20656
+ */
20657
+ const isInTombstone = (model, modelId) => {
20658
+ const { log, cache } = getActiveClient();
20659
+ const key = [model, CACHE_KEY_TOMBSTONE, modelId];
20660
+ if (!cache)
20661
+ return;
20662
+ log('cache/api/isInTombstone', key);
20663
+ const isInTombstone = pullFromCache(key);
20664
+ const { lifeSpan } = queryOptions('cache_then_server', CACHE_LIFESPAN_TOMBSTONE);
20665
+ if (isInTombstone && isFresh(isInTombstone.data, lifeSpan)) {
20666
+ throw new ASCApiError('Item not found!', 400400 /* Amity.ServerError.ITEM_NOT_FOUND */, "error" /* Amity.ErrorLevel.ERROR */);
20667
+ }
20668
+ };
20669
+
20670
+ /**
20671
+ * ```js
20672
+ * import { pushToTombstone } from '@amityco/ts-sdk'
20673
+ * pushToTombstone(["message", "messageId"], { cachedAt: Date.now() })
20674
+ * ```
20675
+ *
20676
+ * Removes an existing {@link Amity.CacheEntry} from the {@link Amity.Client}'s
20677
+ * {@link Amity.Cache} from a given {@link Amity.CacheKey}
20678
+ *
20679
+ * @param model the model to remove and push to Tombstone
20680
+ * @param modelId the related model id
20681
+ *
20682
+ * @category Cache API
20683
+ * @hidden
20684
+ */
20685
+ const pushToTombstone = (model, modelId) => {
20686
+ const { log, cache } = getActiveClient();
20687
+ const cacheKey = [model, CACHE_KEY_TOMBSTONE, modelId];
20688
+ const data = { cachedAt: Date.now() };
20689
+ if (!cache)
20690
+ return;
20691
+ log('cache/api/pushToTombstone', { cacheKey, data });
20692
+ dropFromCache([model, CACHE_KEY_GET, modelId], true);
20693
+ pushToCache(cacheKey, data);
20694
+ };
20695
+
20581
20696
  /**
20582
20697
  * ```js
20583
20698
  * import { getUser } from '@amityco/ts-sdk'
@@ -20592,15 +20707,29 @@ const isConnected = () => {
20592
20707
  * @category User API
20593
20708
  * @async
20594
20709
  */
20595
- const getUser = async (userId) => {
20710
+ const getUser = async (userId, isLive = false) => {
20596
20711
  const client = getActiveClient();
20597
20712
  client.log('user/getUser', userId);
20598
- // API-FIX: endpoint should not be /list, parameters should be querystring.
20599
- const { data } = await client.http.get(`/api/v3/users/${encodeURIComponent(userId)}`);
20713
+ if (!isLive) {
20714
+ console.log('getUser will be deprecated on 25th November 2022, please use liveUser instead.');
20715
+ }
20716
+ isInTombstone('user', userId);
20717
+ let data;
20718
+ try {
20719
+ const response = await client.http.get(`/api/v3/users/${encodeURIComponent(userId)}`);
20720
+ data = response.data;
20721
+ }
20722
+ catch (error) {
20723
+ if (checkIfShouldGoesToTombstone(error === null || error === void 0 ? void 0 : error.code)) {
20724
+ pushToTombstone('user', userId);
20725
+ }
20726
+ throw error;
20727
+ }
20600
20728
  const cachedAt = client.cache && Date.now();
20601
20729
  if (client.cache)
20602
20730
  ingestInCache(data, { cachedAt });
20603
20731
  const { users } = data;
20732
+ fireEvent('user.fetched', data);
20604
20733
  return {
20605
20734
  data: users.find(user => user.userId === userId),
20606
20735
  cachedAt,
@@ -20650,6 +20779,7 @@ const getUsers = async (userIds) => {
20650
20779
  const cachedAt = client.cache && Date.now();
20651
20780
  if (client.cache)
20652
20781
  ingestInCache(data, { cachedAt });
20782
+ fireEvent('user.fetched', data);
20653
20783
  return {
20654
20784
  data: data.users,
20655
20785
  cachedAt,
@@ -20732,8 +20862,7 @@ function __rest(s, e) {
20732
20862
  const queryUsers = async (query = {}) => {
20733
20863
  const client = getActiveClient();
20734
20864
  client.log('user/queryUsers', query);
20735
- const { page, displayName, filter = 'all', sortBy = 'displayName' } = query, params = __rest(query, ["page", "displayName", "filter", "sortBy"]);
20736
- // API-FIX: parameters should be querystring.
20865
+ const { page = { limit: 10 }, displayName, filter = 'all', sortBy = 'displayName' } = query, params = __rest(query, ["page", "displayName", "filter", "sortBy"]);
20737
20866
  const { data } = await client.http.get(`/api/v3/users`, {
20738
20867
  params: Object.assign(Object.assign({}, params), { keyword: displayName, filter,
20739
20868
  sortBy, options: {
@@ -20746,9 +20875,14 @@ const queryUsers = async (query = {}) => {
20746
20875
  const cachedAt = client.cache && Date.now();
20747
20876
  if (client.cache) {
20748
20877
  ingestInCache(payload, { cachedAt });
20749
- const cacheKey = ['user', 'query', query];
20878
+ /*
20879
+ * using query as cache key over params because if the keyword, filter, sort
20880
+ * change the API will NOT cache results, when it should
20881
+ */
20882
+ const cacheKey = ['user', 'query', Object.assign(Object.assign({}, query), { options: Object.assign({}, page) })];
20750
20883
  pushToCache(cacheKey, { users: users.map(getResolver('user')), paging });
20751
20884
  }
20885
+ fireEvent('user.fetched', data);
20752
20886
  const prevPage = toPage(paging.previous);
20753
20887
  const nextPage = toPage(paging.next);
20754
20888
  return { data: users, cachedAt, prevPage, nextPage };
@@ -20773,8 +20907,13 @@ queryUsers.locally = (query = {}) => {
20773
20907
  client.log('user/queryUsers.locally', query);
20774
20908
  if (!client.cache)
20775
20909
  return;
20776
- const queryKey = ['user', 'query', query];
20777
- const { data, cachedAt } = (_a = pullFromCache(queryKey)) !== null && _a !== void 0 ? _a : {};
20910
+ const { page = { limit: 10 } } = query !== null && query !== void 0 ? query : {};
20911
+ const cacheKey = [
20912
+ 'user',
20913
+ 'query',
20914
+ Object.assign(Object.assign({}, query), { options: Object.assign({}, page) }),
20915
+ ];
20916
+ const { data, cachedAt } = (_a = pullFromCache(cacheKey)) !== null && _a !== void 0 ? _a : {};
20778
20917
  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 : [];
20779
20918
  const prevPage = toPage(data === null || data === void 0 ? void 0 : data.paging.previous);
20780
20919
  const nextPage = toPage(data === null || data === void 0 ? void 0 : data.paging.next);
@@ -20812,12 +20951,30 @@ const updateUser = async (userId, patch) => {
20812
20951
  if (client.cache)
20813
20952
  ingestInCache(data, { cachedAt });
20814
20953
  const { users } = data;
20954
+ fireEvent('user.updated', data);
20815
20955
  return {
20816
20956
  data: users.find(user => user.userId === userId),
20817
20957
  cachedAt,
20818
20958
  };
20819
20959
  };
20820
20960
 
20961
+ /**
20962
+ * ```js
20963
+ * import { onUserFetched } from '@amityco/ts-sdk'
20964
+ * const dispose = onUserFetched(user => {
20965
+ * // ...
20966
+ * })
20967
+ * ```
20968
+ *
20969
+ * Fired when a {@link Amity.User} has been fetched
20970
+ *
20971
+ * @param callback The function to call when the event was fired
20972
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
20973
+ *
20974
+ * @category User Events
20975
+ */
20976
+ const onUserFetched = (callback) => createUserEventSubscriber('user.fetched', callback);
20977
+
20821
20978
  /**
20822
20979
  * ```js
20823
20980
  * import { onUserUpdated } from '@amityco/ts-sdk'
@@ -20886,6 +21043,68 @@ const onUserUnflagged = (callback) => createUserEventSubscriber('user.unflagged'
20886
21043
  */
20887
21044
  const onUserFlagCleared = (callback) => createUserEventSubscriber('user.flagCleared', callback);
20888
21045
 
21046
+ const COLLECTION_DEFAULT_PAGINATION_LIMIT = 5;
21047
+ const COLLECTION_DEFAULT_CACHING_POLICY = 'cache_then_server';
21048
+ const ENABLE_CACHE_MESSAGE = 'For using Live Collection feature you need to enable Cache!';
21049
+ const LIVE_OBJECT_ENABLE_CACHE_MESSAGE = 'For using Live Object feature you need to enable Cache!';
21050
+ const UNSYNCED_OBJECT_CACHED_AT_MESSAGE = 'Observing unsynced object is not supported by Live Object.';
21051
+ const UNSYNCED_OBJECT_CACHED_AT_VALUE = -5;
21052
+
21053
+ /**
21054
+ * ```js
21055
+ * import { liveUser } from '@amityco/ts-sdk'
21056
+ *
21057
+ * let users = []
21058
+ * const unsub = liveUser(userId, response => merge(users, response.data))
21059
+ * ```
21060
+ *
21061
+ * Observe all mutation on a given {@link Amity.User}
21062
+ *
21063
+ * @param userId the ID of the user to observe
21064
+ * @param callback the function to call when new data are available
21065
+ * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the users
21066
+ *
21067
+ * @category Message Live Object
21068
+ */
21069
+ const liveUser = (userId, callback) => {
21070
+ const { log, cache } = getActiveClient();
21071
+ let user;
21072
+ if (!cache) {
21073
+ console.log(LIVE_OBJECT_ENABLE_CACHE_MESSAGE);
21074
+ }
21075
+ const timestamp = Date.now();
21076
+ log(`liveUser(tmpid: ${timestamp}) > listen`);
21077
+ const disposers = [];
21078
+ const dispatcher = (data) => {
21079
+ var _a;
21080
+ // we check updatedAt to see if the current snapshot is new so
21081
+ // we won't send old data back, only fresh new data
21082
+ 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')
21083
+ return;
21084
+ callback(data);
21085
+ user = data.data;
21086
+ };
21087
+ const realtimeRouter = (user, action) => {
21088
+ dispatcher({ loading: false, data: user, origin: 'event' });
21089
+ };
21090
+ const onFetch = () => {
21091
+ const query = createQuery(getUser, userId, true);
21092
+ runQuery(query, ({ loading, data, error, origin }) => {
21093
+ dispatcher({ loading, data, origin, error });
21094
+ if (error) {
21095
+ // Terminate observe functionality on error
21096
+ disposers.forEach(fn => fn());
21097
+ }
21098
+ });
21099
+ };
21100
+ 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)));
21101
+ onFetch();
21102
+ return () => {
21103
+ log(`liveUser(tmpid: ${timestamp}) > dispose`);
21104
+ disposers.forEach(fn => fn());
21105
+ };
21106
+ };
21107
+
20889
21108
  /**
20890
21109
  * ```js
20891
21110
  * import { observeUser } from '@amityco/ts-sdk'
@@ -20905,6 +21124,7 @@ const observeUser = (userId, callback) => {
20905
21124
  const { log } = getActiveClient();
20906
21125
  const timestamp = Date.now();
20907
21126
  log(`observeUser(tmpid: ${timestamp}) > listen`);
21127
+ console.log('observeUser will be deprecated on 25th November 2022, please use liveUser instead.');
20908
21128
  // wrapper function to make sure
20909
21129
  const router = (result, action) => {
20910
21130
  var _a, _b, _c;
@@ -21314,15 +21534,30 @@ const dispatchReactable = (referenceType, model) => {
21314
21534
  * @category Message API
21315
21535
  * @async
21316
21536
  */
21317
- const getMessage = async (messageId) => {
21537
+ const getMessage = async (messageId, isLive = false) => {
21318
21538
  const client = getActiveClient();
21319
21539
  client.log('message/getMessage', messageId);
21320
- // API-FIX: endpoint should not be /list, parameters should be querystring.
21321
- const { data } = await client.http.get(`/api/v3/messages/${encodeURIComponent(messageId)}`);
21540
+ if (!isLive) {
21541
+ console.log('getMessage will be deprecated on 15th November 2022, please use liveMessage instead.');
21542
+ }
21543
+ isInTombstone('message', messageId);
21544
+ let data;
21545
+ try {
21546
+ // API-FIX: endpoint should not be /list, parameters should be querystring.
21547
+ const response = await client.http.get(`/api/v3/messages/${encodeURIComponent(messageId)}`);
21548
+ data = response.data;
21549
+ }
21550
+ catch (error) {
21551
+ if (checkIfShouldGoesToTombstone(error === null || error === void 0 ? void 0 : error.code)) {
21552
+ pushToTombstone('message', messageId);
21553
+ }
21554
+ throw error;
21555
+ }
21322
21556
  const cachedAt = client.cache && Date.now();
21323
21557
  if (client.cache)
21324
21558
  ingestInCache(data, { cachedAt });
21325
21559
  const { messages } = data;
21560
+ fireEvent('v3.message.didFetch', { messages });
21326
21561
  return {
21327
21562
  data: messages.find(message => message.messageId === messageId),
21328
21563
  cachedAt,
@@ -21379,8 +21614,10 @@ const getMessages = async (messageIds) => {
21379
21614
  const cachedAt = client.cache && Date.now();
21380
21615
  if (client.cache)
21381
21616
  ingestInCache(data, { cachedAt });
21617
+ const { messages } = data;
21618
+ fireEvent('v3.message.didFetch', { messages });
21382
21619
  return {
21383
- data: data.messages,
21620
+ data: messages,
21384
21621
  cachedAt,
21385
21622
  };
21386
21623
  };
@@ -21457,6 +21694,7 @@ const queryMessages = async (query) => {
21457
21694
  ];
21458
21695
  pushToCache(cacheKey, { messages: messages.map(getResolver('message')), paging });
21459
21696
  }
21697
+ fireEvent('v3.message.didFetch', { messages });
21460
21698
  const nextPage = toPage(paging.next);
21461
21699
  const prevPage = toPage(paging.previous);
21462
21700
  return { data: messages, cachedAt, prevPage, nextPage };
@@ -21522,8 +21760,8 @@ const createMessage = async (bundle) => {
21522
21760
  const cachedAt = client.cache && Date.now();
21523
21761
  if (client.cache)
21524
21762
  ingestInCache(data, { cachedAt });
21525
- // no need for event, fired by server
21526
21763
  const { messages } = data;
21764
+ fireEvent('v3.message.didCreate', { messages });
21527
21765
  return {
21528
21766
  data: messages[0],
21529
21767
  cachedAt,
@@ -21565,11 +21803,12 @@ createMessage.optimistically = (bundle) => {
21565
21803
  // as reused to update created and updated time, which should be the same
21566
21804
  const createdTime = new Date().toISOString();
21567
21805
  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);
21568
- pushToCache(['message', 'get', message.messageId], message, { cachedAt: -1 });
21806
+ const cachedAt = UNSYNCED_OBJECT_CACHED_AT_VALUE;
21807
+ pushToCache(['message', 'get', message.messageId], message, { cachedAt });
21569
21808
  fireEvent('v3.message.didCreate', { messages: [message] });
21570
21809
  return {
21810
+ cachedAt,
21571
21811
  data: message,
21572
- cachedAt: -1,
21573
21812
  };
21574
21813
  };
21575
21814
 
@@ -21598,6 +21837,7 @@ const updateMessage = async (messageId, patch) => {
21598
21837
  if (client.cache)
21599
21838
  ingestInCache(data, { cachedAt });
21600
21839
  const { messages } = data;
21840
+ fireEvent('v3.message.didUpdate', { messages });
21601
21841
  return {
21602
21842
  data: messages.find(message => message.messageId === messageId),
21603
21843
  cachedAt,
@@ -21657,7 +21897,7 @@ const deleteMessage = async (messageId) => {
21657
21897
  // API-FIX: This endpoint has not been implemented yet.
21658
21898
  await client.http.delete(`/api/v3/messages/${encodeURIComponent(messageId)}`);
21659
21899
  const deleted = await getMessage(messageId);
21660
- // no need for event, fired by server
21900
+ fireEvent('v3.message.didDelete', { messages: [deleted.data] });
21661
21901
  return deleted.data;
21662
21902
  };
21663
21903
  /**
@@ -21844,6 +22084,7 @@ getPosts.locally = (postIds) => {
21844
22084
  const getPost = async (postId) => {
21845
22085
  const client = getActiveClient();
21846
22086
  client.log('post/getPost', postId);
22087
+ isInTombstone('post', postId);
21847
22088
  let data;
21848
22089
  try {
21849
22090
  // API-FIX: endpoint should not be /list, parameters should be querystring.
@@ -21851,8 +22092,8 @@ const getPost = async (postId) => {
21851
22092
  data = response.data;
21852
22093
  }
21853
22094
  catch (error) {
21854
- if ((error === null || error === void 0 ? void 0 : error.code) === 400400 /* Amity.ServerError.ITEM_NOT_FOUND */) {
21855
- dropFromCache(['post', 'get', postId], true);
22095
+ if (checkIfShouldGoesToTombstone(error === null || error === void 0 ? void 0 : error.code)) {
22096
+ pushToTombstone('post', postId);
21856
22097
  }
21857
22098
  throw error;
21858
22099
  }
@@ -22632,7 +22873,7 @@ const deletePost = async (postId, permanent = false) => {
22632
22873
  fireEvent('post.deleted', { posts: [deleted] });
22633
22874
  if (permanent) {
22634
22875
  setTimeout(() => {
22635
- dropFromCache(['post', 'get', postId], true);
22876
+ pushToTombstone('post', postId);
22636
22877
  }, 0);
22637
22878
  }
22638
22879
  else {
@@ -23028,17 +23269,6 @@ const deleteComment = async (commentId, permanent = false) => {
23028
23269
  return deleted;
23029
23270
  };
23030
23271
 
23031
- /** @hidden */
23032
- const fetchReference = async (referenceType, referenceId) => {
23033
- if (referenceType === 'message')
23034
- return getMessage(referenceId);
23035
- if (referenceType === 'post')
23036
- return getPost(referenceId);
23037
- if (referenceType === 'comment')
23038
- return getComment(referenceId);
23039
- throw new Error(`Incorrect referenceType: ${referenceType}`);
23040
- };
23041
-
23042
23272
  /** @hidden */
23043
23273
  const preparePayloadForCache = (event, payload) => {
23044
23274
  var _a, _b;
@@ -23126,7 +23356,6 @@ const prepareMessagePayloadForCache = (payload) => {
23126
23356
  * @async
23127
23357
  * */
23128
23358
  const addReaction = async (referenceType, referenceId, reactionName) => {
23129
- var _a, _b;
23130
23359
  const client = getActiveClient();
23131
23360
  client.log('reaction/createReaction', {
23132
23361
  referenceId,
@@ -23138,9 +23367,7 @@ const addReaction = async (referenceType, referenceId, reactionName) => {
23138
23367
  referenceType,
23139
23368
  reactionName,
23140
23369
  });
23141
- const model = await fetchReference(referenceType, referenceId);
23142
- dispatchReactable(referenceType, model.data);
23143
- return (_b = (_a = model.data.myReactions) === null || _a === void 0 ? void 0 : _a.includes(reactionName)) !== null && _b !== void 0 ? _b : false;
23370
+ return true;
23144
23371
  };
23145
23372
  /**
23146
23373
  * ```js
@@ -23197,7 +23424,6 @@ addReaction.optimistically = (referenceType, referenceId, reactionName) => {
23197
23424
  * @async
23198
23425
  * */
23199
23426
  const removeReaction = async (referenceType, referenceId, reactionName) => {
23200
- var _a, _b;
23201
23427
  const client = getActiveClient();
23202
23428
  client.log('reaction/removeReaction', {
23203
23429
  referenceId,
@@ -23207,9 +23433,7 @@ const removeReaction = async (referenceType, referenceId, reactionName) => {
23207
23433
  await client.http.delete(`/api/v2/reactions`, {
23208
23434
  data: { referenceId, referenceType, reactionName },
23209
23435
  });
23210
- const model = await fetchReference(referenceType, referenceId);
23211
- dispatchReactable(referenceType, model.data);
23212
- return (_b = !((_a = model.data.myReactions) === null || _a === void 0 ? void 0 : _a.includes(reactionName))) !== null && _b !== void 0 ? _b : false;
23436
+ return true;
23213
23437
  };
23214
23438
  /**
23215
23439
  * ```js
@@ -23886,10 +24110,6 @@ const onReactorRemoved = (referenceType, referenceId, callback) => {
23886
24110
  return createEventSubscriber(client, 'comment.removeReaction', 'comment.removeReaction', filter);
23887
24111
  };
23888
24112
 
23889
- const COLLECTION_DEFAULT_PAGINATION_LIMIT = 5;
23890
- const COLLECTION_DEFAULT_CACHING_POLICY = 'cache_then_server';
23891
- const ENABLE_CACHE_MESSAGE = 'For using Live Collection feature you need to enable Cache!';
23892
-
23893
24113
  /**
23894
24114
  * ```js
23895
24115
  * import { liveReactions } from '@amityco/ts-sdk'
@@ -24166,9 +24386,24 @@ const queryChannels = async (query) => {
24166
24386
  * @category Channel API
24167
24387
  */
24168
24388
  queryChannels.locally = (query) => {
24389
+ var _a, _b;
24169
24390
  const client = getActiveClient();
24170
24391
  client.log('channel/queryChannels.locally', query);
24171
- // TODO
24392
+ const _c = query !== null && query !== void 0 ? query : {}, { page = { limit: 10 } } = _c, params = __rest(_c, ["page"]);
24393
+ const cacheKey = ['channel', 'query', Object.assign(Object.assign({}, params), { options: Object.assign({}, page) })];
24394
+ const { data, cachedAt } = (_a = pullFromCache(cacheKey)) !== null && _a !== void 0 ? _a : {};
24395
+ if (!(data === null || data === void 0 ? void 0 : data.channels.length))
24396
+ return;
24397
+ const channels = data.channels
24398
+ // ck -> cacheKey, abbrevated to avoid confusion from the above cache key
24399
+ .map(ck => pullFromCache(['channel', 'get', ck]))
24400
+ .filter(Boolean)
24401
+ .map(({ data }) => data);
24402
+ const prevPage = toPage(data === null || data === void 0 ? void 0 : data.paging.previous);
24403
+ const nextPage = toPage(data === null || data === void 0 ? void 0 : data.paging.next);
24404
+ return channels.length === ((_b = data === null || data === void 0 ? void 0 : data.channels) === null || _b === void 0 ? void 0 : _b.length)
24405
+ ? { data: channels, cachedAt, prevPage, nextPage }
24406
+ : undefined;
24172
24407
  };
24173
24408
 
24174
24409
  /**
@@ -24251,9 +24486,18 @@ getChannels.locally = (channelIds) => {
24251
24486
  const getChannel = async (channelId) => {
24252
24487
  const client = getActiveClient();
24253
24488
  client.log('channel/getChannel', channelId);
24254
- // API-FIX: endpoint should not be /list, parameters should be querystring.
24255
- const { data: payload } = await client.http.get(`/api/v3/channels/${encodeURIComponent(channelId)}`);
24256
- const data = prepareChannelPayload(payload);
24489
+ isInTombstone('channel', channelId);
24490
+ let data;
24491
+ try {
24492
+ const { data: payload } = await client.http.get(`/api/v3/channels/${encodeURIComponent(channelId)}`);
24493
+ data = prepareChannelPayload(payload);
24494
+ }
24495
+ catch (error) {
24496
+ if (checkIfShouldGoesToTombstone(error === null || error === void 0 ? void 0 : error.code)) {
24497
+ pushToTombstone('channel', channelId);
24498
+ }
24499
+ throw error;
24500
+ }
24257
24501
  const cachedAt = client.cache && Date.now();
24258
24502
  if (client.cache)
24259
24503
  ingestInCache(data, { cachedAt });
@@ -24445,8 +24689,8 @@ const leaveChannel = async (channelId) => {
24445
24689
  const queryChannelMembers = async (query) => {
24446
24690
  const client = getActiveClient();
24447
24691
  client.log('channel/queryChannelMembers', query);
24448
- const _a = query !== null && query !== void 0 ? query : {}, { page, channelId } = _a, params = __rest(_a, ["page", "channelId"]);
24449
- const { data: queryPayload } = await client.http.get(`/api/v4/channels/${encodeURIComponent(channelId)}/users`, {
24692
+ const _a = query !== null && query !== void 0 ? query : {}, { page = { limit: 10 } } = _a, params = __rest(_a, ["page"]);
24693
+ const { data: queryPayload } = await client.http.get(`/api/v4/channels/${encodeURIComponent(params.channelId)}/users`, {
24450
24694
  params: Object.assign(Object.assign({}, params), { options: {
24451
24695
  token: toToken(page, 'skiplimit'),
24452
24696
  } }),
@@ -24457,6 +24701,15 @@ const queryChannelMembers = async (query) => {
24457
24701
  const cachedAt = client.cache && Date.now();
24458
24702
  if (client.cache) {
24459
24703
  ingestInCache(data);
24704
+ const cacheKey = [
24705
+ 'channelUsers',
24706
+ 'query',
24707
+ Object.assign(Object.assign({}, params), { options: Object.assign({}, page) }),
24708
+ ];
24709
+ pushToCache(cacheKey, {
24710
+ channelUsers: channelUsers.map(({ channelId, userId }) => getResolver('channelUsers')({ channelId, userId })),
24711
+ paging,
24712
+ });
24460
24713
  }
24461
24714
  const nextPage = toPage(paging.next);
24462
24715
  const prevPage = toPage(paging.previous);
@@ -24476,9 +24729,30 @@ const queryChannelMembers = async (query) => {
24476
24729
  * @category Post API
24477
24730
  */
24478
24731
  queryChannelMembers.locally = (query) => {
24732
+ var _a, _b;
24479
24733
  const client = getActiveClient();
24480
- client.log('channel/queryChannelMembers', query);
24481
- // TODO
24734
+ client.log('channel/queryChannelMembers.locally', query);
24735
+ if (!client.cache)
24736
+ return;
24737
+ const { page = { limit: 10 } } = query, params = __rest(query, ["page"]);
24738
+ const cacheKey = [
24739
+ 'channelUsers',
24740
+ 'query',
24741
+ Object.assign(Object.assign({}, params), { options: Object.assign({}, page) }),
24742
+ ];
24743
+ const { data, cachedAt } = (_a = pullFromCache(cacheKey)) !== null && _a !== void 0 ? _a : {};
24744
+ if (!(data === null || data === void 0 ? void 0 : data.channelUsers.length))
24745
+ return;
24746
+ const channelUsers = data.channelUsers
24747
+ // ck -> cacheKey, abbrevated to avoid confusion from the above cache key
24748
+ .map(ck => pullFromCache(['channelUsers', 'get', ck]))
24749
+ .filter(Boolean)
24750
+ .map(({ data }) => data);
24751
+ const prevPage = toPage(data === null || data === void 0 ? void 0 : data.paging.previous);
24752
+ const nextPage = toPage(data === null || data === void 0 ? void 0 : data.paging.next);
24753
+ return channelUsers.length === ((_b = data === null || data === void 0 ? void 0 : data.channelUsers) === null || _b === void 0 ? void 0 : _b.length)
24754
+ ? { data: channelUsers, cachedAt, prevPage, nextPage }
24755
+ : undefined;
24482
24756
  };
24483
24757
 
24484
24758
  /**
@@ -24503,7 +24777,10 @@ const addChannelMembers = async (channelId, userIds) => {
24503
24777
  const data = prepareChannelPayload(payload);
24504
24778
  if (client.cache)
24505
24779
  ingestInCache(data);
24506
- fireEvent('v3.channel.didAddUsers', data);
24780
+ /*
24781
+ * should fire event for each user that was added
24782
+ */
24783
+ userIds.forEach(id => fireEvent('v3.channel.didAddUsers', Object.assign(Object.assign({}, data), { channelUsers: data.channelUsers.filter(u => u.userId === id) })));
24507
24784
  const { channelUsers } = data;
24508
24785
  return !!channelUsers.find(channelUser => channelUser.channelId === channelId && channelUser.membership === 'member');
24509
24786
  };
@@ -24826,6 +25103,56 @@ const onChannelMemberRemoved = (callback) => {
24826
25103
  return createEventSubscriber(client, 'onChannelMemberRemoved', 'v3.channel.didRemoveUsers', filter);
24827
25104
  };
24828
25105
 
25106
+ /**
25107
+ * ```js
25108
+ * import { onChannelMemberBanned } from '@amityco/ts-sdk'
25109
+ * const dispose = onChannelMemberBanned((channel, member) => {
25110
+ * // ...
25111
+ * })
25112
+ * ```
25113
+ *
25114
+ * Fired when any {@link Amity.User} has been banned from any {@link Amity.Channel}
25115
+ *
25116
+ * @param callback The function to call when the event was fired
25117
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
25118
+ *
25119
+ * @category Channel Events
25120
+ */
25121
+ const onChannelMemberBanned = (callback) => {
25122
+ const client = getActiveClient();
25123
+ const filter = (payload) => {
25124
+ const data = prepareChannelPayload(payload);
25125
+ ingestInCache(data);
25126
+ callback(data.channels[0], data.channelUsers.find(cu => cu.membership === 'banned'));
25127
+ };
25128
+ return createEventSubscriber(client, 'onChannelMemberBanned', 'v3.channel.didBan', filter);
25129
+ };
25130
+
25131
+ /**
25132
+ * ```js
25133
+ * import { onChannelMemberUnbanned } from '@amityco/ts-sdk'
25134
+ * const dispose = onChannelMemberUnbanned((channel, member) => {
25135
+ * // ...
25136
+ * })
25137
+ * ```
25138
+ *
25139
+ * Fired when any {@link Amity.User} has been unbanned from any {@link Amity.Channel}
25140
+ *
25141
+ * @param callback The function to call when the event was fired
25142
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
25143
+ *
25144
+ * @category Channel Events
25145
+ */
25146
+ const onChannelMemberUnbanned = (callback) => {
25147
+ const client = getActiveClient();
25148
+ const filter = (payload) => {
25149
+ const data = prepareChannelPayload(payload);
25150
+ ingestInCache(data);
25151
+ callback(data.channels[0], data.channelUsers.find(cu => cu.membership === 'none'));
25152
+ };
25153
+ return createEventSubscriber(client, 'onChannelMemberBanned', 'v3.channel.didUnban', filter);
25154
+ };
25155
+
24829
25156
  /**
24830
25157
  * ```js
24831
25158
  * import { observeChannel } from '@amityco/ts-sdk'
@@ -24920,7 +25247,7 @@ const observeChannels = (callback) => {
24920
25247
  * @category Channel Live Collection
24921
25248
  */
24922
25249
  const liveChannels = (params, callback, config) => {
24923
- const { log, cache } = getActiveClient();
25250
+ const { log, cache, userId } = getActiveClient();
24924
25251
  if (!cache) {
24925
25252
  console.log(ENABLE_CACHE_MESSAGE);
24926
25253
  }
@@ -24948,6 +25275,10 @@ const liveChannels = (params, callback, config) => {
24948
25275
  if (params.excludeTags) {
24949
25276
  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); })); });
24950
25277
  }
25278
+ // userId is required to be able to filter channel by current active user
25279
+ if (params.membership && userId) {
25280
+ channels = filterByChannelMembership(channels, params.membership, userId);
25281
+ }
24951
25282
  const sortBy = params.sortBy || 'lastCreated';
24952
25283
  if (sortBy === 'lastCreated' || sortBy === 'firstCreated') {
24953
25284
  channels = channels.sort(sortBy === 'lastCreated' ? sortByLastCreated : sortByFirstCreated);
@@ -25057,7 +25388,20 @@ const liveChannelMembers = (params, callback, config) => {
25057
25388
  .filter(Boolean)
25058
25389
  .map(({ data }) => data)) !== null && _a !== void 0 ? _a : [];
25059
25390
  channelMembers = filterByPropEquality(channelMembers, 'roles', params.roles);
25060
- channelMembers = filterByPropInclusion(channelMembers, 'membership', params.memberships);
25391
+ if (params.memberships) {
25392
+ /*
25393
+ * even though membership includes muted as a possible value
25394
+ * when querying the server.
25395
+ * Muted is specified under seperarte property namely isMuted
25396
+ * Hence why I've seperately checked for it's equality
25397
+ */
25398
+ if (params.memberships.some(m => ['member', 'banned'].includes(m))) {
25399
+ channelMembers = filterByPropInclusion(channelMembers, 'membership', params.memberships);
25400
+ }
25401
+ if (params.memberships.includes('muted')) {
25402
+ channelMembers = filterByPropEquality(channelMembers, 'isMuted', true);
25403
+ }
25404
+ }
25061
25405
  if (params.search) {
25062
25406
  const containsMatcher = new RegExp(params.search);
25063
25407
  channelMembers = channelMembers.filter(m => m.userId.match(containsMatcher));
@@ -25085,7 +25429,7 @@ const liveChannelMembers = (params, callback, config) => {
25085
25429
  if (action === 'onMemberAdded') {
25086
25430
  collection.data = [...new Set([channelMemberCacheId, ...collection.data])];
25087
25431
  }
25088
- else {
25432
+ if (action === 'onMemberRemoved') {
25089
25433
  collection.data = collection.data.filter(m => m !== channelMemberCacheId);
25090
25434
  }
25091
25435
  pushToCache(cacheKey, collection);
@@ -25112,7 +25456,7 @@ const liveChannelMembers = (params, callback, config) => {
25112
25456
  responder(data);
25113
25457
  }, queryOptions(policy));
25114
25458
  };
25115
- disposers.push(onChannelMemberAdded((_, member) => realtimeRouter(member, 'onMemberAdded')), onChannelMemberRemoved((_, member) => realtimeRouter(member, 'onMemberRemoved')));
25459
+ disposers.push(onChannelMemberAdded((_, member) => realtimeRouter(member, 'onMemberAdded')), onChannelMemberRemoved((_, member) => realtimeRouter(member, 'onMemberRemoved')), onChannelMemberBanned((_, member) => realtimeRouter(member, 'onChannelMemberBanned')), onChannelMemberUnbanned((_, member) => realtimeRouter(member, 'onChannelMemberUnbanned')));
25116
25460
  onFetch();
25117
25461
  return () => {
25118
25462
  log(`liveChannelMembers(tmpid: ${timestamp}) > dispose`);
@@ -25199,82 +25543,92 @@ const onMessageDeleted = (callback) => {
25199
25543
 
25200
25544
  /**
25201
25545
  * ```js
25202
- * import { observeMessage } from '@amityco/ts-sdk'
25203
- *
25204
- * let message = {}
25205
- * const unsub = observeMessage(messageId, updated => message = updated)
25546
+ * import { onMessageFetched } from '@amityco/ts-sdk'
25547
+ * const dispose = onMessageFetched(message => {
25548
+ * // ...
25549
+ * })
25206
25550
  * ```
25207
25551
  *
25208
- * Observe all mutations on a {@link Amity.Message} given a single messageId
25552
+ * Fired when an {@link Amity.Message} has been fetched
25209
25553
  *
25210
- * @param client the client to observe the message with
25211
- * @param messageId the ID of the message to observe
25212
- * @param callback the function to call when new data are available
25213
- * @param policy a query policy option for this observer
25214
- * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the message
25554
+ * @param callback The function to call when the event was fired
25555
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
25215
25556
  *
25216
- * @category Message Observer
25557
+ * @category Message Events
25217
25558
  */
25218
- const observeMessage = (messageId, callback, policy = 'cache_then_server') => {
25219
- const { log } = getActiveClient();
25220
- const timestamp = Date.now();
25221
- log(`observeMessage(tmpid: ${timestamp}) > listen`);
25222
- // wrapper function to make sure
25223
- const router = (result, action) => {
25224
- var _a, _b, _c;
25225
- // filter function
25226
- if (((_a = result.data) === null || _a === void 0 ? void 0 : _a.messageId) !== messageId)
25227
- return;
25228
- if (callback instanceof Function)
25229
- return callback(result);
25230
- if (action !== 'onFetch')
25231
- (_b = callback.onEvent) === null || _b === void 0 ? void 0 : _b.call(callback, action, result);
25232
- (_c = callback[action]) === null || _c === void 0 ? void 0 : _c.call(callback, result);
25233
- };
25234
- const disposers = [];
25235
- disposers.push(onMessageUpdated(data => router({ data, loading: false, origin: 'event' }, 'onUpdate')), onMessageDeleted(data => router({ data, loading: false, origin: 'event' }, 'onDelete')));
25236
- if (policy !== 'no_fetch') {
25237
- runQuery(createQuery(getMessage, messageId), result => result.data && router(result, 'onFetch'), queryOptions(policy));
25238
- }
25239
- return () => {
25240
- log(`observeMessages(tmpid: ${timestamp}) > dispose`);
25241
- disposers.forEach(fn => fn());
25559
+ const onMessageFetched = (callback) => {
25560
+ const client = getActiveClient();
25561
+ const filter = (payload) => {
25562
+ ingestInCache(payload);
25563
+ callback(payload.messages[0]);
25242
25564
  };
25565
+ return createEventSubscriber(client, 'message/onMessageFetched', 'v3.message.didFetch', filter);
25243
25566
  };
25244
25567
 
25245
25568
  /**
25246
25569
  * ```js
25247
- * import { observeMessages } from '@amityco/ts-sdk'
25570
+ * import { liveMessage } from '@amityco/ts-sdk'
25248
25571
  *
25249
25572
  * let messages = []
25250
- * const unsub = observeMessages(channelId, message => merge(messages, message))
25573
+ * const unsub = liveMessage(messageId, response => merge(messages, response.data))
25251
25574
  * ```
25252
25575
  *
25253
- * Observe all mutations on a list of {@link Amity.Message} for a given {@link Amity.Channel} object
25576
+ * Observe all mutation on a given {@link Amity.Message}
25254
25577
  *
25255
- * @param channelId the ID of the channel where to observe the messages
25578
+ * @param messageId the ID of the message to observe
25256
25579
  * @param callback the function to call when new data are available
25257
25580
  * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the messages
25258
25581
  *
25259
- * @category Message Observer
25582
+ * @category Message Live Object
25260
25583
  */
25261
- const observeMessages = (channelId, callback) => {
25262
- const { log } = getActiveClient();
25584
+ const liveMessage = (messageId, callback) => {
25585
+ const { log, cache } = getActiveClient();
25586
+ let message;
25587
+ let isUnsyncedObject = false;
25588
+ if (!cache) {
25589
+ console.log(LIVE_OBJECT_ENABLE_CACHE_MESSAGE);
25590
+ }
25263
25591
  const timestamp = Date.now();
25264
- log(`observeMessages(tmpid: ${timestamp}) > listen`);
25592
+ log(`liveMessage(tmpid: ${timestamp}) > listen`);
25265
25593
  const disposers = [];
25266
- const router = (message, action) => {
25267
- var _a, _b, _c;
25268
- if (((_a = message.data) === null || _a === void 0 ? void 0 : _a.channelId) !== channelId)
25594
+ const dispatcher = (data) => {
25595
+ var _a;
25596
+ // we check updatedAt to see if the current snapshot is new so
25597
+ // we won't send old data back, only fresh new data
25598
+ 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')
25269
25599
  return;
25270
- if (callback instanceof Function)
25271
- return callback(message);
25272
- (_b = callback.onEvent) === null || _b === void 0 ? void 0 : _b.call(callback, action, message);
25273
- (_c = callback[action]) === null || _c === void 0 ? void 0 : _c.call(callback, message);
25600
+ callback(data);
25601
+ message = data.data;
25274
25602
  };
25275
- 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')));
25603
+ const realtimeRouter = (message, action) => {
25604
+ dispatcher({ loading: false, data: message, origin: 'event' });
25605
+ };
25606
+ const onFetch = () => {
25607
+ const query = createQuery(getMessage, messageId, true);
25608
+ runQuery(query, ({ loading, data, error, origin, cachedAt }) => {
25609
+ if (cachedAt === UNSYNCED_OBJECT_CACHED_AT_VALUE) {
25610
+ dispatcher({
25611
+ data,
25612
+ origin,
25613
+ loading: false,
25614
+ error: new ASCApiError(UNSYNCED_OBJECT_CACHED_AT_MESSAGE, 800800 /* Amity.ClientError.DISALOOW_UNSYNCED_OBJECT */, "error" /* Amity.ErrorLevel.ERROR */),
25615
+ });
25616
+ isUnsyncedObject = true;
25617
+ disposers.forEach(fn => fn());
25618
+ }
25619
+ else if (!isUnsyncedObject) {
25620
+ dispatcher({ loading, data, origin, error });
25621
+ }
25622
+ if (error) {
25623
+ // Terminate observe functionality on error
25624
+ disposers.forEach(fn => fn());
25625
+ }
25626
+ });
25627
+ };
25628
+ disposers.push(onMessageFetched(message => realtimeRouter(message)), onMessageUpdated(message => realtimeRouter(message)), onMessageDeleted(message => realtimeRouter(message)));
25629
+ onFetch();
25276
25630
  return () => {
25277
- log(`observeMessages(tmpid: ${timestamp}) > dispose`);
25631
+ log(`liveMessage(tmpid: ${timestamp}) > dispose`);
25278
25632
  disposers.forEach(fn => fn());
25279
25633
  };
25280
25634
  };
@@ -25363,7 +25717,7 @@ const liveMessages = (params, callback, config) => {
25363
25717
  responder(data);
25364
25718
  }, queryOptions(policy));
25365
25719
  };
25366
- disposers.push(onMessageCreated(message => realtimeRouter(message, 'onCreate')), onMessageDeleted(message => realtimeRouter(message, 'onDelete')), onMessageUpdated(message => realtimeRouter(message, 'onUpdate')));
25720
+ disposers.push(onMessageFetched(message => realtimeRouter(message, 'onFetch')), onMessageCreated(message => realtimeRouter(message, 'onCreate')), onMessageDeleted(message => realtimeRouter(message, 'onDelete')), onMessageUpdated(message => realtimeRouter(message, 'onUpdate')));
25367
25721
  onFetch();
25368
25722
  return () => {
25369
25723
  log(`liveMessages(tmpid: ${timestamp}) > dispose`);
@@ -25371,6 +25725,89 @@ const liveMessages = (params, callback, config) => {
25371
25725
  };
25372
25726
  };
25373
25727
 
25728
+ /**
25729
+ * ```js
25730
+ * import { observeMessage } from '@amityco/ts-sdk'
25731
+ *
25732
+ * let message = {}
25733
+ * const unsub = observeMessage(messageId, updated => message = updated)
25734
+ * ```
25735
+ *
25736
+ * Observe all mutations on a {@link Amity.Message} given a single messageId
25737
+ *
25738
+ * @param client the client to observe the message with
25739
+ * @param messageId the ID of the message to observe
25740
+ * @param callback the function to call when new data are available
25741
+ * @param policy a query policy option for this observer
25742
+ * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the message
25743
+ *
25744
+ * @category Message Observer
25745
+ */
25746
+ const observeMessage = (messageId, callback, policy = 'cache_then_server') => {
25747
+ const { log } = getActiveClient();
25748
+ const timestamp = Date.now();
25749
+ log(`observeMessage(tmpid: ${timestamp}) > listen`);
25750
+ console.log('observeMessage will be deprecated on 15th November 2022, please use liveMessage instead.');
25751
+ // wrapper function to make sure
25752
+ const router = (result, action) => {
25753
+ var _a, _b, _c;
25754
+ // filter function
25755
+ if (((_a = result.data) === null || _a === void 0 ? void 0 : _a.messageId) !== messageId)
25756
+ return;
25757
+ if (callback instanceof Function)
25758
+ return callback(result);
25759
+ if (action !== 'onFetch')
25760
+ (_b = callback.onEvent) === null || _b === void 0 ? void 0 : _b.call(callback, action, result);
25761
+ (_c = callback[action]) === null || _c === void 0 ? void 0 : _c.call(callback, result);
25762
+ };
25763
+ const disposers = [];
25764
+ disposers.push(onMessageUpdated(data => router({ data, loading: false, origin: 'event' }, 'onUpdate')), onMessageDeleted(data => router({ data, loading: false, origin: 'event' }, 'onDelete')));
25765
+ if (policy !== 'no_fetch') {
25766
+ runQuery(createQuery(getMessage, messageId), result => result.data && router(result, 'onFetch'), queryOptions(policy));
25767
+ }
25768
+ return () => {
25769
+ log(`observeMessages(tmpid: ${timestamp}) > dispose`);
25770
+ disposers.forEach(fn => fn());
25771
+ };
25772
+ };
25773
+
25774
+ /**
25775
+ * ```js
25776
+ * import { observeMessages } from '@amityco/ts-sdk'
25777
+ *
25778
+ * let messages = []
25779
+ * const unsub = observeMessages(channelId, message => merge(messages, message))
25780
+ * ```
25781
+ *
25782
+ * Observe all mutations on a list of {@link Amity.Message} for a given {@link Amity.Channel} object
25783
+ *
25784
+ * @param channelId the ID of the channel where to observe the messages
25785
+ * @param callback the function to call when new data are available
25786
+ * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the messages
25787
+ *
25788
+ * @category Message Observer
25789
+ */
25790
+ const observeMessages = (channelId, callback) => {
25791
+ const { log } = getActiveClient();
25792
+ const timestamp = Date.now();
25793
+ log(`observeMessages(tmpid: ${timestamp}) > listen`);
25794
+ const disposers = [];
25795
+ const router = (message, action) => {
25796
+ var _a, _b, _c;
25797
+ if (((_a = message.data) === null || _a === void 0 ? void 0 : _a.channelId) !== channelId)
25798
+ return;
25799
+ if (callback instanceof Function)
25800
+ return callback(message);
25801
+ (_b = callback.onEvent) === null || _b === void 0 ? void 0 : _b.call(callback, action, message);
25802
+ (_c = callback[action]) === null || _c === void 0 ? void 0 : _c.call(callback, message);
25803
+ };
25804
+ 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')));
25805
+ return () => {
25806
+ log(`observeMessages(tmpid: ${timestamp}) > dispose`);
25807
+ disposers.forEach(fn => fn());
25808
+ };
25809
+ };
25810
+
25374
25811
  const createCommunityEventSubscriber = (event, callback) => {
25375
25812
  const client = getActiveClient();
25376
25813
  const filter = (payload) => {
@@ -27559,6 +27996,8 @@ exports.CommunityPostSettings = CommunityPostSettings;
27559
27996
  exports.ContentFeedType = ContentFeedType;
27560
27997
  exports.DefaultCommunityPostSetting = DefaultCommunityPostSetting;
27561
27998
  exports.FileType = FileType;
27999
+ exports.MessageContentType = MessageContentType;
28000
+ exports.PostContentType = PostContentType;
27562
28001
  exports.VERSION = VERSION$1;
27563
28002
  exports.VideoResolution = VideoResolution;
27564
28003
  exports.VideoSize = VideoSize;
@@ -27605,6 +28044,7 @@ exports.disposeStream = disposeStream;
27605
28044
  exports.dropFromCache = dropFromCache;
27606
28045
  exports.enableCache = enableCache;
27607
28046
  exports.fileUrlWithSize = fileUrlWithSize;
28047
+ exports.filterByChannelMembership = filterByChannelMembership;
27608
28048
  exports.filterByPropEquality = filterByPropEquality;
27609
28049
  exports.filterByPropInclusion = filterByPropInclusion;
27610
28050
  exports.follow = follow;
@@ -27658,9 +28098,11 @@ exports.liveChannels = liveChannels;
27658
28098
  exports.liveComments = liveComments;
27659
28099
  exports.liveFollowers = liveFollowers;
27660
28100
  exports.liveFollowings = liveFollowings;
28101
+ exports.liveMessage = liveMessage;
27661
28102
  exports.liveMessages = liveMessages;
27662
28103
  exports.livePosts = livePosts;
27663
28104
  exports.liveReactions = liveReactions;
28105
+ exports.liveUser = liveUser;
27664
28106
  exports.mergeInCache = mergeInCache;
27665
28107
  exports.observeChannel = observeChannel;
27666
28108
  exports.observeChannels = observeChannels;
@@ -27682,7 +28124,9 @@ exports.onChannelDeleted = onChannelDeleted;
27682
28124
  exports.onChannelJoined = onChannelJoined;
27683
28125
  exports.onChannelLeft = onChannelLeft;
27684
28126
  exports.onChannelMemberAdded = onChannelMemberAdded;
28127
+ exports.onChannelMemberBanned = onChannelMemberBanned;
27685
28128
  exports.onChannelMemberRemoved = onChannelMemberRemoved;
28129
+ exports.onChannelMemberUnbanned = onChannelMemberUnbanned;
27686
28130
  exports.onChannelUpdated = onChannelUpdated;
27687
28131
  exports.onClientBanned = onClientBanned;
27688
28132
  exports.onClientDisconnected = onClientDisconnected;
@@ -27711,6 +28155,7 @@ exports.onFollowerDeleted = onFollowerDeleted;
27711
28155
  exports.onFollowerRequested = onFollowerRequested;
27712
28156
  exports.onMessageCreated = onMessageCreated;
27713
28157
  exports.onMessageDeleted = onMessageDeleted;
28158
+ exports.onMessageFetched = onMessageFetched;
27714
28159
  exports.onMessageUpdated = onMessageUpdated;
27715
28160
  exports.onPollDeleted = onPollDeleted;
27716
28161
  exports.onPollUpdated = onPollUpdated;
@@ -27731,6 +28176,7 @@ exports.onStreamRecorded = onStreamRecorded;
27731
28176
  exports.onStreamStarted = onStreamStarted;
27732
28177
  exports.onStreamStopped = onStreamStopped;
27733
28178
  exports.onUserDeleted = onUserDeleted;
28179
+ exports.onUserFetched = onUserFetched;
27734
28180
  exports.onUserFlagCleared = onUserFlagCleared;
27735
28181
  exports.onUserFlagged = onUserFlagged;
27736
28182
  exports.onUserFollowed = onUserFollowed;