@amityco/ts-sdk 0.0.1-beta.38 → 0.0.1-beta.39

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 (89) hide show
  1. package/dist/@types/core/model.d.ts +2 -0
  2. package/dist/@types/core/model.d.ts.map +1 -1
  3. package/dist/@types/domains/channel.d.ts +2 -2
  4. package/dist/@types/domains/channel.d.ts.map +1 -1
  5. package/dist/@types/domains/feed.d.ts +1 -2
  6. package/dist/@types/domains/feed.d.ts.map +1 -1
  7. package/dist/@types/domains/message.d.ts +1 -1
  8. package/dist/@types/domains/message.d.ts.map +1 -1
  9. package/dist/@types/domains/post.d.ts +1 -1
  10. package/dist/@types/domains/post.d.ts.map +1 -1
  11. package/dist/@types/domains/user.d.ts +8 -0
  12. package/dist/@types/domains/user.d.ts.map +1 -1
  13. package/dist/channel/observers/liveChannels.d.ts.map +1 -1
  14. package/dist/client/api/connectClient.d.ts.map +1 -1
  15. package/dist/core/model/idResolvers.d.ts.map +1 -1
  16. package/dist/core/model/identifyModel.d.ts.map +1 -1
  17. package/dist/core/model/index.d.ts.map +1 -1
  18. package/dist/core/query/filtering.d.ts +10 -0
  19. package/dist/core/query/filtering.d.ts.map +1 -1
  20. package/dist/core/query/paging.d.ts +2 -2
  21. package/dist/core/query/paging.d.ts.map +1 -1
  22. package/dist/core/query/tests/paging.test.d.ts +2 -0
  23. package/dist/core/query/tests/paging.test.d.ts.map +1 -0
  24. package/dist/index.cjs.js +180 -71
  25. package/dist/index.esm.js +178 -71
  26. package/dist/index.umd.js +4 -4
  27. package/dist/message/api/queryMessages.d.ts +2 -2
  28. package/dist/message/api/queryMessages.d.ts.map +1 -1
  29. package/dist/post/observers/livePosts.d.ts +2 -2
  30. package/dist/post/observers/livePosts.d.ts.map +1 -1
  31. package/dist/report/api/createReport.d.ts.map +1 -1
  32. package/dist/report/api/deleteReport.d.ts.map +1 -1
  33. package/dist/report/api/tests/createReport.test.d.ts +2 -0
  34. package/dist/report/api/tests/createReport.test.d.ts.map +1 -0
  35. package/dist/report/api/tests/deleteReport.test.d.ts +2 -0
  36. package/dist/report/api/tests/deleteReport.test.d.ts.map +1 -0
  37. package/dist/user/api/queryUsers.d.ts +1 -6
  38. package/dist/user/api/queryUsers.d.ts.map +1 -1
  39. package/dist/user/observers/index.d.ts +1 -0
  40. package/dist/user/observers/index.d.ts.map +1 -1
  41. package/dist/user/observers/liveUsers.d.ts +18 -0
  42. package/dist/user/observers/liveUsers.d.ts.map +1 -0
  43. package/dist/user/observers/tests/liveUsers.test.d.ts +2 -0
  44. package/dist/user/observers/tests/liveUsers.test.d.ts.map +1 -0
  45. package/dist/utils/liveObject.d.ts +1 -1
  46. package/dist/utils/liveObject.d.ts.map +1 -1
  47. package/dist/utils/tests/dummy/channel.d.ts.map +1 -1
  48. package/dist/utils/tests/dummy/feed.d.ts +21 -0
  49. package/dist/utils/tests/dummy/feed.d.ts.map +1 -0
  50. package/dist/utils/tests/dummy/index.d.ts +1 -0
  51. package/dist/utils/tests/dummy/index.d.ts.map +1 -1
  52. package/dist/utils/tests/dummy/post.d.ts +9 -0
  53. package/dist/utils/tests/dummy/post.d.ts.map +1 -1
  54. package/dist/utils/tests/dummy/user.d.ts +63 -0
  55. package/dist/utils/tests/dummy/user.d.ts.map +1 -1
  56. package/package.json +1 -1
  57. package/src/@types/core/model.ts +4 -0
  58. package/src/@types/domains/channel.ts +2 -2
  59. package/src/@types/domains/feed.ts +1 -3
  60. package/src/@types/domains/message.ts +4 -1
  61. package/src/@types/domains/post.ts +1 -1
  62. package/src/@types/domains/user.ts +14 -0
  63. package/src/cache/api/tests/ingestInCache.test.ts +2 -0
  64. package/src/channel/observers/liveChannels.ts +29 -24
  65. package/src/client/api/connectClient.ts +0 -2
  66. package/src/core/model/idResolvers.ts +2 -0
  67. package/src/core/model/identifyModel.ts +1 -0
  68. package/src/core/model/index.ts +2 -0
  69. package/src/core/query/filtering.ts +33 -3
  70. package/src/core/query/paging.ts +5 -4
  71. package/src/core/query/tests/paging.test.ts +66 -0
  72. package/src/message/api/queryMessages.ts +7 -7
  73. package/src/message/api/tests/queryMessages.test.ts +6 -6
  74. package/src/message/tests/observers/liveMessages.test.ts +4 -4
  75. package/src/post/observers/livePosts.ts +14 -22
  76. package/src/post/observers/tests/livePosts.test.ts +65 -8
  77. package/src/report/api/createReport.ts +1 -6
  78. package/src/report/api/deleteReport.ts +2 -6
  79. package/src/report/api/tests/createReport.test.ts +93 -0
  80. package/src/report/api/tests/deleteReport.test.ts +93 -0
  81. package/src/user/api/queryUsers.ts +1 -6
  82. package/src/user/observers/index.ts +1 -0
  83. package/src/user/observers/liveUsers.ts +162 -0
  84. package/src/user/observers/tests/liveUsers.test.ts +202 -0
  85. package/src/utils/tests/dummy/channel.ts +1 -0
  86. package/src/utils/tests/dummy/feed.ts +13 -0
  87. package/src/utils/tests/dummy/index.ts +2 -0
  88. package/src/utils/tests/dummy/post.ts +6 -2
  89. package/src/utils/tests/dummy/user.ts +36 -0
