@amityco/ts-sdk 6.3.1 → 6.3.2-3fa3a47.0

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 (50) hide show
  1. package/dist/@types/domains/channel.d.ts +1 -1
  2. package/dist/@types/domains/channel.d.ts.map +1 -1
  3. package/dist/@types/domains/user.d.ts +1 -0
  4. package/dist/@types/domains/user.d.ts.map +1 -1
  5. package/dist/channelRepsitory/channelMembership/observers/getMembers.d.ts.map +1 -1
  6. package/dist/client/api/logout.d.ts.map +1 -1
  7. package/dist/client/api/setSessionState.d.ts.map +1 -1
  8. package/dist/commentRepository/api/tests/deleteComment.test.d.ts +2 -0
  9. package/dist/commentRepository/api/tests/deleteComment.test.d.ts.map +1 -0
  10. package/dist/communityRepository/communityMembership/observers/getMembers.d.ts.map +1 -1
  11. package/dist/core/model/index.d.ts +1 -1
  12. package/dist/core/model/index.d.ts.map +1 -1
  13. package/dist/core/transports/mqtt.d.ts.map +1 -1
  14. package/dist/index.cjs.js +179 -75
  15. package/dist/index.esm.js +179 -75
  16. package/dist/index.umd.js +2 -2
  17. package/dist/userRepository/observers/getUsers.d.ts.map +1 -1
  18. package/dist/userRepository/observers/index.d.ts +1 -0
  19. package/dist/userRepository/observers/index.d.ts.map +1 -1
  20. package/dist/userRepository/observers/searchUserByDisplayName.d.ts +19 -0
  21. package/dist/userRepository/observers/searchUserByDisplayName.d.ts.map +1 -0
  22. package/dist/userRepository/observers/tests/searchUserByDisplayName.test.d.ts +2 -0
  23. package/dist/userRepository/observers/tests/searchUserByDisplayName.test.d.ts.map +1 -0
  24. package/dist/utils/isEqual.d.ts +17 -0
  25. package/dist/utils/isEqual.d.ts.map +1 -0
  26. package/dist/utils/liveObject.d.ts.map +1 -1
  27. package/dist/utils/tests/isEquals.test.d.ts +2 -0
  28. package/dist/utils/tests/isEquals.test.d.ts.map +1 -0
  29. package/package.json +1 -1
  30. package/src/@types/domains/channel.ts +1 -1
  31. package/src/@types/domains/user.ts +2 -0
  32. package/src/channelRepsitory/channelMembership/observers/getMembers.ts +17 -15
  33. package/src/channelRepsitory/channelMembership/observers/tests/getMembers.test.ts +0 -62
  34. package/src/channelRepsitory/channelMembership/observers/tests/searchMembers.test.ts +0 -31
  35. package/src/client/api/logout.ts +13 -10
  36. package/src/client/api/setSessionState.ts +7 -2
  37. package/src/commentRepository/api/tests/deleteComment.test.ts +60 -0
  38. package/src/communityRepository/communityMembership/observers/getMembers.ts +14 -10
  39. package/src/communityRepository/communityMembership/observers/tests/getMembers.test.ts +1 -27
  40. package/src/core/model/index.ts +2 -11
  41. package/src/core/transports/mqtt.ts +14 -24
  42. package/src/messageRepository/observers/getMessages.ts +1 -1
  43. package/src/userRepository/observers/getUsers.ts +18 -11
  44. package/src/userRepository/observers/index.ts +1 -0
  45. package/src/userRepository/observers/searchUserByDisplayName.ts +30 -0
  46. package/src/userRepository/observers/tests/getUsers.test.ts +1 -29
  47. package/src/userRepository/observers/tests/searchUserByDisplayName.test.ts +113 -0
  48. package/src/utils/isEqual.ts +69 -0
  49. package/src/utils/liveObject.ts +21 -2
  50. package/src/utils/tests/isEquals.test.ts +86 -0
@@ -29,7 +29,7 @@ import {
29
29
  } from '../events';
30
30
 
31
31
  /* begin_public_function
32
- id: user.query, user.search
32
+ id: user.query
33
33
  */
