@amityco/ts-sdk 0.0.1-beta.43.1 → 0.0.1-beta.45.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 (53) hide show
  1. package/dist/category/api/queryCategories.d.ts.map +1 -1
  2. package/dist/comment/api/queryComments.d.ts.map +1 -1
  3. package/dist/community/api/queryCommunities.d.ts.map +1 -1
  4. package/dist/core/query/filtering.d.ts +10 -0
  5. package/dist/core/query/filtering.d.ts.map +1 -1
  6. package/dist/index.cjs.js +136 -114
  7. package/dist/index.esm.js +136 -115
  8. package/dist/index.umd.js +4 -4
  9. package/dist/message/api/queryMessages.d.ts +1 -17
  10. package/dist/message/api/queryMessages.d.ts.map +1 -1
  11. package/dist/message/utils/prepareMessagePayload.d.ts.map +1 -1
  12. package/dist/post/api/queryPosts.d.ts.map +1 -1
  13. package/dist/post/observers/livePosts.d.ts.map +1 -1
  14. package/dist/subChannel/api/querySubChannels.d.ts.map +1 -1
  15. package/dist/utils/inferIsDeleted.d.ts +2 -0
  16. package/dist/utils/inferIsDeleted.d.ts.map +1 -0
  17. package/dist/utils/tests/inferIsDeleted.test.d.ts +2 -0
  18. package/dist/utils/tests/inferIsDeleted.test.d.ts.map +1 -0
  19. package/package.json +1 -1
  20. package/src/category/api/queryCategories.ts +3 -1
  21. package/src/category/observers/liveCategories.ts +5 -5
  22. package/src/category/observers/tests/liveCategories.test.ts +7 -5
  23. package/src/channel/api/tests/removeChannelMembers.test.ts +3 -4
  24. package/src/channel/observers/liveChannelMembers.ts +4 -4
  25. package/src/channel/observers/liveChannels.ts +4 -4
  26. package/src/channel/observers/tests/liveChannels.test.ts +9 -15
  27. package/src/client/api/connectClient.ts +1 -1
  28. package/src/comment/api/queryComments.ts +3 -1
  29. package/src/comment/observers/liveComments.ts +5 -5
  30. package/src/community/api/queryCommunities.ts +2 -1
  31. package/src/community/api/test/queryCommunity.test.ts +2 -2
  32. package/src/community/observers/liveCommunities.ts +5 -5
  33. package/src/community/observers/liveCommunityMembers.ts +4 -4
  34. package/src/community/observers/tests/liveCommunities.test.ts +7 -7
  35. package/src/core/query/filtering.ts +38 -0
  36. package/src/follow/observers/liveFollowers.ts +4 -4
  37. package/src/follow/observers/liveFollowings.ts +4 -4
  38. package/src/message/api/queryMessages.ts +15 -50
  39. package/src/message/api/tests/queryMessages.test.ts +2 -37
  40. package/src/message/observers/liveMessages.ts +7 -7
  41. package/src/message/tests/observers/liveMessage.test.ts +1 -1
  42. package/src/message/tests/observers/liveMessages.test.ts +1 -1
  43. package/src/message/utils/prepareMessagePayload.ts +3 -1
  44. package/src/post/api/queryPosts.ts +3 -1
  45. package/src/post/observers/livePosts.ts +11 -6
  46. package/src/post/observers/tests/livePosts.test.ts +10 -4
  47. package/src/reaction/observers/liveReactions.ts +4 -4
  48. package/src/subChannel/api/querySubChannels.ts +3 -1
  49. package/src/subChannel/observers/liveSubChannels.ts +1 -1
  50. package/src/user/observers/liveUsers.ts +4 -4
  51. package/src/user/observers/tests/liveUsers.test.ts +3 -9
  52. package/src/utils/inferIsDeleted.ts +16 -0
  53. package/src/utils/tests/inferIsDeleted.test.ts +15 -0
@@ -2,7 +2,9 @@ import { getActiveClient } from '~/client/api';
2
2
 
3
3
  import { fireEvent } from '~/core/events';
4
4
  import { toPageRaw } from '~/core/query';
