@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
@@ -0,0 +1,30 @@
1
+ import { getActiveClient } from '~/client/api';
2
+ import { createEventSubscriber } from '~/core/events';
3
+
4
+ import { ingestInCache } from '~/cache/api/ingestInCache';
5
+
6
+ /**
7
+ * ```js
8
+ * import { onMessageFetched } from '@amityco/ts-sdk'
9
+ * const dispose = onMessageFetched(message => {
10
+ * // ...
11
+ * })
12
+ * ```
13
+ *
14
+ * Fired when an {@link Amity.Message} has been fetched
15
+ *
16
+ * @param callback The function to call when the event was fired
17
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
18
+ *
19
+ * @category Message Events
20
+ */
21
+ export const onMessageFetched = (callback: Amity.Listener<Amity.Message>): Amity.Unsubscriber => {
22
+ const client = getActiveClient();
23
+
24
+ const filter = (payload: Amity.MessagePayload) => {
25
+ ingestInCache(payload);
26
+ callback(payload.messages[0]);
27
+ };
28
+
29
+ return createEventSubscriber(client, 'message/onMessageFetched', 'v3.message.didFetch', filter);
30
+ };
@@ -1,3 +1,4 @@
1
+ export { liveMessage } from './liveMessage';
2
+ export { liveMessages } from './liveMessages';
1
3
  export { observeMessage } from './observeMessage';
2
4
  export { observeMessages } from './observeMessages';
3
- export { liveMessages } from './liveMessages';
@@ -0,0 +1,100 @@
1
+ import { ASCApiError } from '~/core/errors';
2
+ import { getActiveClient } from '~/client/api';
3
+ import { createQuery, runQuery } from '~/core/query';
4
+ import {
5
+ UNSYNCED_OBJECT_CACHED_AT_VALUE,
6
+ LIVE_OBJECT_ENABLE_CACHE_MESSAGE,
7
+ UNSYNCED_OBJECT_CACHED_AT_MESSAGE,
8
+ } from '~/utils/constants';
9
+
10
+ import { getMessage } from '../api';
11
+ import { onMessageUpdated, onMessageDeleted, onMessageFetched } from '../events';
12
+
13
+ /**
14
+ * ```js
15
+ * import { liveMessage } from '@amityco/ts-sdk'
16
+ *
17
+ * let messages = []
18
+ * const unsub = liveMessage(messageId, response => merge(messages, response.data))
19
+ * ```
20
+ *
21
+ * Observe all mutation on a given {@link Amity.Message}
22
+ *
23
+ * @param messageId the ID of the message to observe
24
+ * @param callback the function to call when new data are available
25
+ * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the messages
26
+ *
27
+ * @category Message Live Object
28
+ */
29
+ export const liveMessage = (
30
+ messageId: Amity.Message['messageId'],
31
+ callback: Amity.LiveObjectCallback<Amity.Message>,
32
+ ): Amity.Unsubscriber => {
33
+ const { log, cache } = getActiveClient();
34
+ let message: Amity.Message;
35
+ let isUnsyncedObject = false;
36
+
37
+ if (!cache) {
38
+ console.log(LIVE_OBJECT_ENABLE_CACHE_MESSAGE);
39
+ }
40
+
41
+ const timestamp = Date.now();
42
+ log(`liveMessage(tmpid: ${timestamp}) > listen`);
43
+
44
+ const disposers: Amity.Unsubscriber[] = [];
45
+
46
+ const dispatcher = (data: Amity.LiveObject<Amity.Message>) => {
47
+ // we check updatedAt to see if the current snapshot is new so
48
+ // we won't send old data back, only fresh new data
49
+ if (message && data.data?.updatedAt === message?.updatedAt && data.origin !== 'server') return;
50
+
51
+ callback(data);
52
+ message = data.data;
53
+ };
54
+
55
+ const realtimeRouter = (message: Amity.Message, action: Amity.MessageActionType) => {
56
+ dispatcher({ loading: false, data: message, origin: 'event' });
57
+ };
58
+
59
+ const onFetch = () => {
60
+ const query = createQuery(getMessage, messageId, true);
61
+
62
+ runQuery(query, ({ loading, data, error, origin, cachedAt }) => {
63
+ if (cachedAt === UNSYNCED_OBJECT_CACHED_AT_VALUE) {
64
+ dispatcher({
65
+ data,
66
+ origin,
67
+ loading: false,
68
+ error: new ASCApiError(
69
+ UNSYNCED_OBJECT_CACHED_AT_MESSAGE,
70
+ Amity.ClientError.DISALOOW_UNSYNCED_OBJECT,
71
+ Amity.ErrorLevel.ERROR,
72
+ ),
73
+ });
74
+
75
+ isUnsyncedObject = true;
76
+ disposers.forEach(fn => fn());
77
+ } else if (!isUnsyncedObject) {
78
+ dispatcher({ loading, data, origin, error });
79
+ }
80
+
81
+ if (error) {
82
+ // Terminate observe functionality on error
83
+ disposers.forEach(fn => fn());
84
+ }
85
+ });
86
+ };
87
+
88
+ disposers.push(
89
+ onMessageFetched(message => realtimeRouter(message, 'onFetch')),
90
+ onMessageUpdated(message => realtimeRouter(message, 'onUpdate')),
91
+ onMessageDeleted(message => realtimeRouter(message, 'onDelete')),
92
+ );
93
+
94
+ onFetch();
95
+
96
+ return () => {
97
+ log(`liveMessage(tmpid: ${timestamp}) > dispose`);
98
+ disposers.forEach(fn => fn());
99
+ };
100
+ };
@@ -10,7 +10,7 @@ import {
10
10
  ENABLE_CACHE_MESSAGE,
11
11
  } from '~/utils/constants';