@@ -0,0 +1,162 @@
1
+ /* eslint-disable no-use-before-define */
2
+ import { getResolver } from '~/core/model';
3
+ import { pullFromCache, pushToCache } from '~/cache/api';
4
+ import { getActiveClient } from '~/client/api';
5
+
6
+ import {
7
+ createQuery,
8
+ runQuery,
9
+ queryOptions,
10
+ filterByPropEquality,
11
+ sortByLastCreated,
12
+ sortByDisplayName,
13
+ sortByFirstCreated,
14
+ } from '~/core/query';
15
+
16
+ import {
17
+ COLLECTION_DEFAULT_CACHING_POLICY,
18
+ COLLECTION_DEFAULT_PAGINATION_LIMIT,
19
+ ENABLE_CACHE_MESSAGE,
20
+ } from '~/utils/constants';
21
+
22
+ import { queryUsers } from '../api';
23
+ import {
24
+ onUserFlagged,
25
+ onUserUpdated,
26
+ onUserUnflagged,
27
+ onUserFlagCleared,
28
+ onUserDeleted,
29
+ } from '../events';
30
+
31
+ /**
32
+ * ```js
33
+ * import { liveUsers } from '@amityco/ts-sdk'
34
+ *
35
+ * let users = []
36
+ * const unsub = liveUsers({}, response => merge(users, response.data))
37
+ * ```
38
+ *
39
+ * Observe all mutations on a list of {@link Amity.User}s
40
+ *
41
+ * @param params for querying users
42
+ * @param callback the function to call when new data are available
43
+ * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the users
44
+ *
45
+ * @category Category Live Collection
46
+ */
47
+ export const liveUsers = (
48
+ params: Amity.UserLiveCollection,
49
+ callback: Amity.LiveCollectionCallback<Amity.User>,
50
+ config?: Amity.LiveCollectionConfig,
51
+ ) => {
52
+ const { log, cache } = getActiveClient();
53
+
54
+ if (!cache) {
55
+ console.log(ENABLE_CACHE_MESSAGE);
56
+ }
57
+
58
+ const timestamp = Date.now();
59
+ log(`liveUsers(tmpid: ${timestamp}) > listen`);
60
+
61
+ const { limit: queryLimit, ...queryParams } = params;
62
+
63
+ const limit = queryLimit ?? COLLECTION_DEFAULT_PAGINATION_LIMIT;
64
+ const { policy = COLLECTION_DEFAULT_CACHING_POLICY } = config ?? {};
65
+
66
+ const disposers: Amity.Unsubscriber[] = [];
67
+ const cacheKey = ['user', 'collection', {}];
68
+
69
+ const responder = (data: Amity.UserLiveCollectionCache) => {
70
+ let users: Amity.User[] =
71
+ data.data
72
+ .map(userId => pullFromCache<Amity.User>(['user', 'get', userId])!)
73
+ .filter(Boolean)
74
+ .map(({ data }) => data) ?? [];
75
+
76
+ users = filterByPropEquality(users, 'displayName', params.displayName);
77
+
78
+ switch (params.sortBy) {
79
+ case 'firstCreated':
80
+ users = users.sort(sortByFirstCreated);
81
+ break;
82
+
83
+ case 'lastCreated':
84
+ users = users.sort(sortByLastCreated);
85
+ break;
86
+
87
+ default:
88
+ users = users
89
+ // this needs to be aligned with the backend data type
90
+ .map(u => (u.displayName ? u : { ...u, displayName: '' }))
91
+ // @ts-ignore
92
+ .sort(sortByDisplayName);
93
+ }
94
+
95
+ callback({
96
+ onNextPage: onFetch,
97
+ data: users,
98
+ hasNextPage: !!data.params?.page,
99
+ loading: data.loading,
100
+ error: data.error,
101
+ });
102
+ };
103
+
104
+ const realtimeRouter = (_: Amity.UserActionType) => (user: Amity.User) => {
105
+ const collection = pullFromCache<Amity.CommunityLiveCollectionCache>(cacheKey)?.data;
106
+ if (!collection) return;
107
+
108
+ collection.data = [...new Set([user.userId, ...collection.data])];
109
+
110
+ pushToCache(cacheKey, collection);
111
+ responder(collection);
112
+ };
113
+
114
+ const onFetch = () => {
115
+ const collection = pullFromCache<Amity.UserLiveCollectionCache>(cacheKey)?.data;
116
+
117
+ const users = collection?.data ?? [];
118
+
119
+ if (users.length > 0 && !collection?.params.page) return;
120
+
121
+ const query = createQuery(queryUsers, {
122
+ ...queryParams,
123
+ page: collection?.params.page ?? { limit },
124
+ });
125
+
126
+ runQuery(
127
+ query,
128
+ ({ data: result, error, loading, nextPage: page }) => {
129
+ const data = {
130
+ loading,
131
+ error,
132
+ params: { page },
133
+ data: users,
134
+ };
135
+
136
+ if (result) {
137
+ data.data = [...new Set([...users, ...result.map(getResolver('user'))])];
138
+ }
139
+
140
+ pushToCache(cacheKey, data);
141
+
142
+ responder(data);
143
+ },
144
+ queryOptions(policy),
145
+ );
146
+ };
147
+
148
+ disposers.push(
149
+ onUserUpdated(realtimeRouter('onUpdate')),
150
+ onUserDeleted(realtimeRouter('onDelete')),
151
+ onUserFlagged(realtimeRouter('onFlagged')),
152
+ onUserUnflagged(realtimeRouter('onUnflagged')),
153
+ onUserFlagCleared(realtimeRouter('onFlagCleared')),
154
+ );
155
+
156
+ onFetch();
157
+
158
+ return () => {
159
+ log(`liveUsers(tmpid: ${timestamp}) > dispose`);
160
+ disposers.forEach(fn => fn());
161
+ };
162
+ };
@@ -0,0 +1,202 @@
1
+ import { disableCache, enableCache } from '~/cache/api';
2
+ import {
3
+ client,
4
+ connectClient,
5
+ disconnectClient,
6
+ pause,
7
+ user11,
8
+ user12,
9
+ user21,
10
+ userQueryResponse,
11
+ userQueryResponsePage2,
12
+ } from '~/utils/tests';
13
+
14
+ import { liveUsers } from '../liveUsers';
15
+
16
+ const getSnapshot = (params?: Record<string, any>) => {
17
+ return {
18
+ data: [] as Amity.User[],
19
+ loading: true,
20
+ error: undefined as any,
21
+ ...params,
22
+ };
23
+ };
24
+
25
+ const { paging, ...payload } = userQueryResponse.data;
26
+ const { paging: paging2, ...payload2 } = userQueryResponsePage2.data;
27
+
28
+ describe('liveUsers', () => {
29
+ beforeAll(connectClient);
30
+ afterAll(disconnectClient);
31
+
32
+ beforeEach(enableCache);
33
+
34
+ afterEach(disableCache);
35
+
36
+ const params = { sortBy: 'lastCreated' } as Amity.UserLiveCollection;
37
+
38
+ test('it should return users collection', async () => {
39
+ const callback = jest.fn();
40
+ client.http.get = jest.fn().mockResolvedValue(userQueryResponse);
41
+
42
+ liveUsers(params, callback);
43
+ await pause();
44
+
45
+ expect(callback).toHaveBeenCalledTimes(2);
46
+ expect(callback).toHaveBeenNthCalledWith(1, expect.objectContaining(getSnapshot()));
47
+ expect(callback).toHaveBeenNthCalledWith(
48
+ 2,
49
+ expect.objectContaining(
50
+ getSnapshot({
51
+ data: payload.users,
52
+ loading: false,
53
+ }),
54
+ ),
55
+ );
56
+ });
57
+
58
+ test('it should return data from cache', async () => {
59
+ const callback = jest.fn();
60
+ client.http.get = jest.fn().mockResolvedValue(userQueryResponse);
61
+
62
+ liveUsers(params, () => undefined);
63
+ await pause();
64
+ liveUsers(params, callback);
65
+ await pause();
66
+
67
+ expect(callback).toHaveBeenCalledTimes(2);
68
+ expect(callback).toHaveBeenNthCalledWith(
69
+ 1,
70
+ expect.objectContaining(
71
+ getSnapshot({
72
+ data: payload.users,
73
+ }),
74
+ ),
75
+ );
76
+ expect(callback).toHaveBeenNthCalledWith(
77
+ 2,
78
+ expect.objectContaining(
79
+ getSnapshot({
80
+ data: payload.users,
81
+ loading: false,
82
+ }),
83
+ ),
84
+ );
85
+ });
86
+
87
+ test('it should return method to fetch next page', async () => {
88
+ const callback = jest.fn();
89
+ client.http.get = jest
90
+ .fn()
91
+ .mockResolvedValue(userQueryResponse)
92
+ .mockResolvedValueOnce(userQueryResponsePage2);
93
+
94
+ liveUsers(params, callback);
95
+ await pause();
96
+
97
+ expect(callback).toHaveBeenCalled();
98
+ expect(callback.mock.lastCall).toHaveLength(1);
99
+
100
+ const { onNextPage, hasNextPage } = callback.mock.lastCall[0];
101
+
102
+ expect(hasNextPage).toBe(true);
103
+ expect(onNextPage).toBeTruthy();
104
+
105
+ onNextPage();
106
+ await pause();
107
+
108
+ // 4 -> because 1 local & server call each per call (2)
109
+ expect(callback).toHaveBeenCalledTimes(4);
110
+ expect(callback).toHaveBeenNthCalledWith(
111
+ 4,
112
+ expect.objectContaining(
113
+ getSnapshot({
114
+ loading: false,
115
+ data: [...payload2.users, ...payload.users],
116
+ }),
117
+ ),
118
+ );
119
+ });
120
+
121
+ describe('events', () => {
122
+ const updatedUser = { ...user11, displayName: 'updated-user' };
123
+ const flaggedUser = { ...user11, flagCount: 1 };
124
+ const unflaggedUser = { ...user11, flagCount: 1 };
125
+ const flagClearedUser = { ...user11, flagCount: 0 };
126
+
127
+ const cases: [string, keyof Amity.Events, Amity.UserPayload, Amity.User[]][] = [
128
+ [
129
+ 'it should update user in collection onUpdate',
130
+ 'user.updated',
131
+ { users: [updatedUser], files: [] },
132
+ [updatedUser, user12],
133
+ ],
134
+ [
135
+ 'it should update user in collection onUpdate',
136
+ 'user.flagged',
137
+ { users: [flaggedUser], files: [] },
138
+ [flaggedUser, user12],
139
+ ],
140
+ [
141
+ 'it should update user in collection onUpdate',
142
+ 'user.unflagged',
143
+ { users: [unflaggedUser], files: [] },
144
+ [unflaggedUser, user12],
145
+ ],
146
+ [
147
+ 'it should update user in collection onUpdate',
148
+ 'user.flagCleared',
149
+ { users: [flagClearedUser], files: [] },
150
+ [flagClearedUser, user12],
151
+ ],
152
+ ];
153
+
154
+ test.each(cases)('%s', async (test, event, user, expected) => {
155
+ const callback = jest.fn();
156
+ client.http.get = jest.fn().mockResolvedValue(userQueryResponse);
157
+
158
+ liveUsers(params, callback);
159
+ await pause();
160
+
161
+ client.emitter.emit(event, user);
162
+ await pause();
163
+
164
+ expect(callback).toHaveBeenNthCalledWith(
165
+ 3,
166
+ expect.objectContaining(
167
+ getSnapshot({
168
+ data: expected,
169
+ loading: false,
170
+ }),
171
+ ),
172
+ );
173
+ });
174
+ });
175
+
176
+ const filters: [string, Amity.UserLiveCollection, Amity.User[]][] = [
177
+ ['displayName', { displayName: 'displayName' }, [user21]],
178
+ ];
179
+
180
+ test.each(filters)('it should filter by %s communities', async (filter, params, expected) => {
181
+ const callback = jest.fn();
182
+ client.http.get = jest.fn().mockResolvedValue(userQueryResponsePage2);
183
+
184
+ liveUsers(params, callback);
185
+
186
+ expect(callback).toHaveBeenCalledTimes(1);
187
+ // check if cache data returned (should be empty)
188
+ expect(callback).toHaveBeenCalledWith(expect.objectContaining(getSnapshot()));
189
+
190
+ await pause();
191
+
192
+ expect(callback).toHaveBeenCalledTimes(2);
193
+ expect(callback).toHaveBeenCalledWith(
194
+ expect.objectContaining(
195
+ getSnapshot({
196
+ loading: false,
197
+ data: expected,
198
+ }),
199
+ ),
200
+ );
201
+ });
202
+ });
@@ -6,6 +6,7 @@ export function generateRawChannel(params?: Partial<Amity.ChannelRaw>): Amity.Ch
6
6
 