5
- import { pullFromCache } from '~/cache/api';
5
+ import { pushToCache } from '~/cache/api';
6
+ import { ingestInCache } from '~/cache/api/ingestInCache';
7
+ import { getResolver } from '~/core/model';
6
8
  import { convertQueryParams, prepareMessagePayload } from '../utils';
7
9
 
8
10
  /* begin_public_function
@@ -47,12 +49,18 @@ export const queryMessages = async (
47
49
 
48
50
  const cachedAt = client.cache && Date.now();
49
51
 
50
- // if (client.cache) {
51
- // ingestInCache(data, { cachedAt });
52
- //
53
- // const cacheKey = ['message', 'query', params as Amity.Serializable];
54
- // pushToCache(cacheKey, { messages: messages.map(getResolver('message')), paging });
55
- // }
52
+ if (client.cache) {
53
+ /*
54
+ * queryMessages.locally is unsupported for messages as message list updates
55
+ * frequently and leads to bugs when user switches between channels, i.e. when
56
+ * the public function (live messages) calls unmount. The list fetched on
57
+ * after from the cache is stale.
58
+ */
59
+ ingestInCache(data, { cachedAt });
60
+
61
+ const cacheKey = ['message', 'query', params as Amity.Serializable];
62
+ pushToCache(cacheKey, { messages: messages.map(getResolver('message')), paging });
63
+ }
56
64
 
57
65
  fireEvent('local.message.fetched', { messages });
58
66
 
@@ -62,46 +70,3 @@ export const queryMessages = async (
62
70
  return { data: messages, cachedAt, prevPage, nextPage };
63
71
  };
64
72
  /* end_public_function */
65
-
66
- /**
67
- * ```js
68
- * import { queryMessages } from '@amityco/ts-sdk'
69
- * const messages = queryMessages.locally!({ channelId })
70
- * ```
71
- *
72
- * Queries a paginable list of {@link Amity.Message} objects from cache
73
- *
74
- * @param query The query parameters
75
- * @returns A page of {@link Amity.Message} objects
76
- *
77
- * @category Message API
78
- */
79
- queryMessages.locally = (
80
- query: Parameters<typeof queryMessages>[0],
81
- ): Amity.Cached<Amity.Paged<Amity.Message, Amity.Page<string>>> | undefined => {
82
- const client = getActiveClient();
83
- client.log('message/queryMessages.locally', query);
84
-
85
- if (!client.cache) return;
86
-
87
- const params = convertQueryParams(query);
88
-
89
- const queryKey = ['message', 'query', params as Amity.Serializable];
90
- const { data, cachedAt } =
91
- pullFromCache<{ messages: Pick<Amity.Message, 'messageId'>[] } & Amity.Pagination>(queryKey) ??
92
- {};
93
-
94
- if (!data?.messages.length) return;
95
-
96
- const messages: Amity.Message[] = data.messages
97
- .map(messageId => pullFromCache<Amity.Message>(['message', 'get', messageId])!)
98
- .filter(Boolean)
99
- .map(({ data }) => data);
100
-
101
- const prevPage = toPageRaw(data?.paging.previous);
102
- const nextPage = toPageRaw(data?.paging.next);
103
-
104
- return messages.length === data?.messages?.length
105
- ? { data: messages, cachedAt, prevPage, nextPage }
106
- : undefined;
107
- };
@@ -106,48 +106,13 @@ describe('queryMessages', () => {
106
106
  expect(mockFn.mock.lastCall[1].params.isDeleted).toBe(false);
107
107
  });
108
108
 