34
34
  /**
35
35
  * ```js
@@ -69,14 +69,8 @@ export const getUsers = (
69
69
  const disposers: Amity.Unsubscriber[] = [];
70
70
  const cacheKey = ['user', 'collection', {}];
71
71
 
72
- const responder = (data: Amity.UserLiveCollectionCache) => {
73
- let users: Amity.User[] =
74
- data.data
75
- .map(userId => pullFromCache<Amity.User>(['user', 'get', userId])!)
76
- .filter(Boolean)
77
- .map(({ data }) => data) ?? [];
78
-
79
- users = filterByPropEquality(users, 'displayName', params.displayName);
72
+ const applyFilter = (data: Amity.User[]): Amity.User[] => {
73
+ let users = filterByPropEquality(data, 'displayName', params.displayName);
80
74
 
81
75
  switch (params.sortBy) {
82
76
  case 'firstCreated':
@@ -95,9 +89,22 @@ export const getUsers = (
95
89
  .sort(sortByDisplayName);
96
90
  }
97
91
 
92
+ return users;
93
+ };
94
+
95
+ const responder = (data: Amity.UserLiveCollectionCache, isEventModel = false) => {
96
+ const users: Amity.User[] =
97
+ data.data
98
+ .map(userId => pullFromCache<Amity.User>(['user', 'get', userId])!)
99
+ .filter(Boolean)
100
+ .map(({ data }) => data) ?? [];
101
+
98
102
  callback({
99
103
  onNextPage: onFetch,
100
- data: users,
104
+ /*
105
+ * Only apply filter to RTE Model
106
+ */
107
+ data: isEventModel ? applyFilter(users) : users,
101
108
  hasNextPage: !!data.params?.page,
102
109
  loading: data.loading,
103
110
  error: data.error,
@@ -111,7 +118,7 @@ export const getUsers = (
111
118
  collection.data = [...new Set([user.userId, ...collection.data])];
112
119
 
113
120
  pushToCache(cacheKey, collection);
114
- responder(collection);
121
+ responder(collection, true);
115
122
  };
116
123
 
117
124
  const onFetch = (initial = false) => {
@@ -2,3 +2,4 @@ export { getUser } from './getUser';
2
2
  export { getUsers } from './getUsers';
3
3
  export { observeUser } from './observeUser';
4
4
  export { getBlockedUsers } from './getBlockedUsers';
5
+ export { searchUserByDisplayName } from './searchUserByDisplayName';
@@ -0,0 +1,30 @@
1
+ import { getUsers } from './getUsers';
2
+
3
+ /* begin_public_function
4
+ id: user.search
5
+ */
6
+ /**
7
+ * ```js
8
+ * import { UserRepository } from '@amityco/ts-sdk'
9
+ *
10
+ * let users = []
11
+ * const unsub = UserRepository.searchUserByDisplayName({}, response => merge(users, response.data))
12
+ * ```
13
+ *
14
+ * Observe all mutations on a list of {@link Amity.User}s
15
+ *
16
+ * @param params for searching users
17
+ * @param callback the function to call when new data are available
18
+ * @param config the configuration for the live collection
19
+ * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the users
20
+ *
21
+ * @category Category Live Collection
22
+ */
23
+ export const searchUserByDisplayName = (
24
+ params: Amity.UserSearchLiveCollection,
25
+ callback: Amity.LiveCollectionCallback<Amity.User>,
26
+ config?: Amity.LiveCollectionConfig,
27
+ ) => {
28
+ return getUsers({ ...params, filter: 'all' }, callback, config);
29
+ };
30
+ /* end_public_function */
@@ -6,7 +6,6 @@ import {
6
6
  pause,
7
7
  user11,
8
8
  user12,
9
- user21,
10
9
  userQueryResponse,
11
10
  userQueryResponsePage2,
12
11
  } from '~/utils/tests';
@@ -114,6 +113,7 @@ describe('getUsers', () => {
114
113
  });
115
114
 
116
115
  describe('events', () => {
116
+ // integration_test_id: 60b4b354-efca-49bc-aa8b-d63a8da6ef31
117
117
  const updatedUser = { ...user11, displayName: 'updated-user' };
118
118
  const flaggedUser = { ...user11, flagCount: 1 };
119
119
  const unflaggedUser = { ...user11, flagCount: 1 };
@@ -167,32 +167,4 @@ describe('getUsers', () => {
167
167
  );
168
168
  });
169
169
  });