12
12
 
13
- import { onMessageCreated, onMessageDeleted, onMessageUpdated } from '../events';
13
+ import { onMessageCreated, onMessageDeleted, onMessageUpdated, onMessageFetched } from '../events';
14
14
  import { queryMessages } from '../api';
15
15
 
16
16
  /**
@@ -126,6 +126,8 @@ export const liveMessages = (
126
126
  };
127
127
 
128
128
  disposers.push(
129
+ onMessageFetched(message => realtimeRouter(message, 'onFetch')),
130
+
129
131
  onMessageCreated(message => realtimeRouter(message, 'onCreate')),
130
132
  onMessageDeleted(message => realtimeRouter(message, 'onDelete')),
131
133
 
@@ -34,6 +34,10 @@ export const observeMessage = <Events extends ['onFetch', 'onUpdate', 'onDelete'
34
34
  const timestamp = Date.now();
35
35
  log(`observeMessage(tmpid: ${timestamp}) > listen`);
36
36
 
37
+ console.log(
38
+ 'observeMessage will be deprecated on 15th November 2022, please use liveMessage instead.',
39
+ );
40
+
37
41
  // wrapper function to make sure
38
42
  const router = (result: Amity.Snapshot<Amity.Message>, action: Events[number]) => {
39
43
  // filter function
@@ -0,0 +1,248 @@
1
+ import { ASCApiError } from '~/core/errors';
2
+ import { disableCache, enableCache } from '~/cache/api';
3
+ import {
4
+ LIVE_OBJECT_ENABLE_CACHE_MESSAGE,
5
+ UNSYNCED_OBJECT_CACHED_AT_MESSAGE,
6
+ } from '~/utils/constants';
7
+ import { liveMessage } from '../../observers/liveMessage';
8
+ import { createMessage } from '../../api';
9
+
10
+ import {
11
+ pause,
12
+ client,
13
+ connectClient,
14
+ disconnectClient,
15
+ messageQueryResponse,
16
+ message11 as defaultMessage,
17
+ } from '~/utils/tests';
18
+
19
+ const getSnapshot = () => {
20
+ return {
21
+ loading: true,
22
+ error: undefined as any,
23
+ data: undefined as undefined | Amity.Message,
24
+ };
25
+ };
26
+
27
+ describe('liveMessage', () => {
28
+ beforeAll(async () => {
29
+ await connectClient();
30
+ });
31
+
32
+ afterAll(async () => {
33
+ await disconnectClient();
34
+ });
35
+
36
+ beforeEach(async () => {
37
+ enableCache();
38
+ });
39
+
40
+ afterEach(disableCache);
41
+
42
+ test('it should show message if cache not enabled', () => {
43
+ /*
44
+ * NOTE: at the time of writing this test cache is diabled by default, but
45
+ * there is a proposal to enable cache by default. So I'm disabling cache
46
+ * here to ensure cache is disabled
47
+ */
48
+ disableCache();
49
+
50
+ const callback = jest.fn();
51
+ jest.spyOn(global.console, 'log');
52
+ client.http.get = jest.fn();
53
+
54
+ // call liveMessage check if mocked console get's called with the correct
55
+ // message
56
+ client.use();
57
+ liveMessage(defaultMessage.messageId, callback);
58
+
59
+ expect(console.log).toBeCalledWith(LIVE_OBJECT_ENABLE_CACHE_MESSAGE);
60
+ });
61
+
62
+ test('it should return message live object', async () => {
63
+ const snapshot = getSnapshot();
64
+
65
+ // mock response of queryMessages and validate
66
+ const callback = jest.fn();
67
+ client.http.get = jest.fn().mockResolvedValue(messageQueryResponse);
68
+
69
+ liveMessage(defaultMessage.messageId, callback);
70
+
71
+ expect(callback).toHaveBeenCalledTimes(1);
72
+
73
+ // check if cache data returned (should be empty)
74
+ expect(callback).toHaveBeenCalledWith(expect.objectContaining(snapshot));
75
+
76
+ await pause();
77
+
78
+ expect(callback).toHaveBeenCalledTimes(2);
79
+
80
+ snapshot.loading = false;
81
+ snapshot.data = defaultMessage;
82
+
83
+ expect(callback).toHaveBeenCalledWith(expect.objectContaining(snapshot));
84
+ });
85
+
86
+ test('it should return error on api failure', async () => {
87
+ const snapshot = getSnapshot();
88
+ const error = new ASCApiError(
89
+ 'server error!',
90
+ Amity.ServerError.ITEM_NOT_FOUND,
91
+ Amity.ErrorLevel.ERROR,
92
+ );
93
+
94
+ // mock response of queryMessages and validate
95
+ const callback = jest.fn();
96
+ client.http.get = jest.fn().mockRejectedValue(error);
97
+
98
+ liveMessage(defaultMessage.messageId, callback);
99
+
100
+ expect(callback).toHaveBeenCalledTimes(1);
101
+
102
+ // check if cache data returned (should be empty)
103
+ expect(callback).toHaveBeenCalledWith(expect.objectContaining(snapshot));
104
+
105
+ await pause();
106
+
107
+ expect(callback).toHaveBeenCalledTimes(2);
108
+
109
+ snapshot.error = error;
110
+ snapshot.loading = false;
111
+ expect(callback).toHaveBeenLastCalledWith(expect.objectContaining(snapshot));
112
+ });
113
+
114
+ test('it should terminate updates on api failure', async () => {
115
+ const snapshot = getSnapshot();
116
+ const error = new ASCApiError(
117
+ 'server error!',
118
+ Amity.ServerError.ITEM_NOT_FOUND,
119
+ Amity.ErrorLevel.ERROR,
120
+ );
121
+
122
+ // mock response of queryMessages and validate
123
+ const callback = jest.fn();
124
+ client.http.get = jest.fn().mockRejectedValue(error);
125
+
126
+ liveMessage(defaultMessage.messageId, callback);
127
+
128
+ expect(callback).toHaveBeenCalledTimes(1);
129
+
130
+ // check if cache data returned (should be empty)
131
+ expect(callback).toHaveBeenCalledWith(expect.objectContaining(snapshot));
132
+
133
+ await pause();
134
+
135
+ expect(callback).toHaveBeenCalledTimes(2);
136
+
137
+ snapshot.error = error;
138
+ snapshot.loading = false;
139
+ expect(callback).toHaveBeenLastCalledWith(expect.objectContaining(snapshot));
140
+
141
+ const updatedAt = new Date(defaultMessage?.updatedAt ?? new Date());
142
+ updatedAt.setDate(updatedAt.getDate() + 7);
143
+
144
+ const updatedMessage = {
145
+ ...defaultMessage,
146
+ updatedAt: updatedAt.toISOString(),
147
+ };
148
+ client.emitter.emit('v3.message.didUpdate', { messages: [updatedMessage] });
149
+
150
+ delete snapshot.error;
151
+ snapshot.loading = false;
152
+ snapshot.data = updatedMessage;
153
+ expect(callback).not.toHaveBeenNthCalledWith(3, expect.objectContaining(snapshot));
154
+ });
155
+
156
+ test('it should not return updates on obtain once!', async () => {
157
+ const snapshot = getSnapshot();
158
+
159
+ // mock response of queryMessages and validate
160
+ const callback = jest.fn();
161
+ client.http.get = jest.fn().mockResolvedValue(messageQueryResponse);
162
+
163
+ const unsub = liveMessage(defaultMessage.messageId, callback);
164
+
165
+ expect(callback).toHaveBeenCalledTimes(1);
166
+
167
+ // check if cache data returned (should be empty)
168
+ expect(callback).toHaveBeenCalledWith(expect.objectContaining(snapshot));
169
+
170
+ await pause();
171
+
172
+ expect(callback).toHaveBeenCalledTimes(2);
173
+
174
+ unsub();
175
+
176
+ const updatedAt = new Date(defaultMessage?.updatedAt ?? new Date());
177
+ updatedAt.setDate(updatedAt.getDate() + 7);
178
+
179
+ const updatedMessage = {
180
+ ...defaultMessage,
181
+ updatedAt: updatedAt.toISOString(),
182
+ };
183
+ client.emitter.emit('v3.message.didUpdate', { messages: [updatedMessage] });
184
+
185
+ delete snapshot.error;
186
+ snapshot.loading = false;
187
+ snapshot.data = updatedMessage;
188
+ expect(callback).not.toHaveBeenNthCalledWith(3, expect.objectContaining(snapshot));
189
+ });
190
+
191
+ const events: [string, keyof Amity.Events][] = [
192
+ ['it should return updated message on onFetch', 'v3.message.didFetch'],
193
+ ['it should return updated message on onUpdate', 'v3.message.didUpdate'],
194
+ ['it should return updated message on onDelete', 'v3.message.didDelete'],
195
+ ];
196
+
197
+ test.each(events)('%s', async (test, event) => {
198
+ const snapshot = getSnapshot();
199
+
200
+ // mock response of queryMessages and validate
201
+ const callback = jest.fn();
202
+ client.http.get = jest.fn().mockResolvedValue(messageQueryResponse);
203
+
204
+ liveMessage(defaultMessage.messageId, callback);
205
+
206
+ await pause();
207
+
208
+ expect(callback).toHaveBeenCalledTimes(2);
209
+
210
+ const updatedAt = new Date(defaultMessage?.updatedAt ?? new Date());
211
+ updatedAt.setDate(updatedAt.getDate() + 7);
212
+
213
+ const updatedMessage = {
214
+ ...defaultMessage,
215
+ updatedAt: updatedAt.toISOString(),
216
+ };
217
+ client.emitter.emit(event, { messages: [updatedMessage] });
218
+
219
+ delete snapshot.error;
220
+ snapshot.loading = false;
221
+ snapshot.data = updatedMessage;
222
+ expect(callback).toHaveBeenNthCalledWith(3, expect.objectContaining(snapshot));
223
+ });
224
+
225
+ test('it should terminate updates for unsynced object', async () => {
226
+ const callback = jest.fn();
227
+ client.http.get = jest.fn();
228
+ const snapshot = getSnapshot();
229
+ const data = { channelId: 'test', data: { text: 'text' } };
230
+
231
+ const message = createMessage.optimistically(data);
232
+
233
+ const error = new ASCApiError(
234
+ UNSYNCED_OBJECT_CACHED_AT_MESSAGE,
235
+ Amity.ClientError.DISALOOW_UNSYNCED_OBJECT,
236
+ Amity.ErrorLevel.ERROR,
237
+ );
238
+
239
+ liveMessage(message!.data.messageId, callback);
240
+
241
+ expect(callback).toHaveBeenCalledTimes(1);
242
+
243
+ snapshot.error = error;
244
+ snapshot.loading = false;
245
+ snapshot.data = message!.data;
246
+ expect(callback).toHaveBeenLastCalledWith(expect.objectContaining(snapshot));
247
+ });
248
+ });
@@ -2,15 +2,15 @@ import { ASCApiError } from '~/core/errors';
2
2
  import { disableCache, enableCache } from '~/cache/api';