109
- test('it should set isDeleted to undefined if includeDeleted is undefined', async () => {
109
+ test('it should set isDeleted to false if includeDeleted is undefined', async () => {
110
110
  const mockFn = jest.fn();
111
111
  client.http.get = mockFn.mockResolvedValue(getResolvedMessageValue());
112
112
 
113
113
  await queryMessages(queryCriteriaWithoutPage);
114
114
 
115
115
  expect(mockFn.mock.lastCall).toHaveLength(2);
116
- expect(mockFn.mock.lastCall[1].params.isDeleted).toBeUndefined();
117
- });
118
- });
119
-
120
- describe('queryMessages.locally', () => {
121
- beforeEach(() => enableCache());
122
- afterEach(() => disableCache());
123
-
124
- test('should return cached messages', () => {
125
- messagesToQuery.forEach(message => pushToCache(['message', 'get', message.messageId], message));
126
-
127
- pushToCache(['message', 'query', convertQueryParams(queryCriteria)], {
128
- messages: messageIds,
129
- paging,
130
- });
131
-
132
- expect(queryMessages.locally(queryCriteria)).toEqual(
133
- expect.objectContaining({
134
- data: messagesToQuery,
135
- prevPage: undefined,
136
- nextPage,
137
- }),
138
- );
139
- });
140
-
141
- test(`shouldn't return if individual message cache not exist`, () => {
142
- pushToCache(['message', 'query', convertQueryParams(queryCriteria)], {
143
- messages: messageIds,
144
- paging,
145
- });
146
-
147
- expect(queryMessages.locally(queryCriteria)).toBeUndefined();
148
- });
149
-
150
- test('it should return undefined if message not in cache', () => {
151
- expect(queryMessages.locally({ subChannelId: 'non-existent' })).toBeUndefined();
116
+ expect(mockFn.mock.lastCall[1].params.isDeleted).toBe(false);
152
117
  });
153
118
  });
@@ -22,7 +22,7 @@ import {
22
22
  onMessageUpdated,
23
23
  } from '../events';
24
24
  import { onMessageFetched } from '../events/onMessageFetched';
25
- import { queryMessages } from '../api';
25
+ import { queryMessages } from '../api/queryMessages';
26
26
 
27
27
  /**
28
28
  * ```js
@@ -76,7 +76,7 @@ export const liveMessages = (
76
76
  * for cases when message is deleted via RTE, this flag is used to get
77
77
  * items from cache that are !deleted
78
78
  */
