@amityco/ts-sdk 0.0.1-beta.27 → 0.0.1-beta.28

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 (185) hide show
  1. package/.eslintrc.json +1 -0
  2. package/dist/@types/core/payload.d.ts +12 -10
  3. package/dist/@types/core/payload.d.ts.map +1 -1
  4. package/dist/@types/domains/channel.d.ts +5 -1
  5. package/dist/@types/domains/channel.d.ts.map +1 -1
  6. package/dist/@types/domains/client.d.ts +13 -0
  7. package/dist/@types/domains/client.d.ts.map +1 -1
  8. package/dist/@types/domains/content.d.ts +26 -3
  9. package/dist/@types/domains/content.d.ts.map +1 -1
  10. package/dist/@types/domains/file.d.ts +36 -3
  11. package/dist/@types/domains/file.d.ts.map +1 -1
  12. package/dist/@types/domains/follow.d.ts +11 -0
  13. package/dist/@types/domains/follow.d.ts.map +1 -1
  14. package/dist/@types/domains/group.d.ts +1 -0
  15. package/dist/@types/domains/group.d.ts.map +1 -1
  16. package/dist/@types/domains/message.d.ts +13 -0
  17. package/dist/@types/domains/message.d.ts.map +1 -1
  18. package/dist/@types/global.d.ts +1 -0
  19. package/dist/@types/global.d.ts.map +1 -1
  20. package/dist/@types/index.d.ts +2 -2
  21. package/dist/@types/index.d.ts.map +1 -1
  22. package/dist/channel/api/addChannelMembers.d.ts.map +1 -1
  23. package/dist/channel/api/addChannelMembersRole.d.ts.map +1 -1
  24. package/dist/channel/api/banChannelMembers.d.ts.map +1 -1
  25. package/dist/channel/api/createChannel.d.ts.map +1 -1
  26. package/dist/channel/api/getChannel.d.ts.map +1 -1
  27. package/dist/channel/api/getChannels.d.ts.map +1 -1
  28. package/dist/channel/api/joinChannel.d.ts.map +1 -1
  29. package/dist/channel/api/leaveChannel.d.ts.map +1 -1
  30. package/dist/channel/api/queryChannelMembers.d.ts.map +1 -1
  31. package/dist/channel/api/queryChannels.d.ts.map +1 -1
  32. package/dist/channel/api/removeChannelMembers.d.ts.map +1 -1
  33. package/dist/channel/api/removeChannelMembersRole.d.ts.map +1 -1
  34. package/dist/channel/api/unbanChannelMembers.d.ts.map +1 -1
  35. package/dist/channel/api/updateChannel.d.ts.map +1 -1
  36. package/dist/channel/events/index.d.ts +1 -0
  37. package/dist/channel/events/index.d.ts.map +1 -1
  38. package/dist/channel/events/onChannelCreated.d.ts.map +1 -1
  39. package/dist/channel/events/onChannelDeleted.d.ts.map +1 -1
  40. package/dist/channel/events/onChannelJoined.d.ts.map +1 -1
  41. package/dist/channel/events/onChannelLeft.d.ts.map +1 -1
  42. package/dist/channel/events/onChannelMemberAdded.d.ts.map +1 -1
  43. package/dist/channel/events/onChannelMemberRemoved.d.ts.map +1 -1
  44. package/dist/channel/events/onChannelUpdated.d.ts.map +1 -1
  45. package/dist/channel/observers/index.d.ts +1 -0
  46. package/dist/channel/observers/index.d.ts.map +1 -1
  47. package/dist/channel/observers/observeChannel.d.ts +1 -1
  48. package/dist/channel/observers/observeChannel.d.ts.map +1 -1
  49. package/dist/channel/observers/observeChannels.d.ts +17 -0
  50. package/dist/channel/observers/observeChannels.d.ts.map +1 -0
  51. package/dist/channel/tests/utils/prepareChannelPayload.test.d.ts +2 -0
  52. package/dist/channel/tests/utils/prepareChannelPayload.test.d.ts.map +1 -0
  53. package/dist/channel/utils/index.d.ts +2 -0
  54. package/dist/channel/utils/index.d.ts.map +1 -0
  55. package/dist/channel/utils/prepareChannelPayload.d.ts +3 -0
  56. package/dist/channel/utils/prepareChannelPayload.d.ts.map +1 -0
  57. package/dist/client/api/connectClient.d.ts +1 -8
  58. package/dist/client/api/connectClient.d.ts.map +1 -1
  59. package/dist/client/api/createClient.d.ts +8 -1
  60. package/dist/client/api/createClient.d.ts.map +1 -1
  61. package/dist/client/api/getFeedSettings.d.ts +16 -0
  62. package/dist/client/api/getFeedSettings.d.ts.map +1 -0
  63. package/dist/client/api/index.d.ts +1 -0
  64. package/dist/client/api/index.d.ts.map +1 -1
  65. package/dist/core/events.d.ts +3 -3
  66. package/dist/core/events.d.ts.map +1 -1
  67. package/dist/core/query/query.d.ts +1 -1
  68. package/dist/core/tests/query/query/runQuery.test.d.ts +2 -0
  69. package/dist/core/tests/query/query/runQuery.test.d.ts.map +1 -0
  70. package/dist/file/api/createVideo.d.ts +2 -1
  71. package/dist/file/api/createVideo.d.ts.map +1 -1
  72. package/dist/follow/api/queryFollowers.d.ts +1 -5
  73. package/dist/follow/api/queryFollowers.d.ts.map +1 -1
  74. package/dist/follow/api/queryFollowings.d.ts +1 -5
  75. package/dist/follow/api/queryFollowings.d.ts.map +1 -1
  76. package/dist/follow/observers/index.d.ts +2 -0
  77. package/dist/follow/observers/index.d.ts.map +1 -1
  78. package/dist/follow/observers/liveFollowers.d.ts +20 -0
  79. package/dist/follow/observers/liveFollowers.d.ts.map +1 -0
  80. package/dist/follow/observers/liveFollowings.d.ts +20 -0
  81. package/dist/follow/observers/liveFollowings.d.ts.map +1 -0
  82. package/dist/index.cjs.js +553 -111
  83. package/dist/index.d.ts +1 -1
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.esm.js +543 -112
  86. package/dist/index.umd.js +4 -4
  87. package/dist/message/api/queryMessages.d.ts +1 -10
  88. package/dist/message/api/queryMessages.d.ts.map +1 -1
  89. package/dist/message/observers/index.d.ts +1 -0
  90. package/dist/message/observers/index.d.ts.map +1 -1
  91. package/dist/message/observers/liveMessages.d.ts +20 -0
  92. package/dist/message/observers/liveMessages.d.ts.map +1 -0
  93. package/dist/message/observers/observeMessages.d.ts +0 -1
  94. package/dist/message/observers/observeMessages.d.ts.map +1 -1
  95. package/dist/message/observers/tests/liveMessages.test.d.ts +2 -0
  96. package/dist/message/observers/tests/liveMessages.test.d.ts.map +1 -0
  97. package/dist/post/observers/livePosts.d.ts.map +1 -1
  98. package/dist/utils/constants.d.ts +1 -0
  99. package/dist/utils/constants.d.ts.map +1 -1
  100. package/dist/utils/tests/client.d.ts +5 -2
  101. package/dist/utils/tests/client.d.ts.map +1 -1
  102. package/dist/utils/tests/dummy/channel.d.ts +10 -0
  103. package/dist/utils/tests/dummy/channel.d.ts.map +1 -0
  104. package/dist/utils/tests/dummy/index.d.ts +10 -0
  105. package/dist/utils/tests/dummy/index.d.ts.map +1 -0
  106. package/dist/utils/tests/dummy/message.d.ts +8 -0
  107. package/dist/utils/tests/dummy/message.d.ts.map +1 -0
  108. package/dist/utils/tests/{dummy.d.ts → dummy/post.d.ts} +3 -2
  109. package/dist/utils/tests/dummy/post.d.ts.map +1 -0
  110. package/dist/utils/tests/dummy/user.d.ts +2 -0
  111. package/dist/utils/tests/dummy/user.d.ts.map +1 -0
  112. package/dist/utils/tests/index.d.ts +2 -2
  113. package/dist/utils/tests/index.d.ts.map +1 -1
  114. package/package.json +1 -1
  115. package/src/@types/core/payload.ts +16 -10
  116. package/src/@types/domains/channel.ts +16 -1
  117. package/src/@types/domains/client.ts +17 -0
  118. package/src/@types/domains/content.ts +34 -3
  119. package/src/@types/domains/file.ts +42 -3
  120. package/src/@types/domains/follow.ts +28 -0
  121. package/src/@types/domains/group.ts +1 -0
  122. package/src/@types/domains/message.ts +20 -0
  123. package/src/@types/global.ts +2 -0
  124. package/src/@types/index.ts +3 -2
  125. package/src/channel/api/addChannelMembers.ts +6 -5
  126. package/src/channel/api/addChannelMembersRole.ts +5 -1
  127. package/src/channel/api/banChannelMembers.ts +7 -3
  128. package/src/channel/api/createChannel.ts +6 -2
  129. package/src/channel/api/getChannel.ts +5 -1
  130. package/src/channel/api/getChannels.ts +5 -1
  131. package/src/channel/api/joinChannel.ts +5 -1
  132. package/src/channel/api/leaveChannel.ts +5 -1
  133. package/src/channel/api/queryChannelMembers.ts +16 -12
  134. package/src/channel/api/queryChannels.ts +9 -4
  135. package/src/channel/api/removeChannelMembers.ts +6 -5
  136. package/src/channel/api/removeChannelMembersRole.ts +5 -1
  137. package/src/channel/api/unbanChannelMembers.ts +7 -3
  138. package/src/channel/api/updateChannel.ts +5 -1
  139. package/src/channel/events/index.ts +1 -0
  140. package/src/channel/events/onChannelCreated.ts +6 -2
  141. package/src/channel/events/onChannelDeleted.ts +6 -2
  142. package/src/channel/events/onChannelJoined.ts +6 -2
  143. package/src/channel/events/onChannelLeft.ts +6 -2
  144. package/src/channel/events/onChannelMemberAdded.ts +6 -2
  145. package/src/channel/events/onChannelMemberRemoved.ts +6 -2
  146. package/src/channel/events/onChannelUpdated.ts +6 -2
  147. package/src/channel/observers/index.ts +1 -0
  148. package/src/channel/observers/observeChannel.ts +1 -11
  149. package/src/channel/observers/observeChannels.ts +59 -0
  150. package/src/channel/tests/utils/prepareChannelPayload.test.ts +120 -0
  151. package/src/channel/utils/index.ts +1 -0
  152. package/src/channel/utils/prepareChannelPayload.ts +31 -0
  153. package/src/client/api/connectClient.ts +4 -9
  154. package/src/client/api/createClient.ts +19 -4
  155. package/src/client/api/getFeedSettings.ts +29 -0
  156. package/src/client/api/index.ts +2 -0
  157. package/src/core/events.ts +2 -1
  158. package/src/core/query/query.ts +3 -3
  159. package/src/core/tests/query/query/runQuery.test.ts +182 -0
  160. package/src/file/api/createVideo.ts +6 -0
  161. package/src/follow/api/queryFollowers.ts +3 -5
  162. package/src/follow/api/queryFollowings.ts +3 -5
  163. package/src/follow/api/utils.ts +7 -3
  164. package/src/follow/observers/index.ts +3 -0
  165. package/src/follow/observers/liveFollowers.ts +149 -0
  166. package/src/follow/observers/liveFollowings.ts +149 -0
  167. package/src/index.ts +1 -1
  168. package/src/message/api/queryMessages.ts +3 -10
  169. package/src/message/observers/index.ts +1 -0
  170. package/src/message/observers/liveMessages.ts +141 -0
  171. package/src/message/observers/observeMessages.ts +0 -1
  172. package/src/message/observers/tests/liveMessages.test.ts +133 -0
  173. package/src/post/observers/livePosts.ts +2 -1
  174. package/src/post/tests/api/getPost.test.ts +3 -3
  175. package/src/post/tests/api/queryPosts.test.ts +4 -4
  176. package/src/utils/constants.ts +1 -0
  177. package/src/utils/tests/client.ts +31 -3
  178. package/src/utils/tests/dummy/channel.ts +52 -0
  179. package/src/utils/tests/dummy/index.ts +7 -0
  180. package/src/utils/tests/dummy/message.ts +21 -0
  181. package/src/utils/tests/dummy/post.ts +31 -0
  182. package/src/utils/tests/dummy/user.ts +11 -0
  183. package/src/utils/tests/index.ts +2 -2
  184. package/dist/utils/tests/dummy.d.ts.map +0 -1
  185. package/src/utils/tests/dummy.ts +0 -7