7
7
  return {
8
8
  _id: `${channelId}-internalId`,
9
+ lastActivity: '',
9
10
  channelId,
10
11
  createdAt: date,
11
12
  isDeleted: false,
@@ -0,0 +1,13 @@
1
+ export const feed11 = {
2
+ targetType: 'community',
3
+ targetId: '6396a419becb56760a7b0bb9',
4
+ feedType: 'published',
5
+ feedId: '16708167935683a5eb158a39f40ffbdd49b7f70dc9bfd',
6
+ postCount: 4,
7
+ createdAt: '2022-12-12T03:46:33.557Z',
8
+ updatedAt: '2022-12-12T05:51:37.927Z',
9
+ };
10
+
11
+ export const feedPayload = {
12
+ feeds: [feed11],
13
+ };
@@ -21,3 +21,5 @@ export * from './community';
21
21
  export * from './category';
22
22
 
23
23
  export * from './stream';
24
+
25
+ export * from './feed';
@@ -1,4 +1,4 @@
1
- import { user11, file11, communityUser11, date } from '.';
1
+ import { user11, file11, date, feed11 } from '.';
2
2
  import { encodeJson } from '..';
3
3
 
4
4
  export function generatePost(params?: Partial<Amity.Post>): Amity.Post {
@@ -18,6 +18,7 @@ export function generatePost(params?: Partial<Amity.Post>): Amity.Post {
18
18
  path: '',
19
19
  postedUserId: user11.userId,
20
20
  postId: 'postId11',
21
+ isDeleted: false,
21
22
  reactions: {},
22
23
  reactionsCount: 0,
23
24
  targetId: user11.userId,
@@ -73,6 +74,7 @@ export const post14: Amity.Post = {
73
74
  ...post11,
74
75
  postId: 'post14',
75
76
  targetType: 'community',
77
+ feedId: 'feedId',
76
78
  isDeleted: false,
77
79
  children: [],
78
80
  };
@@ -82,6 +84,7 @@ export const post15: Amity.Post = {
82
84
  postId: 'post15',
83
85
  isDeleted: true,
84
86
  children: [post11.postId],
87
+ feedId: 'feedId',
85
88
  targetType: 'community',
86
89
  tags: ['test_tag'],
87
90
  };
@@ -128,8 +131,9 @@ export const postQueryResponse = {
128
131
 
129
132
  export const postQueryResponsePage2 = {
130
133
  data: {
131
- posts: [post14, post15, post16],
134
+ posts: [post14, post15],
132
135
  files: [],
136
+ feeds: [{ ...feed11, feedType: 'reviewing', targetId: post14.targetId, feedId: post14.feedId }],
133
137
  paging: {
134
138
  next: nextPage,
135
139
  },
@@ -26,6 +26,17 @@ export const user14: Amity.User = {
26
26
  userId: 'user4',
27
27
  };
28
28
 
29
+ export const user21: Amity.User = {
30
+ ...user11,
31
+ displayName: 'displayName',
32
+ userId: 'user21',
33
+ };
34
+
35
+ export const user22: Amity.User = {
36
+ ...user11,
37
+ userId: 'user22',
38
+ };
39
+
29
40
  export const userQueryResponse = {
30
41
  data: {
31
42
  users: [user11, user12],
@@ -37,6 +48,30 @@ export const userQueryResponse = {
37
48
  },
38
49
  };
39
50
 
51
+ export const userQueryResponsePage2 = {
52
+ data: {
53
+ users: [user21, user22],
54
+ files: [],
55
+ paging: {
56
+ previous: 'eyJiZWZvcmUiOjU1LCJsYXN0IjoxMH0=',
57
+ next: 'eyJiZWZvcmUiOjU1LCJsYXN0IjoxMH0=',
58
+ },
59
+ },
60
+ };
61
+
62
+ export const flaggedUser = {
63
+ ...user11,
64
+ userId: 'to-be-flagged',
65
+ flagCount: 1,
66
+ };
67
+
68
+ export const flaggedUserQueryResponse = {
69
+ data: {
70
+ users: [flaggedUser],
71
+ files: [],
72
+ },
73
+ };
74
+
40
75
  export const userUpdateResponse = {
41
76
  data: {
42
77
  users: [
@@ -57,6 +92,7 @@ export const userUpdateResponse = {
57
92
  files: [],
58
93
  },
59
94
  };
95
+
60
96
  export const users = [user11, user12, user13, user14];
61
97
 
62
98
  // user payload with file