79
- if (params.includeDeleted === false) {
79
+ if (!params.includeDeleted) {
80
80
  messages = filterByPropEquality(messages, 'isDeleted', false);
81
81
  }
82
82
 
@@ -101,16 +101,16 @@ export const liveMessages = (
101
101
  responder(collection);
102
102
  };
103
103
 
104
- const onFetch = () => {
104
+ const onFetch = (initial = false) => {
105
105
  const collection = pullFromCache<Amity.MessageLiveCollectionCache>(cacheKey)?.data;
106
106
 
107
107
  const messages = collection?.data ?? [];
108
108
 
109
- if (messages.length > 0 && !collection?.params?.page) return;
109
+ if (!initial && messages.length > 0 && !collection?.params?.page) return;
110
110
 
111
111
  const query = createQuery(queryMessages, {
112
112
  ...queryParams,
113
- page: collection?.params?.page ?? { limit },
113
+ page: (!initial ? collection?.params.page : undefined) ?? { limit },
114
114
  });
115
115
 
116
116
  runQuery(
@@ -147,11 +147,11 @@ export const liveMessages = (
147
147
  onMessageReactionRemoved(realtimeRouter('onReactionRemoved')),
148
148
  );
149
149
 
150
- onFetch();
150
+ onFetch(true);
151
+ disposers.push(() => dropFromCache(cacheKey));
151
152
 
152
153
  return () => {
153
154
  log(`liveMessages(tmpid: ${timestamp}) > dispose`);
154
155
  disposers.forEach(fn => fn());
155
- dropFromCache(cacheKey);
156
156
  };
157
157
  };
@@ -58,7 +58,7 @@ describe('liveMessage', () => {
58
58
  ];
59
59
 
60
60
  test.each(events)('%s', async (test, event) => {
61
- await disableCache();
61
+ disableCache();
62
62
  const snapshot = getSnapshot();
63
63
 
64
64
  // mock response of queryMessages and validate
@@ -201,7 +201,7 @@ describe('liveMessages', () => {
201
201
  },
202
202
  });
203
203
 
204
- liveMessages({ subChannelId }, callback);
204
+ liveMessages({ subChannelId, includeDeleted: true }, callback);
205
205
 
206
206
  expect(callback).toHaveBeenCalled();
207
207
 
@@ -4,6 +4,8 @@ import { toToken } from '~/core/query';
4
4
  import { getMessageMarkers } from '~/marker/api';
5
5
  import { prepareMessagePayloadForCache } from '~/reaction/utils/prepareMessagePayloadForCache';
6
6
 
7
+ import { inferIsDeleted } from '~/utils/inferIsDeleted';
8
+
7
9
  const getCachedMarker = (message: Amity.Message | Amity.RawMessage) => {
8
10
  const key = {
9
11
  creatorId: message.creatorId,
@@ -166,7 +168,7 @@ export function convertQueryParams({
166
168
  const out: RawQueryMessages = {
167
169
  ...rest,
168
170
  messageFeedId: subChannelId,
169
- isDeleted: includeDeleted === false ? false : undefined,
171
+ isDeleted: inferIsDeleted(includeDeleted),
170
172
  options: { token: toToken(page || { limit: 10 }, 'afterbeforeraw')! },
171
173
  };
172
174
 
@@ -5,7 +5,9 @@ import { toToken, toPageRaw } from '~/core/query';
5
5
  import { pullFromCache, pushToCache } from '~/cache/api';
6
6
  import { ingestInCache } from '~/cache/api/ingestInCache';
7
7
  import { getResolver } from '~/core/model';
8
+
8
9
  import { prepareMembershipPayload } from '~/group/utils';
10
+ import { inferIsDeleted } from '~/utils/inferIsDeleted';
9
11
 
10
12
  /* begin_public_function
11
13
  id: post.query, feed.query.community_feed, feed.query.user_feed
@@ -39,7 +41,7 @@ export const queryPosts = async (
39
41
  const { data } = await client.http.get<Amity.PostPayload & Amity.Pagination>(`/api/v4/posts`, {
40
42
  params: {
41
43
  ...params,
42
- isDeleted: includeDeleted === false ? false : undefined,
44
+ isDeleted: inferIsDeleted(includeDeleted),
43
45
  /*
44
46
  * when creating post like image, file, video BE will create 2 posts
45
47
  * 1. parent post to store text with dataType=text
@@ -7,6 +7,7 @@ import {
7
7
  createQuery,
8
8
  filterByFeedType,
9
9
  filterByPropEquality,
10
+ filterByPostDataTypes,
10
11
  queryOptions,
11
12
  runQuery,
12
13
  sortByFirstCreated,
@@ -89,7 +90,7 @@ export const livePosts = (
89
90
  const sortBy = params.sortBy ? params.sortBy : 'lastCreated';
90
91
  posts = posts.sort(sortBy === 'lastCreated' ? sortByLastCreated : sortByFirstCreated);
91
92
 
92
- if (params.includeDeleted === false) {
93
+ if (!params.includeDeleted) {
93
94
  posts = filterByPropEquality(posts, 'isDeleted', false);
94
95
  }
95
96
 
@@ -101,6 +102,10 @@ export const livePosts = (
101
102
  posts = filterByFeedType(posts, params.feedType);
102
103
  }
103
104
 
105
+ if (params.dataTypes?.length) {
106
+ posts = filterByPostDataTypes(posts, params.dataTypes);
107
+ }
108
+
104
109
  callback({
105
110
  onNextPage: onFetch,
106
111
  data: posts,
@@ -127,20 +132,21 @@ export const livePosts = (
127
132
  }
128
133
 
129
134
  pushToCache(cacheKey, collection);
135
+ disposers.push(() => dropFromCache(cacheKey));
130
136
 
131
137
  responder(collection);
132
138
  };
133
139
 
134
- const onFetch = () => {
140
+ const onFetch = (initial = false) => {
135
141
  const collection = pullFromCache<Amity.PostLiveCollectionCache>(cacheKey)?.data;
136
142
 
137
143
  const posts = collection?.data ?? [];
138
144
 
139
- if (posts.length > 0 && !collection?.params?.page) return;
145
+ if (!initial && posts.length > 0 && !collection?.params?.page) return;
140
146
 
141
147
  const query = createQuery(queryPosts, {
142
148
  ...queryParams,
143
- page: collection?.params?.page ?? { limit },
149
+ page: (!initial ? collection?.params.page : undefined) ?? { limit },
144
150
  });
145
151
 
146
152
  runQuery(
@@ -178,11 +184,10 @@ export const livePosts = (
178
184
  onPostReactionRemoved(realtimeRouter('onReactionRemoved')),
179
185
  );
180
186
 
181
- onFetch();
187
+ onFetch(true);
182
188
 
183
189
  return () => {
184
190
  log(`livePosts(tmpid: ${timestamp}) > dispose`);
185
191
  disposers.forEach(fn => fn());
186
- dropFromCache(cacheKey);
187
192
  };
188
193
  };
@@ -66,7 +66,7 @@ describe('livePosts', () => {
66
66
 
67
67
  const filters: [string, Amity.PostLiveCollection, number?][] = [
68
68
  ['not deleted', { ...params, includeDeleted: false }],
69
- ['tags', { ...params, tags: ['test_tag'] }, 1],
69
+ ['tags', { ...params, tags: ['test_tag'], includeDeleted: true }, 1],
70
70
  ];
71
71
 
72
72
  test.each(filters)(
@@ -104,7 +104,7 @@ describe('livePosts', () => {
104
104
  .mockResolvedValue(postQueryResponse)
105
105
  .mockResolvedValueOnce(postQueryResponsePage2);
106
106
 
107
- livePosts(params, callback);
107
+ livePosts({ ...params, includeDeleted: true }, callback);
108
108
 
109
109
  expect(callback).toHaveBeenCalled();
110
110
 
@@ -292,7 +292,10 @@ describe('livePosts', () => {
292
292
  const callback = jest.fn();
293
293
  client.http.get = jest.fn().mockResolvedValue(postQueryResponsePage2);
294
294
 
295
- livePosts({ ...params, targetType: 'community', feedType: 'reviewing' }, callback);
295
+ livePosts(
296
+ { ...params, targetType: 'community', feedType: 'reviewing', includeDeleted: true },
297
+ callback,
298
+ );
296
299
 
297
300
  await pause();
298
301
  const payload = postQueryResponsePage2.data.posts;
@@ -335,7 +338,10 @@ describe('livePosts', () => {
335
338
  const callback = jest.fn();
336
339
  client.http.get = jest.fn().mockResolvedValue(postQueryResponsePage2);
337
340
 
338
- livePosts({ ...params, targetType: 'community', feedType: 'published' }, callback);
341
+ livePosts(
342
+ { ...params, targetType: 'community', feedType: 'published', includeDeleted: true },
343
+ callback,
344
+ );
339
345
 
340
346
  await pause();
341
347
  const payload = postQueryResponsePage2.data.posts;
@@ -88,16 +88,16 @@ export const liveReactions = (
88
88
  responder(collection);
89
89
  };
90
90
 
91
- const onFetch = () => {
91
+ const onFetch = (initial = false) => {
92
92
  const collection = pullFromCache<Amity.ReactionLiveCollectionCache>(cacheKey)?.data;
93
93
 
94
94
  const reactions = collection?.data ?? [];
95
95
 
96
- if (reactions.length > 0 && !collection?.params?.page) return;
96
+ if (!initial && reactions.length > 0 && !collection?.params?.page) return;
97
97
 
98
98
  const query = createQuery(queryReactions, {
99
99
  ...queryParams,
100
- page: collection?.params?.page ?? { limit },
100
+ page: (!initial ? collection?.params.page : undefined) ?? { limit },
101
101
  });
102
102
 
103
103
  runQuery(
@@ -133,7 +133,7 @@ export const liveReactions = (
133
133
  ),
134
134
  );
135
135
 
136
- onFetch();
136
+ onFetch(true);
137
137
 
138
138
  return () => {
139
139
  log(`liveReactions(tmpid: ${timestamp}) > dispose`);
@@ -4,6 +4,8 @@ import { getActiveClient } from '~/client/api';
4
4
  import { getResolver } from '~/core/model';
5
5
  import { toPage, toToken } from '~/core/query';
6
6
  import { fireEvent } from '~/core/events';
7
+
8
+ import { inferIsDeleted } from '~/utils/inferIsDeleted';
7
9
  import { convertQueryParams, prepareSubChannelPayload } from '../utils';
8
10
 
9
11
  /* begin_public_function
@@ -36,7 +38,7 @@ export const querySubChannels = async (
36
38
  {
37
39
  params: {
38
40
  ...params,
39
- isDeleted: includeDeleted === false ? false : undefined,
41
+ isDeleted: inferIsDeleted(includeDeleted),
40
42
  options: {
41
43
  token: toToken(page, 'skiplimit'),
42
44
  },
@@ -59,7 +59,7 @@ export const liveSubChannels = (
59
59
  const responder = (data: Amity.SubChannelLiveCollectionCache) => {
60
60
  let subChannels: Amity.SubChannel[] = data.data;
61
61
 
62
- if (params.includeDeleted === false) {
62
+ if (!params.includeDeleted) {
63
63
  subChannels = filterByPropEquality(subChannels, 'isDeleted', false);
64
64
  }
65
65
 
@@ -111,16 +111,16 @@ export const liveUsers = (
111
111
  responder(collection);
112
112
  };
113
113
 
114
- const onFetch = () => {
114
+ const onFetch = (initial = false) => {
115
115
  const collection = pullFromCache<Amity.UserLiveCollectionCache>(cacheKey)?.data;
116
116
 
117
117
  const users = collection?.data ?? [];
118
118
 
119
- if (users.length > 0 && !collection?.params.page) return;
119
+ if (!initial && users.length > 0 && !collection?.params.page) return;
120
120
 
121
121
  const query = createQuery(queryUsers, {
122
122
  ...queryParams,
123
- page: collection?.params.page ?? { limit },
123
+ page: (!initial ? collection?.params.page : undefined) ?? { limit },
124
124
  });
125
125
 
126
126
  runQuery(
@@ -153,7 +153,7 @@ export const liveUsers = (
153
153
  onUserFlagCleared(realtimeRouter('onFlagCleared')),
154
154
  );
155
155
 
156
- onFetch();
156
+ onFetch(true);
157
157
 
158
158
  return () => {
159
159
  log(`liveUsers(tmpid: ${timestamp}) > dispose`);
@@ -65,17 +65,11 @@ describe('liveUsers', () => {
65
65
  liveUsers(params, callback);
66
66
  await pause();
67
67
 
68
- expect(callback).toHaveBeenCalledTimes(2);
68
+ // The second `getUsers` call fetches data from the cache,
69
+ // so there is no loading state and the callback only fires once.
70
+ expect(callback).toHaveBeenCalledTimes(1);
69
71
  expect(callback).toHaveBeenNthCalledWith(
70
72
  1,
71
- expect.objectContaining(
72
- getSnapshot({
73
- data: payload.users,
74
- }),
75
- ),
76
- );
77
- expect(callback).toHaveBeenNthCalledWith(
78
- 2,
79
73
  expect.objectContaining(
80
74
  getSnapshot({
81
75
  data: payload.users,
@@ -0,0 +1,16 @@
1
+ /*
2
+ * This is a simple utility that infers the value of isDeleted based on the
3
+ * value of includeDeleted
4
+ *
5
+ * There are two important things to note here:
6
+ * 1. `includeDeleted` is purely client side query param and not recognized by
7
+ * the server
8
+ * 2. The only values we wish to expose with regards to `isDeleted` (the server
9
+ * param for queries) is false | undefined and want to disallow users to query
10
+ * for deleted entities
11
+ *
12
+ * Although this is a very simple utility, it's only purpose is to keep things
13
+ * DRY
14
+ */
15
+ export const inferIsDeleted = (includeDeleted?: boolean): boolean | undefined =>
16
+ includeDeleted === true ? undefined : false;
@@ -0,0 +1,15 @@
1
+ import { inferIsDeleted } from '../inferIsDeleted';
2
+
3
+ describe('utils > inferIsDeleted', () => {
4
+ test('it should return false if includeDeleted false', () => {
5
+ expect(inferIsDeleted(false)).toBe(false);
6
+ });
7
+
8
+ test('it should return false if includeDeleted undefined', () => {
9
+ expect(inferIsDeleted(undefined)).toBe(false);
10
+ });
11
+
12
+ test('it should return undefined if includeDeleted true', () => {
13
+ expect(inferIsDeleted(true)).toBeUndefined();
14
+ });
15
+ });