170
-
171
- // integration_test_id: 60b4b354-efca-49bc-aa8b-d63a8da6ef31
172
- const filters: [string, Amity.UserLiveCollection, Amity.User[]][] = [
173
- ['displayName', { displayName: 'displayName' }, [user21]],
174
- ];
175
-
176
- test.each(filters)('it should filter by %s communities', async (filter, params, expected) => {
177
- const callback = jest.fn();
178
- client.http.get = jest.fn().mockResolvedValue(userQueryResponsePage2);
179
-
180
- getUsers(params, callback);
181
-
182
- expect(callback).toHaveBeenCalledTimes(1);
183
- // check if cache data returned (should be empty)
184
- expect(callback).toHaveBeenCalledWith(expect.objectContaining(getSnapshot()));
185
-
186
- await pause();
187
-
188
- expect(callback).toHaveBeenCalledTimes(2);
189
- expect(callback).toHaveBeenCalledWith(
190
- expect.objectContaining(
191
- getSnapshot({
192
- loading: false,
193
- data: expected,
194
- }),
195
- ),
196
- );
197
- });
198
170
  });
@@ -0,0 +1,113 @@
1
+ import { disableCache, enableCache } from '~/cache/api';
2
+ import {
3
+ client,
4
+ connectClient,
5
+ disconnectClient,
6
+ pause,
7
+ user21,
8
+ userQueryResponse,
9
+ userQueryResponsePage2,
10
+ } from '~/utils/tests';
11
+
12
+ import { searchUserByDisplayName } from '../searchUserByDisplayName';
13
+
14
+ const getSnapshot = (params?: Record<string, any>) => {
15
+ return {
16
+ data: [] as Amity.User[],
17
+ loading: true,
18
+ error: undefined as any,
19
+ ...params,
20
+ };
21
+ };
22
+
23
+ const { paging, ...payload } = userQueryResponse.data;
24
+ const { paging: paging2, ...payload2 } = userQueryResponsePage2.data;
25
+
26
+ describe('searchUserByDisplayName', () => {
27
+ beforeAll(connectClient);
28
+ afterAll(disconnectClient);
29
+
30
+ beforeEach(enableCache);
31
+
32
+ afterEach(disableCache);
33
+
34
+ const params = { displayName: user21.displayName } as Amity.UserLiveCollection;
35
+
36
+ // integration_test_id: c7cb9776-5c72-4abe-9b3c-572d74d534fe
37
+ test('it should return users collection', async () => {
38
+ const callback = jest.fn();
39
+ client.http.get = jest.fn().mockResolvedValue(userQueryResponse);
40
+
41
+ searchUserByDisplayName(params, callback);
42
+ await pause();
43
+
44
+ expect(callback).toHaveBeenCalledTimes(2);
45
+ expect(callback).toHaveBeenNthCalledWith(1, expect.objectContaining(getSnapshot()));
46
+ expect(callback).toHaveBeenNthCalledWith(
47
+ 2,
48
+ expect.objectContaining(
49
+ getSnapshot({
50
+ data: payload.users,
51
+ loading: false,
52
+ }),
53
+ ),
54
+ );
55
+ });
56
+
57
+ test('it should return data from cache', async () => {
58
+ const callback = jest.fn();
59
+ client.http.get = jest.fn().mockResolvedValue(userQueryResponse);
60
+
61
+ searchUserByDisplayName(params, () => undefined);
62
+ await pause();
63
+ searchUserByDisplayName(params, callback);
64
+ await pause();
65
+
66
+ // The second `searchUserByDisplayName` call fetches data from the cache,
67
+ // so there is no loading state and the callback only fires once.
68
+ expect(callback).toHaveBeenCalledTimes(1);
69
+ expect(callback).toHaveBeenNthCalledWith(
70
+ 1,
71
+ expect.objectContaining(
72
+ getSnapshot({
73
+ data: payload.users,
74
+ loading: false,
75
+ }),
76
+ ),
77
+ );
78
+ });
79
+
80
+ test('it should return method to fetch next page', async () => {
81
+ const callback = jest.fn();
82
+ client.http.get = jest
83
+ .fn()
84
+ .mockResolvedValue(userQueryResponse)
85
+ .mockResolvedValueOnce(userQueryResponsePage2);
86
+
87
+ searchUserByDisplayName(params, callback);
88
+ await pause();
89
+
90
+ expect(callback).toHaveBeenCalled();
91
+ expect(callback.mock.lastCall).toHaveLength(1);
92
+
93
+ const { onNextPage, hasNextPage } = callback.mock.lastCall[0];
94
+
95
+ expect(hasNextPage).toBe(true);
96
+ expect(onNextPage).toBeTruthy();
97
+
98
+ onNextPage();
99
+ await pause();
100
+
101
+ // 4 -> because 1 local & server call each per call (2)
102
+ expect(callback).toHaveBeenCalledTimes(4);
103
+ expect(callback).toHaveBeenNthCalledWith(
104
+ 4,
105
+ expect.objectContaining(
106
+ getSnapshot({
107
+ loading: false,
108
+ data: [...payload2.users, ...payload.users],
109
+ }),
110
+ ),
111
+ );
112
+ });
113
+ });
@@ -0,0 +1,69 @@
1
+ /**
2
+ * ```js
3
+ * import { isEqual } from '~/utils/isEqual'
4
+ * const isEqual = isEqual(post1, post2)
5
+ * ```
6
+ *
7
+ * Compares two Amity.Model
8
+ *
9
+ * @param x the Amity.Model to compare
10
+ * @param y the Amity.Model to compare wit x
11
+ * @returns a boolean based on equality
12
+ *
13
+ * @category utility
14
+ * @private
15
+ */
16
+ export function isEqual(x: any, y: any): boolean {
17
+ if (x === null || x === undefined || y === null || y === undefined) {
18
+ return x === y;
19
+ }
20
+
21
+ // after this just checking type of one would be enough
22
+ if (x.constructor !== y.constructor) {
23
+ return false;
24
+ }
25
+
26
+ // if they are functions, they should exactly refer to same one (because of closures)
27
+ if (x instanceof Function) {
28
+ return x === y;
29
+ }
30
+
31
+ // if they are regexps, they should exactly refer to same one
32
+ if (x instanceof RegExp) {
33
+ return x === y;
34
+ }
35
+
36
+ if (x === y || x.valueOf() === y.valueOf()) {
37
+ return true;
38
+ }
39
+
40
+ if (Array.isArray(x) && x.length !== y.length) {
41
+ return false;
42
+ }
43
+
44
+ // if they are dates, they must had equal valueOf
45
+ if (x instanceof Date) {
46
+ return false;
47
+ }
48
+
49
+ // if they are strictly equal, they both need to be object at least
50
+ if (!(x instanceof Object)) {
51
+ return false;
52
+ }
53
+
54
+ if (!(y instanceof Object)) {
55
+ return false;
56
+ }
57
+
58
+ // recursive object equality check
59
+ const p = Object.keys(x);
60
+ return (
61
+ Object.keys(y).every(i => {
62
+ // @ts-ignore
63
+ return p.indexOf(i) !== -1;
64
+ }) &&
65
+ p.every(i => {
66
+ return isEqual(x[i], y[i]);
67
+ })
68
+ );
69
+ }
@@ -8,6 +8,8 @@ import { hasUpdates } from '~/core/model';
8
8
  import { createQuery, runQuery } from '~/core/query';