@@ -0,0 +1,149 @@
1
+ /* eslint-disable no-use-before-define */
2
+ import { getResolver } from '~/core/model';
3
+ import { getActiveClient } from '~/client/api';
4
+ import { pushToCache, pullFromCache } from '~/cache/api';
5
+ import { createQuery, runQuery, queryOptions, filterByPropEquality } from '~/core/query';
6
+
7
+ import {
8
+ COLLECTION_DEFAULT_CACHING_POLICY,
9
+ COLLECTION_DEFAULT_PAGINATION_LIMIT,
10
+ } from '~/utils/constants';
11
+
12
+ import {
13
+ onFollowerRequested,
14
+ onFollowRequestAccepted,
15
+ onFollowRequestDeclined,
16
+ onFollowRequestCanceled,
17
+ onUserFollowed,
18
+ onUserUnfollowed,
19
+ onFollowerDeleted,
20
+ } from '../events';
21
+ import { queryFollowers } from '../api';
22
+
23
+ /**
24
+ * ```js
25
+ * import { liveFollowers } from '@amityco/ts-sdk'
26
+ *
27
+ * let followers = []
28
+ * const unsub = liveFollowers({
29
+ * userId: Amity.User['userId'];
30
+ * }, response => merge(followers, response.data))
31
+ * ```
32
+ *
33
+ * Observe all mutations on a list of {@link Amity.FollowStatus} followers for a given userId
34
+ *
35
+ * @param userId the user
36
+ * @param callback the function to call when new data are available
37
+ * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the messages
38
+ *
39
+ * @category Followers Live Collection
40
+ */
41
+ export const liveFollowers = (
42
+ params: Amity.FollowerLiveCollection,
43
+ callback: Amity.LiveCollectionCallback<Amity.FollowStatus>,
44
+ config?: Amity.LiveCollectionConfig,
45
+ ): Amity.Unsubscriber => {
46
+ const { log, cache } = getActiveClient();
47
+
48
+ if (!cache) {
49
+ console.log('For using Live Collection feature you need to enable Cache!');
50
+ }
51
+
52
+ const timestamp = Date.now();
53
+ log(`liveFollowers(tmpid: ${timestamp}) > listen`);
54
+
55
+ const { limit: queryLimit, ...queryParams } = params;
56
+
57
+ const limit = queryLimit ?? COLLECTION_DEFAULT_PAGINATION_LIMIT;
58
+ const { policy = COLLECTION_DEFAULT_CACHING_POLICY } = config ?? {};
59
+
60
+ const disposers: Amity.Unsubscriber[] = [];
61
+ const cacheKey = ['follow', 'collection', { userId: params.userId, type: 'follower' }];
62
+
63
+ const responder = (data: Amity.FollowerLiveCollectionCache) => {
64
+ let followers: Amity.FollowStatus[] =
65
+ data.data
66
+ .map(key => pullFromCache<Amity.FollowStatus>(['follow', 'get', key])!)
67
+ .filter(Boolean)
68
+ .map(({ data }) => data) ?? [];
69
+
70
+ if (params.status) {
71
+ followers = filterByPropEquality(followers, 'status', params.status);
72
+ }
73
+
74
+ callback({
75
+ onNextPage: onFetch,
76
+ data: followers,
77
+ hasNextPage: !!data.params?.page,
78
+ loading: data.loading,
79
+ error: data.error,
80
+ });
81
+ };
82
+
83
+ const realtimeRouter = (followStatus: Amity.FollowStatus, action: Amity.FollowActionType) => {
84
+ const collection = pullFromCache<Amity.FollowerLiveCollectionCache>(cacheKey)?.data;
85
+
86
+ if (params.userId !== followStatus.to || !collection) return;
87
+
88
+ if (['onDeclined', 'onCanceled', 'onUnfollowed', 'onDeleted'].includes(action)) {
89
+ collection.data = collection.data.filter(p => p !== getResolver('follow')(followStatus));
90
+ } else if (['onRequested', 'onAccepted', 'onFollowed'].includes(action)) {
91
+ collection.data = [...new Set([getResolver('follow')(followStatus), ...collection.data])];
92
+ }
93
+
94
+ pushToCache(cacheKey, collection);
95
+
96
+ responder(collection);
97
+ };
98
+
99
+ const onFetch = () => {
100
+ const collection = pullFromCache<Amity.FollowerLiveCollectionCache>(cacheKey)?.data;
101
+
102
+ const followers = collection?.data ?? [];
103
+
104
+ if (followers.length > 0 && !collection?.params?.page) return;
105
+
106
+ const query = createQuery(queryFollowers, {
107
+ ...queryParams,
108
+ page: collection?.params?.page ?? { limit },
109
+ });
110
+
111
+ runQuery(
112
+ query,
113
+ ({ data: result, error, loading, nextPage: page }) => {
114
+ const data = {
115
+ loading,
116
+ error,
117
+ params: { page },
118
+ data: followers,
119
+ };
120
+
121
+ if (result) {
122
+ data.data = [...new Set([...followers, ...result.map(getResolver('follow'))])];
123
+ }
124
+
125
+ pushToCache(cacheKey, data);
126
+
127
+ responder(data);
128
+ },
129
+ queryOptions(policy),
130
+ );
131
+ };
132
+
133
+ disposers.push(
134
+ onFollowerRequested(followStatus => realtimeRouter(followStatus, 'onRequested')),
135
+ onFollowRequestAccepted(followStatus => realtimeRouter(followStatus, 'onAccepted')),
136
+ onFollowRequestDeclined(followStatus => realtimeRouter(followStatus, 'onDeclined')),
137
+ onFollowRequestCanceled(followStatus => realtimeRouter(followStatus, 'onCanceled')),
138
+ onUserFollowed(followStatus => realtimeRouter(followStatus, 'onFollowed')),
139
+ onUserUnfollowed(followStatus => realtimeRouter(followStatus, 'onUnfollowed')),
140
+ onFollowerDeleted(followStatus => realtimeRouter(followStatus, 'onDeleted')),
141
+ );
142
+
143
+ onFetch();
144
+
145
+ return () => {
146
+ log(`liveFollowers(tmpid: ${timestamp}) > dispose`);
147
+ disposers.forEach(fn => fn());
148
+ };
149
+ };
@@ -0,0 +1,149 @@
1
+ /* eslint-disable no-use-before-define */
2
+ import { getResolver } from '~/core/model';
3
+ import { getActiveClient } from '~/client/api';
4
+ import { pushToCache, pullFromCache } from '~/cache/api';
5
+ import { createQuery, runQuery, queryOptions, filterByPropEquality } from '~/core/query';
6
+
7
+ import {
8
+ COLLECTION_DEFAULT_CACHING_POLICY,
9
+ COLLECTION_DEFAULT_PAGINATION_LIMIT,
10
+ } from '~/utils/constants';
11
+
12
+ import {
13
+ onFollowerRequested,
14
+ onFollowRequestAccepted,
15
+ onFollowRequestDeclined,
16
+ onFollowRequestCanceled,
17
+ onUserFollowed,
18
+ onUserUnfollowed,
19
+ onFollowerDeleted,
20
+ } from '../events';
21
+ import { queryFollowings } from '../api';
22
+
23
+ /**
24
+ * ```js
25
+ * import { liveFollowings } from '@amityco/ts-sdk'
26
+ *
27
+ * let followings = []
28
+ * const unsub = liveFollowings({
29
+ * userId: Amity.User['userId'];
30
+ * }, response => merge(followings, response.data))
31
+ * ```
32
+ *
33
+ * Observe all mutations on a list of {@link Amity.FollowStatus} followings for a given userId
34
+ *
35
+ * @param userId the user
36
+ * @param callback the function to call when new data are available
37
+ * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the messages
38
+ *
39
+ * @category Followings Live Collection
40
+ */
41
+ export const liveFollowings = (
42
+ params: Amity.FollowingLiveCollection,
43
+ callback: Amity.LiveCollectionCallback<Amity.FollowStatus>,
44
+ config?: Amity.LiveCollectionConfig,
45
+ ): Amity.Unsubscriber => {
46
+ const { log, cache } = getActiveClient();
47
+
48
+ if (!cache) {
49
+ console.log('For using Live Collection feature you need to enable Cache!');
50
+ }
51
+
52
+ const timestamp = Date.now();
53
+ log(`liveFollowings(tmpid: ${timestamp}) > listen`);
54
+
55
+ const { limit: queryLimit, ...queryParams } = params;
56
+
57
+ const limit = queryLimit ?? COLLECTION_DEFAULT_PAGINATION_LIMIT;
58
+ const { policy = COLLECTION_DEFAULT_CACHING_POLICY } = config ?? {};
59
+
60
+ const disposers: Amity.Unsubscriber[] = [];
61
+ const cacheKey = ['follow', 'collection', { userId: params.userId, type: 'following' }];
62
+
63
+ const responder = (data: Amity.FollowingLiveCollectionCache) => {
64
+ let followings: Amity.FollowStatus[] =
65
+ data.data
66
+ .map(key => pullFromCache<Amity.FollowStatus>(['follow', 'get', key])!)
67
+ .filter(Boolean)
68
+ .map(({ data }) => data) ?? [];
69
+
70
+ if (params.status) {
71
+ followings = filterByPropEquality(followings, 'status', params.status);
72
+ }
73
+
74
+ callback({
75
+ onNextPage: onFetch,
76
+ data: followings,
77
+ hasNextPage: !!data.params?.page,
78
+ loading: data.loading,
79
+ error: data.error,
80
+ });
81
+ };
82
+
83
+ const realtimeRouter = (followStatus: Amity.FollowStatus, action: Amity.FollowActionType) => {
84
+ const collection = pullFromCache<Amity.FollowingLiveCollectionCache>(cacheKey)?.data;
85
+
86
+ if (params.userId !== followStatus.from || !collection) return;
87
+
88
+ if (['onDeclined', 'onCanceled', 'onUnfollowed', 'onDeleted'].includes(action)) {
89
+ collection.data = collection.data.filter(p => p !== getResolver('follow')(followStatus));
90
+ } else if (['onRequested', 'onAccepted', 'onFollowed'].includes(action)) {
91
+ collection.data = [...new Set([getResolver('follow')(followStatus), ...collection.data])];
92
+ }
93
+
94
+ pushToCache(cacheKey, collection);
95
+
96
+ responder(collection);
97
+ };
98
+
99
+ const onFetch = () => {
100
+ const collection = pullFromCache<Amity.FollowingLiveCollectionCache>(cacheKey)?.data;
101
+
102
+ const followings = collection?.data ?? [];
103
+
104
+ if (followings.length > 0 && !collection?.params?.page) return;
105
+
106
+ const query = createQuery(queryFollowings, {
107
+ ...queryParams,
108
+ page: collection?.params?.page ?? { limit },
109
+ });
110
+
111
+ runQuery(
112
+ query,
113
+ ({ data: result, error, loading, nextPage: page }) => {
114
+ const data = {
115
+ loading,
116
+ error,
117
+ params: { page },
118
+ data: followings,
119
+ };
120
+
121
+ if (result) {
122
+ data.data = [...new Set([...followings, ...result.map(getResolver('follow'))])];
123
+ }
124
+
125
+ pushToCache(cacheKey, data);
126
+
127
+ responder(data);
128
+ },
129
+ queryOptions(policy),
130
+ );
131
+ };
132
+
133
+ disposers.push(
134
+ onFollowerRequested(followStatus => realtimeRouter(followStatus, 'onRequested')),
135
+ onFollowRequestAccepted(followStatus => realtimeRouter(followStatus, 'onAccepted')),
136
+ onFollowRequestDeclined(followStatus => realtimeRouter(followStatus, 'onDeclined')),
137
+ onFollowRequestCanceled(followStatus => realtimeRouter(followStatus, 'onCanceled')),
138
+ onUserFollowed(followStatus => realtimeRouter(followStatus, 'onFollowed')),
139
+ onUserUnfollowed(followStatus => realtimeRouter(followStatus, 'onUnfollowed')),
140
+ onFollowerDeleted(followStatus => realtimeRouter(followStatus, 'onDeleted')),
141
+ );
142
+
143
+ onFetch();
144
+
145
+ return () => {
146
+ log(`liveFollowings(tmpid: ${timestamp}) > dispose`);
147
+ disposers.forEach(fn => fn());
148
+ };
149
+ };
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // types
2
- import './@types';
2
+ export * from './@types';
3
3
 