3
3
  import { ENABLE_CACHE_MESSAGE } from '~/utils/constants';
4
4
 
5
- import { liveMessages } from '../liveMessages';
6
- import { queryMessages } from '~/message/api';
5
+ import { queryMessages } from '../../api';
6
+ import { liveMessages } from '../../observers';
7
7
 
8
8
  import {
9
9
  client,
10
10
  connectClient,
11
11
  disconnectClient,
12
12
  messages,
13
- message as defaultMessage,
13
+ message11 as defaultMessage,
14
14
  } from '~/utils/tests';
15
15
 
16
16
  describe('liveMessages', () => {
@@ -222,6 +222,32 @@ describe('liveMessages', () => {
222
222
  );
223
223
  });
224
224
 
225
+ test('it should add new message to collection onFetch', async () => {
226
+ const callback = jest.fn();
227
+ client.http.get = jest.fn().mockResolvedValue({
228
+ data: {
229
+ messages: returnValue,
230
+ paging: {},
231
+ },
232
+ });
233
+
234
+ liveMessages({ channelId }, callback);
235
+
236
+ await expect(queryMessages({ channelId })).resolves.toBeTruthy();
237
+ expect(callback).toHaveBeenCalledTimes(2);
238
+
239
+ client.emitter.emit('v3.message.didFetch', { messages: [message] });
240
+
241
+ expect(callback).toHaveBeenNthCalledWith(
242
+ 3,
243
+ expect.objectContaining({
244
+ data: [message],
245
+ error: undefined,
246
+ loading: false,
247
+ }),
248
+ );
249
+ });
250
+
225
251
  test('it should add new message to collection onCreate', async () => {
226
252
  const newMessage = {
227
253
  ...message,
@@ -1,8 +1,9 @@
1
1
  import { getActiveClient } from '~/client/api';
2
2
 
3
- import { getCommunity } from '~/community/api';
4
- import { dropFromCache, upsertInCache } from '~/cache/api';
5
3
  import { fireEvent } from '~/core/events';
4
+ import { upsertInCache } from '~/cache/api';
5
+ import { getCommunity } from '~/community/api';
6
+ import { pushToTombstone } from '~/cache/api/pushToTombstone';
6
7
 
7
8
  import { getPost } from './getPost';
8
9
 
@@ -53,7 +54,7 @@ export const deletePost = async (
53
54
 
54
55
  if (permanent) {
55
56
  setTimeout(() => {
56
- dropFromCache(['post', 'get', postId], true);
57
+ pushToTombstone('post', postId);
57
58
  }, 0);
58
59
  } else {
59
60
  upsertInCache(['post', 'get', postId], { isDeleted: true });
@@ -1,7 +1,10 @@
1
1
  import { getActiveClient } from '~/client/api';
2
2
 
3
- import { pullFromCache, dropFromCache } from '~/cache/api';
3
+ import { pullFromCache } from '~/cache/api';
4
4
  import { ingestInCache } from '~/cache/api/ingestInCache';
5
+ import { isInTombstone } from '~/cache/api/isInTombstone';
6
+ import { checkIfShouldGoesToTombstone } from '~/cache/utils';
7
+ import { pushToTombstone } from '~/cache/api/pushToTombstone';
5
8
 
6
9
  /**
7
10
  * ```js
@@ -21,6 +24,8 @@ export const getPost = async (postId: Amity.Post['postId']): Promise<Amity.Cache
21
24
  const client = getActiveClient();
22
25
  client.log('post/getPost', postId);
23
26
 
27
+ isInTombstone('post', postId);
28
+
24
29
  let data: Amity.PostPayload;
25
30
 
26
31
  try {
@@ -28,11 +33,10 @@ export const getPost = async (postId: Amity.Post['postId']): Promise<Amity.Cache
28
33
  const response = await client.http.get<Amity.PostPayload>(
29
34
  `/api/v3/posts/${encodeURIComponent(postId)}`,
30
35
  );
31
-
32
36
  data = response.data;
33
37
  } catch (error) {
34
- if ((error as Amity.ErrorResponse)?.code === Amity.ServerError.ITEM_NOT_FOUND) {
35
- dropFromCache(['post', 'get', postId], true);
38
+ if (checkIfShouldGoesToTombstone((error as Amity.ErrorResponse)?.code)) {
39
+ pushToTombstone('post', postId);
36
40
  }
37
41
 
38
42
  throw error;
@@ -22,7 +22,12 @@ import { fireEvent } from '~/core/events';
22
22
  */
23
23
  export const updatePost = async (
24
24
  postId: Amity.Post['postId'],
25
- patch: Patch<Amity.Post, 'data' | 'metadata' | 'mentionees' | 'tags'>,
25
+ patch: Patch<Amity.Post, 'data' | 'metadata' | 'mentionees' | 'tags'> & {
26
+ attachments?: {
27
+ type: 'image' | 'file' | 'video';
28
+ fileId: Amity.File['fileId'];
29
+ }[];
30
+ },
26
31
  ): Promise<Amity.Cached<Amity.Post>> => {
27
32
  const client = getActiveClient();
28
33
  client.log('user/updatePost', patch);
@@ -1,23 +1,122 @@
1
- import { queryPosts } from '~/post/api';
2
- import { client, posts } from '~/utils/tests';
3
-
4
- describe('post/api', () => {
5
- test('should return a post query', async () => {
6
- const returnValue = [{ postId: posts.page1[0] }];
7
-
8
- client.http.get = jest.fn().mockResolvedValue({
9
- data: {
10
- posts: returnValue,
11
- paging: {},
12
- },
13
- });
1
+ import { getResolver } from '~/core/model';
2
+
3
+ import { disableCache, enableCache, pushToCache } from '~/cache/api';
4
+ import { ingestInCache } from '~/cache/api/ingestInCache';
5
+
6
+ import { client, postQueryResponse } from '~/utils/tests';
7
+
8
+ import { queryPosts } from '../../api/queryPosts';
9
+
10
+ const params = { targetType: 'user', targetId: 'test' } as Amity.QueryPosts;
11
+
12
+ describe('queryPosts', () => {
13
+ test('it should return posts', async () => {
14
+ const { posts: expected } = postQueryResponse.data;
15
+ client.http.get = jest.fn().mockResolvedValue(postQueryResponse);
16
+
17
+ const { data } = await queryPosts(params);
18
+
19
+ expect(data).toEqual(expected);
20
+ });
21
+
22
+ test('it should throw error', async () => {
23
+ client.http.get = jest.fn().mockRejectedValueOnce(new Error('error'));
24
+
25
+ await expect(queryPosts(params)).rejects.toThrow('error');
26
+ });
27
+
28
+ test('it should update cache upon fetching data from server', async () => {
29
+ enableCache();
30
+
31
+ client.http.get = jest.fn().mockResolvedValue(postQueryResponse);
32
+
33
+ const { data: expected } = await queryPosts(params);
34
+ const { data: received } = queryPosts.locally(params)!;
35
+
36
+ expect(received).toBeDefined();
37
+ expect(received).toEqual(expected);
38
+
39
+ disableCache();
40
+ });
41
+ });
42
+
43
+ describe('queryPosts.locally', () => {
44
+ const cacheKey = ['post', 'query', { ...params, options: { limit: 10 } } as Amity.Serializable];
45
+
46
+ test('it should fetch query locally if present in cache', () => {
47
+ enableCache();
48
+
49
+ const { data } = postQueryResponse;
50
+ const { posts: expected, paging } = data;
51
+
52
+ ingestInCache(data as Amity.PostPayload);
53
+
54
+ pushToCache(cacheKey, { posts: expected.map(getResolver('post')), paging });
14
55
 
15
- const data = await queryPosts({
16
- targetId: posts.targetId,
17
- targetType: posts.targetType,
56
+ const { data: received } = queryPosts.locally(params)!;
57
+
58
+ expect(received).toBeDefined();
59
+ expect(received).toEqual(expected);
60
+
61
+ disableCache();
62
+ });
63
+
64
+ test('it should return undefined if only partial data in cache', () => {
65
+ enableCache();
66
+
67
+ const { data } = postQueryResponse;
68
+ const { posts, paging } = data;
69
+
70
+ // ingest incomplete data
71
+ ingestInCache({
72
+ ...data,
73
+ posts: [data.posts[0]],
74
+ } as Amity.PostPayload);
75
+
76
+ pushToCache(cacheKey, {
77
+ posts: posts.map(getResolver('post')),
78
+ paging,
18
79
  });
19
80
 
20
- expect(data).toBeDefined();
21
- expect(data?.data).toEqual(returnValue);
81
+ const received = queryPosts.locally(params)!;
82
+
83
+ expect(received).toBeUndefined();
84
+
85
+ disableCache();
86
+ });
87
+
88
+ // added because of comment above cache key in queryPosts
89
+ test('it should return undefined if params do not match', () => {
90
+ enableCache();
91
+
92
+ const { data } = postQueryResponse;
93
+ const { posts: expected, paging } = data;
94
+
95
+ ingestInCache(data as Amity.PostPayload);
96
+
97
+ pushToCache(cacheKey, { posts: expected.map(getResolver('post')), paging });
98
+
99
+ const { data: received } = queryPosts.locally(params)!;
100
+ const received2 = queryPosts.locally({ ...params, tags: ['tag1'] })!;
101
+
102
+ expect(received).toBeDefined();
103
+ expect(received).toEqual(expected);
104
+ expect(received2).toBeUndefined();
105
+
106
+ disableCache();
107
+ });
108
+
109
+ test('it should return undefined if data not in cache', () => {
110
+ enableCache();
111
+
112
+ const received = queryPosts.locally(params);
113
+
114
+ expect(received).toBeUndefined();
115
+
116
+ disableCache();
117
+ });
118
+
119
+ test('it should return undefined if cache disabled', () => {
120
+ expect(queryPosts.locally(params)).toBeUndefined();
22
121
  });
23
122
  });
@@ -38,10 +38,7 @@ export const addReaction = async (
38
38
  reactionName,
39
39
  });
40
40
 
41
- const model = await fetchReference(referenceType, referenceId);
42
- dispatchReactable(referenceType, model.data);
43
-
44
- return model.data.myReactions?.includes(reactionName) ?? false;
41
+ return true;
45
42
  };
46
43
 
47
44
  /**