9
9
  import { ASCApiError } from '~/core/errors';
10
10
 
11
+ import { isEqual } from './isEqual';
12
+
11
13
  export const liveObject = <
12
14
  T extends Amity.Models[Amity.Domain],
13
15
  K extends keyof T,
@@ -46,8 +48,25 @@ export const liveObject = <
46
48
  return;
47
49
  }
48
50
 
49
- if (model && !hasUpdates(model, eventModel)) {
50
- return;
51
+ if (model) {
52
+ /*
53
+ * This test is required as follow does not extend Amity.Timestamps
54
+ * Also, follow can have an event where the paylod is not updated i.e.
55
+ * when a request is declined
56
+ */
57
+ if ('updatedAt' in model && 'updatedAt' in eventModel) {
58
+ /*
59
+ * NOTE: the isEqual is added as a fail safe as the server does not update
60
+ * the updatedAt when the model updates rather when the data in the data of
61
+ * the model (if any) updates.
62
+ *
63
+ * Ex: post.addReaction | post.removeReaction does not update the updatedAt
64
+ * of the post
65
+ */
66
+ if (!hasUpdates(model, eventModel) || isEqual(model, eventModel)) {
67
+ return;
68
+ }
69
+ }
51
70
  }
52
71
 
53
72
  dispatcher({ loading: false, data: eventModel, origin: 'event' });
@@ -0,0 +1,86 @@
1
+ import { isEqual } from '../isEqual';
2
+
3
+ describe('utils > isEqual', () => {
4
+ describe('isEqual > primitive types', () => {
5
+ test('it should return true if both objects are null', () => {
6
+ expect(isEqual(null, null)).toBe(true);
7
+ });
8
+
9
+ test('it should return false if one of the object is undefined', () => {
10
+ expect(isEqual(null, undefined)).toBe(false);
11
+ });
12
+
13
+ test('it should return false if x & y regexps that dont to the same instance', () => {
14
+ expect(isEqual(/abc/, /abc/)).toBe(false);
15
+ });
16
+
17
+ test('it should return false if x & y are not the same regexps', () => {
18
+ expect(isEqual(/abc/, /123/)).toBe(false);
19
+ });
20
+
21
+ test('it should return true if x & y are regexps with the same instance', () => {
22
+ const regEx = /123/;
23
+ expect(isEqual(regEx, regEx)).toBe(true);
24
+ });
25
+
26
+ test('it should return true if x & y are the same string', () => {
27
+ expect(isEqual('test', 'test')).toBe(true);
28
+ });
29
+
30
+ test('it should return true if x & y are the same number', () => {
31
+ expect(isEqual(5, 5)).toBe(true);
32
+ });
33
+
34
+ test('it should return false if x & y are not the same number', () => {
35
+ expect(isEqual(5, 6)).toBe(false);
36
+ });
37
+ });
38
+
39
+ describe('isEqual > complex types', () => {
40
+ describe('- arrays', () => {
41
+ test('it should return true if x & y are empty arrays', () => {
42
+ expect(isEqual([], [])).toBe(true);
43
+ });
44
+
45
+ test('it should return true if x & y are the same array', () => {
46
+ expect(isEqual([1, 'hi'], [1, 'hi'])).toBe(true);
47
+ });
48
+
49
+ test('it should return false if x & y are not the same array', () => {
50
+ expect(isEqual([1, 'hi'], ['hi', 1])).toBe(false);
51
+ });
52
+
53
+ test('it should return false if x & y are only partially similar', () => {
54
+ expect(isEqual([1, 'hi'], [1, 'hi', 'fail'])).toBe(false);
55
+ });
56
+ });
57
+
58
+ describe('- objects', () => {
59
+ test('it should return true if x & y are empty objects', () => {
60
+ expect(isEqual({}, {})).toBe(true);
61
+ });
62
+
63
+ test('it should return true if x & y are the same object', () => {
64
+ expect(isEqual({ a: 1, b: 2 }, { a: 1, b: 2 })).toBe(true);
65
+ });
66
+
67
+ test('it should return false if x & y are the same nested object', () => {
68
+ expect(
69
+ isEqual(
70
+ { 1: { name: 'mhc', age: 28 }, 2: { name: 'arb', age: 26 } },
71
+ { 1: { name: 'mhc', age: 28 }, 2: { name: 'arb', age: 26 } },
72
+ ),
73
+ ).toBe(true);
74
+ });
75
+
76
+ test('it should return false if x & y are not the same nested object', () => {
77
+ expect(
78
+ isEqual(
79
+ { 1: { name: 'mhc', age: 28 }, 2: { name: 'arb', age: 26 } },
80
+ { 1: { name: 'mhc', age: 28 }, 2: { name: 'arb', age: 27 } },
81
+ ),
82
+ ).toBe(false);
83
+ });
84
+ });
85
+ });
86
+ });