4
4
  export * from './version';
5
5
 
@@ -19,16 +19,9 @@ import { getResolver } from '~/core/model';
19
19
  * @category Message API
20
20
  * @async
21
21
  */
22
- export const queryMessages = async (query: {
23
- channelId: Amity.Channel['channelId'];
24
- type?: Amity.MessageContentType;
25
- parentId?: Amity.Message['parentId'];
26
- filterByParentId?: boolean;
27
- isDeleted?: boolean;
28
- tags?: Amity.Taggable['tags'];
29
- excludeTags?: Amity.Taggable['tags'];
30
- page?: Amity.Page;
31
- }): Promise<Amity.Cached<Amity.Paged<Amity.Message>>> => {
22
+ export const queryMessages = async (
23
+ query: Amity.QueryMessages,
24
+ ): Promise<Amity.Cached<Amity.Paged<Amity.Message>>> => {
32
25
  const client = getActiveClient();
33
26
  client.log('message/queryMessages', query);
34
27
 
@@ -1,2 +1,3 @@
1
1
  export { observeMessage } from './observeMessage';
2
2
  export { observeMessages } from './observeMessages';
3
+ export { liveMessages } from './liveMessages';
@@ -0,0 +1,141 @@
1
+ /* eslint-disable no-use-before-define */
2
+ import { getResolver } from '~/core/model';
3
+ import { getActiveClient } from '~/client/api';
4
+ import { pushToCache, pullFromCache } from '~/cache/api';
5
+ import { createQuery, runQuery, queryOptions, filterByPropEquality } from '~/core/query';
6
+
7
+ import {
8
+ COLLECTION_DEFAULT_CACHING_POLICY,
9
+ COLLECTION_DEFAULT_PAGINATION_LIMIT,
10
+ ENABLE_CACHE_MESSAGE,
11
+ } from '~/utils/constants';
12
+
13
+ import { onMessageCreated, onMessageDeleted, onMessageUpdated } from '../events';
14
+ import { queryMessages } from '../api';
15
+
16
+ /**
17
+ * ```js
18
+ * import { liveMessages } from '@amityco/ts-sdk'
19
+ *
20
+ * let messages = []
21
+ * const unsub = liveMessages({
22
+ * channelId: Amity.Channel['channelId'];
23
+ * }, response => merge(messages, response.data))
24
+ * ```
25
+ *
26
+ * Observe all mutations on a list of {@link Amity.Message} for a given target object
27
+ *
28
+ * @param params for querying memssages from a channel
29
+ * @param callback the function to call when new data are available
30
+ * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the messages
31
+ *
32
+ * @category Messages Live Collection
33
+ */
34
+ export const liveMessages = (
35
+ params: Amity.MessagesLiveCollection,
36
+ callback: Amity.LiveCollectionCallback<Amity.Message>,
37
+ config?: Amity.LiveCollectionConfig,
38
+ ): Amity.Unsubscriber => {
39
+ const { log, cache } = getActiveClient();
40
+
41
+ if (!cache) {
42
+ console.log(ENABLE_CACHE_MESSAGE);
43
+ }
44
+
45
+ const timestamp = Date.now();
46
+ log(`liveMessages(tmpid: ${timestamp}) > listen`);
47
+
48
+ const { limit: queryLimit, ...queryParams } = params;
49
+
50
+ const limit = queryLimit ?? COLLECTION_DEFAULT_PAGINATION_LIMIT;
51
+ const { policy = COLLECTION_DEFAULT_CACHING_POLICY } = config ?? {};
52
+
53
+ const disposers: Amity.Unsubscriber[] = [];
54
+ const cacheKey = ['message', 'collection', { channelId: params.channelId }];
55
+
56
+ const responder = (data: Amity.MessageLiveCollectionCache) => {
57
+ let messages: Amity.Message[] =
58
+ data.data
59
+ .map(messageId => pullFromCache<Amity.Message>(['message', 'get', messageId])!)
60
+ .filter(Boolean)
61
+ .map(({ data }) => data) ?? [];
62
+
63
+ /*
64
+ * for cases when message is deleted via RTE, this flag is used to get
65
+ * items fro cache that are deleted | !deleted
66
+ */
67
+ messages = filterByPropEquality(messages, 'isDeleted', params.isDeleted);
68
+
69
+ callback({
70
+ onNextPage: onFetch,
71
+ data: messages,
72
+ hasNextPage: !!data.params?.page,
73
+ loading: data.loading,
74
+ error: data.error,
75
+ });
76
+ };
77
+
78
+ const realtimeRouter = (message: Amity.Message, action: Amity.MessageActionType) => {
79
+ const collection = pullFromCache<Amity.MessageLiveCollectionCache>(cacheKey)?.data;
80
+
81
+ if (params.channelId !== message.channelId || !collection) return;
82
+
83
+ if (action === 'onCreate') {
84
+ collection.data = [...new Set([message.messageId, ...collection.data])];
85
+ } else if (['onDelete'].includes(action)) {
86
+ collection.data = collection.data.filter(m => m !== message.messageId);
87
+ }
88
+
89
+ pushToCache(cacheKey, collection);
90
+
91
+ responder(collection);
92
+ };
93
+
94
+ const onFetch = () => {
95
+ const collection = pullFromCache<Amity.MessageLiveCollectionCache>(cacheKey)?.data;
96
+
97
+ const messages = collection?.data ?? [];
98
+
99
+ if (messages.length > 0 && !collection?.params?.page) return;
100
+
101
+ const query = createQuery(queryMessages, {
102
+ ...queryParams,
103
+ page: collection?.params?.page ?? { limit },
104
+ });
105
+
106
+ runQuery(
107
+ query,
108
+ ({ data: result, error, loading, prevPage: page }) => {
109
+ const data = {
110
+ loading,
111
+ error,
112
+ params: { page },
113
+ data: messages,
114
+ };
115
+
116
+ if (result) {
117
+ data.data = [...new Set([...messages, ...result.map(getResolver('message'))])];
118
+ }
119
+
120
+ pushToCache(cacheKey, data);
121
+
122
+ responder(data);
123
+ },
124
+ queryOptions(policy),
125
+ );
126
+ };
127
+
128
+ disposers.push(
129
+ onMessageCreated(message => realtimeRouter(message, 'onCreate')),
130
+ onMessageDeleted(message => realtimeRouter(message, 'onDelete')),
131
+
132
+ onMessageUpdated(message => realtimeRouter(message, 'onUpdate')),
133
+ );
134
+
135
+ onFetch();
136
+
137
+ return () => {
138
+ log(`liveMessages(tmpid: ${timestamp}) > dispose`);
139
+ disposers.forEach(fn => fn());
140
+ };
141
+ };
@@ -12,7 +12,6 @@ import { onMessageCreated, onMessageUpdated, onMessageDeleted } from '../events'
12
12
  *
13
13
  * Observe all mutations on a list of {@link Amity.Message} for a given {@link Amity.Channel} object
14
14
  *
15
- * @param client the client to observe the message with
16
15
  * @param channelId the ID of the channel where to observe the messages
17
16
  * @param callback the function to call when new data are available
18
17
  * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the messages
@@ -0,0 +1,133 @@
1
+ import { disableCache, enableCache, pushToCache } from '~/cache/api';
2
+ import { ENABLE_CACHE_MESSAGE } from '~/utils/constants';
3
+
4
+ import { liveMessages } from '../liveMessages';
5
+
6
+ import { client, messages } from '~/utils/tests';
7
+ import { queryMessages } from '~/message/api';
8
+
9
+ describe('liveMessages', () => {
10
+ beforeEach(enableCache);
11
+ afterEach(disableCache);
12
+
13
+ test('it should show message if cache not enabled', () => {
14
+ /*
15
+ * NOTE: at the time of writing this test cache is diabled by default, but
16
+ * there is a proposal to enable cache by default. So I'm disabling cache
17
+ * here to ensure cache is disabled
18
+ */
19
+ disableCache();
20
+
21
+ const callback = jest.fn();
22
+ jest.spyOn(global.console, 'log');
23
+ client.http.get = jest.fn();
24
+
25
+ // call liveMessages check if mocked console get's called with the correct
26
+ // message
27
+ client.use();
28
+ liveMessages({ channelId: messages.channelId }, callback);
29
+
30
+ expect(console.log).toBeCalledWith(ENABLE_CACHE_MESSAGE);
31
+ });
32
+
33
+ test('it should return message collection', async () => {
34
+ const returnValue = [{ messageId: messages.page1[0] }];
35
+
36
+ // mock response of queryMessages and validate
37
+ const callback = jest.fn();
38
+ client.http.get = jest.fn().mockResolvedValue({
39
+ data: {
40
+ messages: returnValue,
41
+ paging: {},
42
+ },
43
+ });
44
+
45
+ liveMessages({ channelId: messages.channelId }, callback);
46
+
47
+ expect(callback).toHaveBeenCalledTimes(1);
48
+
49
+ // check if cache data returned (should be empty)
50
+ expect(callback).toHaveBeenCalledWith(
51
+ expect.objectContaining({
52
+ data: [],
53
+ error: undefined,
54
+ loading: true,
55
+ }),
56
+ );
57
+
58
+ await expect(queryMessages({ channelId: messages.channelId })).resolves.toBeTruthy();
59
+
60
+ expect(callback).toHaveBeenCalledTimes(2);
61
+
62
+ expect(callback).toHaveBeenCalledWith(
63
+ expect.objectContaining({
64
+ data: returnValue,
65
+ error: undefined,
66
+ loading: false,
67
+ }),
68
+ );
69
+ });
70
+
71
+ test('it should filter deleted messages based on param', async () => {
72
+ // mock response of queryMessages with a deleted message
73
+ const returnValue = [
74
+ { messageId: messages.page1[0], isDeleted: true },
75
+ { messageId: messages.page1[1], isDeleted: false },
76
+ ];
77
+
78
+ // mock response of queryMessages and validate
79
+ const callback = jest.fn();
80
+ client.http.get = jest.fn().mockResolvedValue({
81
+ data: {
82
+ messages: returnValue,
83
+ paging: {},
84
+ },
85
+ });
86
+
87
+ liveMessages({ channelId: messages.channelId, isDeleted: false }, callback);
88
+
89
+ expect(callback).toHaveBeenCalledTimes(1);
90
+
91
+ // check if cache data returned (should be empty)
92
+ expect(callback).toHaveBeenCalledWith(
93
+ expect.objectContaining({
94
+ data: [],
95
+ error: undefined,
96
+ loading: true,
97
+ }),
98
+ );
99
+
100
+ await expect(queryMessages({ channelId: messages.channelId })).resolves.toBeTruthy();
101
+
102
+ expect(callback).toHaveBeenCalledTimes(2);
103
+
104
+ expect(callback).toHaveBeenCalledWith(
105
+ expect.objectContaining({
106
+ data: [returnValue[1]],
107
+ error: undefined,
108
+ loading: false,
109
+ }),
110
+ );
111
+ });
112
+
113
+ test('it should return method to fetch next page', async () => {
114
+ // mock nextPage()
115
+ });
116
+
117
+ test('it should add new message to collection onCreate', () => {
118
+ // new message should be added to top of collection
119
+ });
120
+
121
+ test('it should update message in collection onUpdate', () => {
122
+ // updated message should be reflected in collection
123
+ });
124
+
125
+ test('it should delete message from collection onDelete', () => {
126
+ // should remove message from collection on delete
127
+ });
128
+
129
+ test('it should get messages from cache if present', () => {
130
+ // push message with Id in collection response to cache
131
+ // get request to get that messageId should not be called
132
+ });
133
+ });
@@ -14,6 +14,7 @@ import {
14
14
  import {
15
15
  COLLECTION_DEFAULT_CACHING_POLICY,
16
16
  COLLECTION_DEFAULT_PAGINATION_LIMIT,
17
+ ENABLE_CACHE_MESSAGE,
17
18
  } from '~/utils/constants';
18
19
 
19
20
  import {
@@ -57,7 +58,7 @@ export const livePosts = (
57
58
  const { log, cache } = getActiveClient();
58
59
 
59
60
  if (!cache) {
60
- console.log('For using Live Collection feature you need to enable Cache!');
61
+ console.log(ENABLE_CACHE_MESSAGE);
61
62
  }
62
63
 
63
64
  const timestamp = Date.now();
@@ -1,12 +1,12 @@
1
1
  import { ASCApiError } from '~/core/errors';
2
- import { client, userPosts } from '~/utils/tests';
2
+ import { client, posts } from '~/utils/tests';
3
3
  import { pushToCache, enableCache, disableCache } from '~/cache/api';
4
4
 
5
5
  import { getPost } from '~/post/api';
6
6
 
7
7
  describe('post/api', () => {
8
- const post = { postId: userPosts.page1[0] };
9
- const post2 = { postId: userPosts.page1[1] };
8
+ const post = { postId: posts.page1[0] };
9
+ const post2 = { postId: posts.page1[1] };
10
10
 
11
11
  describe('getPost.locally', () => {
12
12
  test('should return undefined if cache is turned off', () => {
@@ -1,9 +1,9 @@
1
1
  import { queryPosts } from '~/post/api';
2
- import { client, userPosts } from '~/utils/tests';
2
+ import { client, posts } from '~/utils/tests';
3
3
 
4
4
  describe('post/api', () => {
5
5
  test('should return a post query', async () => {
6
- const returnValue = [{ postId: userPosts.page1[0] }];
6
+ const returnValue = [{ postId: posts.page1[0] }];
7
7
 
8
8
  client.http.get = jest.fn().mockResolvedValue({
9
9
  data: {
@@ -13,8 +13,8 @@ describe('post/api', () => {
13
13
  });
14
14
 
15
15
  const data = await queryPosts({
16
- targetId: userPosts.targetId,
17
- targetType: userPosts.targetType,
16
+ targetId: posts.targetId,
17
+ targetType: posts.targetType,
18
18
  });
19
19
 
20
20
  expect(data).toBeDefined();
@@ -1,2 +1,3 @@
1
1
  export const COLLECTION_DEFAULT_PAGINATION_LIMIT = 5;
2
2
  export const COLLECTION_DEFAULT_CACHING_POLICY: Amity.QueryPolicy = 'cache_then_server';
3
+ export const ENABLE_CACHE_MESSAGE = 'For using Live Collection feature you need to enable Cache!';