@amityco/ts-sdk 7.32.1-d7637d9.0 → 7.32.1-e3468d0b.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 (40) hide show
  1. package/dist/@types/domains/content.d.ts +8 -2
  2. package/dist/@types/domains/content.d.ts.map +1 -1
  3. package/dist/@types/domains/feed.d.ts +4 -1
  4. package/dist/@types/domains/feed.d.ts.map +1 -1
  5. package/dist/@types/domains/post.d.ts +11 -0
  6. package/dist/@types/domains/post.d.ts.map +1 -1
  7. package/dist/cache/api/dropFromCache.d.ts.map +1 -1
  8. package/dist/cache/api/pushToCache.d.ts.map +1 -1
  9. package/dist/cache/api/queryCache.d.ts.map +1 -1
  10. package/dist/cache/api/queryCacheByProperty.d.ts +3 -0
  11. package/dist/cache/api/queryCacheByProperty.d.ts.map +1 -0
  12. package/dist/cache/api/tests/queryCache.test.d.ts +2 -0
  13. package/dist/cache/api/tests/queryCache.test.d.ts.map +1 -0
  14. package/dist/cache/utils/cacheIndex.d.ts +15 -0
  15. package/dist/cache/utils/cacheIndex.d.ts.map +1 -0
  16. package/dist/index.cjs.js +162 -54
  17. package/dist/index.esm.js +162 -54
  18. package/dist/index.umd.js +2 -2
  19. package/dist/messagePreview/utils/getSubChannelMessagePreviewWithUser.d.ts +1 -1
  20. package/dist/messageRepository/utils/getMessageFromMainDB.d.ts.map +1 -1
  21. package/dist/messageRepository/utils/prepareMessagePayload.d.ts.map +1 -1
  22. package/dist/postRepository/api/createMixedMediaPost.d.ts +6 -6
  23. package/dist/postRepository/api/createMixedMediaPost.d.ts.map +1 -1
  24. package/dist/postRepository/api/createPost.d.ts +1 -4
  25. package/dist/postRepository/api/createPost.d.ts.map +1 -1
  26. package/dist/postRepository/api/editPost.d.ts +4 -5
  27. package/dist/postRepository/api/editPost.d.ts.map +1 -1
  28. package/dist/postRepository/api/tests/gifPost.test.d.ts +2 -0
  29. package/dist/postRepository/api/tests/gifPost.test.d.ts.map +1 -0
  30. package/dist/postRepository/observers/getPosts.d.ts +4 -0
  31. package/dist/postRepository/observers/getPosts.d.ts.map +1 -1
  32. package/dist/postRepository/utils/attachments.d.ts +8 -0
  33. package/dist/postRepository/utils/attachments.d.ts.map +1 -0
  34. package/dist/utils/linkedObject/channelLinkedObject.d.ts.map +1 -1
  35. package/dist/utils/linkedObject/tests/channelCachePerformance.test.d.ts +2 -0
  36. package/dist/utils/linkedObject/tests/channelCachePerformance.test.d.ts.map +1 -0
  37. package/dist/utils/postTypePredicate.d.ts +1 -0
  38. package/dist/utils/postTypePredicate.d.ts.map +1 -1
  39. package/dist/utils/tests/dummy/comment.d.ts +1 -1
  40. package/package.json +1 -1
package/dist/index.esm.js CHANGED
@@ -177,6 +177,7 @@ const PostContentType = Object.freeze({
177
177
  AUDIO: 'audio',
178
178
  ROOM: 'room',
179
179
  EVENT: 'event',
180
+ GIF: 'gif',
180
181
  });
181
182
  const PostStructureType = Object.freeze({
182
183
  TEXT: 'text',
@@ -186,6 +187,8 @@ const PostStructureType = Object.freeze({
186
187
  AUDIO: 'audio',
187
188
  MIXED: 'mixed',
188
189
  EVENT: 'event',
190
+ /** A post that holds only GIFs. GIFs beside other media make the post `mixed`. */
191
+ GIF: 'gif',
189
192
  });
190
193
 
191
194
  var InvitationTypeEnum;
@@ -1168,6 +1171,116 @@ const wipeCache = async (storageKey = 'amitySdk') => {
1168
1171
  return true;
1169
1172
  };
1170
1173
 
1174
+ // Indexes never enter persisted cache data. Replacing data (restore/wipe/enable) starts a new index.
1175
+ const indexes = new WeakMap();
1176
+ const prefixesFor = (key) => {
1177
+ if (typeof key[0] !== 'string')
1178
+ return [];
1179
+ const prefixes = [JSON.stringify([key[0]])];
1180
+ if (typeof key[1] === 'string')
1181
+ prefixes.push(JSON.stringify([key[0], key[1]]));
1182
+ return prefixes;
1183
+ };
1184
+ const removePropertyValue = (property, indexed) => {
1185
+ const previous = property.previousValues.get(indexed.encodedKey);
1186
+ if (previous === undefined)
1187
+ return;
1188
+ const matches = property.values.get(previous);
1189
+ matches.delete(indexed);
1190
+ if (!matches.size)
1191
+ property.values.delete(previous);
1192
+ property.previousValues.delete(indexed.encodedKey);
1193
+ };
1194
+ const addPropertyValue = (data, property, indexed) => {
1195
+ var _a;
1196
+ if (!partialMatch(property.prefix, indexed.key))
1197
+ return;
1198
+ const value = (_a = data[indexed.encodedKey].data) === null || _a === void 0 ? void 0 : _a[property.property];
1199
+ if (typeof value !== 'string')
1200
+ return;
1201
+ let matches = property.values.get(value);
1202
+ if (!matches) {
1203
+ matches = new Set();
1204
+ property.values.set(value, matches);
1205
+ }
1206
+ matches.add(indexed);
1207
+ property.previousValues.set(indexed.encodedKey, value);
1208
+ };
1209
+ const addKey = (index, encodedKey) => {
1210
+ const indexed = { encodedKey, key: decodeKey(encodedKey), order: index.nextOrder };
1211
+ index.nextOrder += 1; // eslint-disable-line no-param-reassign
1212
+ index.keys.set(encodedKey, indexed);
1213
+ prefixesFor(indexed.key).forEach(prefix => {
1214
+ let keys = index.prefixes.get(prefix);
1215
+ if (!keys) {
1216
+ keys = new Map();
1217
+ index.prefixes.set(prefix, keys);
1218
+ }
1219
+ keys.set(encodedKey, indexed);
1220
+ });
1221
+ return indexed;
1222
+ };
1223
+ const getIndex = (data) => {
1224
+ let index = indexes.get(data);
1225
+ if (!index) {
1226
+ index = { keys: new Map(), prefixes: new Map(), properties: new Map(), nextOrder: 0 };
1227
+ Object.keys(data).forEach(encodedKey => addKey(index, encodedKey));
1228
+ indexes.set(data, index);
1229
+ }
1230
+ return index;
1231
+ };
1232
+ const candidatesFor = (index, key) => {
1233
+ var _a, _b;
1234
+ const prefixes = prefixesFor(key);
1235
+ if (!prefixes.length)
1236
+ return index.keys.values();
1237
+ return (_b = (_a = index.prefixes.get(prefixes[prefixes.length - 1])) === null || _a === void 0 ? void 0 : _a.values()) !== null && _b !== void 0 ? _b : [];
1238
+ };
1239
+ /** @hidden */
1240
+ const getMatchingCacheKeys = (data, key) => Array.from(candidatesFor(getIndex(data), key)).filter(indexed => partialMatch(key, indexed.key));
1241
+ /** Update only an index that has already been used; writes alone need no index build. @hidden */
1242
+ const updateCacheIndex = (data, encodedKey) => {
1243
+ var _a;
1244
+ const index = indexes.get(data);
1245
+ if (!index)
1246
+ return;
1247
+ const indexed = (_a = index.keys.get(encodedKey)) !== null && _a !== void 0 ? _a : addKey(index, encodedKey);
1248
+ index.properties.forEach(property => {
1249
+ removePropertyValue(property, indexed);
1250
+ addPropertyValue(data, property, indexed);
1251
+ });
1252
+ };
1253
+ /** @hidden */
1254
+ const removeFromCacheIndex = (data, encodedKey) => {
1255
+ const index = indexes.get(data);
1256
+ const indexed = index === null || index === void 0 ? void 0 : index.keys.get(encodedKey);
1257
+ if (!index || !indexed)
1258
+ return;
1259
+ index.properties.forEach(property => removePropertyValue(property, indexed));
1260
+ prefixesFor(indexed.key).forEach(prefix => {
1261
+ const keys = index.prefixes.get(prefix);
1262
+ keys.delete(encodedKey);
1263
+ if (!keys.size)
1264
+ index.prefixes.delete(prefix);
1265
+ });
1266
+ index.keys.delete(encodedKey);
1267
+ };
1268
+ /** Index a string model property within one domain/operation, retaining cache insertion order. @hidden */
1269
+ const getCacheEntriesByProperty = (data, prefix, property, value) => {
1270
+ var _a;
1271
+ const index = getIndex(data);
1272
+ const identity = JSON.stringify([prefix, property]);
1273
+ let propertyIndex = index.properties.get(identity);
1274
+ if (!propertyIndex) {
1275
+ propertyIndex = { prefix, property, values: new Map(), previousValues: new Map() };
1276
+ index.properties.set(identity, propertyIndex);
1277
+ Array.from(candidatesFor(index, prefix)).forEach(indexed => addPropertyValue(data, propertyIndex, indexed));
1278
+ }
1279
+ return Array.from((_a = propertyIndex.values.get(value)) !== null && _a !== void 0 ? _a : [])
1280
+ .sort((a, b) => a.order - b.order)
1281
+ .map(({ encodedKey }) => data[encodedKey]);
1282
+ };
1283
+
1171
1284
  /**
1172
1285
  * ```js
1173
1286
  * import { queryCache } from '@amityco/ts-sdk'
@@ -1188,12 +1301,7 @@ const queryCache = (key) => {
1188
1301
  if (!cache)
1189
1302
  return;
1190
1303
  log('cache/api/queryCache', { key });
1191
- return Object.keys(cache.data)
1192
- .filter(stringKey => {
1193
- const decodedKey = decodeKey(stringKey);
1194
- return partialMatch(key, decodedKey);
1195
- })
1196
- .map(stringKey => cache.data[stringKey]);
1304
+ return getMatchingCacheKeys(cache.data, key).map(({ encodedKey }) => cache.data[encodedKey]);
1197
1305
  };
1198
1306
 
1199
1307
  /**
@@ -1249,6 +1357,7 @@ const pushToCache = (key, data, options = { cachedAt: Date.now() }) => {
1249
1357
  }
1250
1358
  const encodedKey = encodeKey(key);
1251
1359
  cache.data[encodedKey] = Object.assign({ key, data }, options);
1360
+ updateCacheIndex(cache.data, encodedKey);
1252
1361
  return true;
1253
1362
  };
1254
1363
 
@@ -1333,15 +1442,14 @@ const dropFromCache = (key, exact = false) => {
1333
1442
  return false;
1334
1443
  log('cache/api/dropFromCache', { key, exact });
1335
1444
  if (!exact) {
1336
- return Object.keys(cache.data)
1337
- .map(stringKey => decodeKey(stringKey))
1338
- .filter(candidate => partialMatch(key, candidate))
1339
- .map(filteredKey => dropFromCache(filteredKey, true))
1445
+ return getMatchingCacheKeys(cache.data, key)
1446
+ .map(({ key: filteredKey }) => dropFromCache(filteredKey, true))
1340
1447
  .every(returned => returned);
1341
1448
  }
1342
1449
  const encodedKey = encodeKey(key);
1343
1450
  if (encodedKey in cache.data) {
1344
1451
  delete cache.data[encodedKey];
1452
+ removeFromCacheIndex(cache.data, encodedKey);
1345
1453
  return true;
1346
1454
  }
1347
1455
  return false;
@@ -3746,13 +3854,20 @@ const getUserMarker = async () => {
3746
3854
  return { data: latestUserMarker, cachedAt };
3747
3855
  };
3748
3856
 
3857
+ /** Internal model lookup; deliberately excluded from the public Cache API. @hidden */
3858
+ const queryCacheByProperty = (prefix, property, value) => {
3859
+ const { cache } = getActiveClient();
3860
+ if (!cache)
3861
+ return;
3862
+ return getCacheEntriesByProperty(cache.data, prefix, property, value);
3863
+ };
3864
+
3749
3865
  const getMessageFromMainDB = (messageId) => {
3750
- var _a, _b;
3866
+ var _a, _b, _c;
3751
3867
  const message = (_a = pullFromCache(['message', 'get', messageId])) === null || _a === void 0 ? void 0 : _a.data;
3752
3868
  if (message)
3753
3869
  return message;
3754
- const messages = queryCache(['message', 'get']);
3755
- return (_b = messages === null || messages === void 0 ? void 0 : messages.find(({ data }) => data.messageId === messageId)) === null || _b === void 0 ? void 0 : _b.data;
3870
+ return (_c = (_b = queryCacheByProperty(['message', 'get'], 'messageId', messageId)) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.data;
3756
3871
  };
3757
3872
 
3758
3873
  /** @hidden */
@@ -3894,7 +4009,8 @@ function convertFromRaw$2(_a) {
3894
4009
 
3895
4010
  const mergePayloadWithLocal = (payload) => {
3896
4011
  var _a, _b, _c;
3897
- const localMessage = (_b = (_a = queryCache(['message', 'get'])) === null || _a === void 0 ? void 0 : _a.find(({ data }) => data.messageId === payload.messageId)) === null || _b === void 0 ? void 0 : _b.data;
4012
+ // Cache keys may be local referenceIds even after a server messageId is assigned.
4013
+ const localMessage = (_b = (_a = queryCacheByProperty(['message', 'get'], 'messageId', payload.messageId)) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.data;
3898
4014
  if (localMessage) {
3899
4015
  return Object.assign(Object.assign(Object.assign({}, localMessage), payload), {
3900
4016
  // NOTE: referenceId is missing in the some payload event. If we have local message data with referenceId, use it instead.
@@ -5461,9 +5577,9 @@ const channelLinkedObject = (channel) => {
5461
5577
  var _a;
5462
5578
  let previewMembers = [];
5463
5579
  if (['conversation', 'community'].includes(channel.type)) {
5464
- const channelUsers = queryCache(['channelUsers', 'get']);
5580
+ const channelUsers = queryCacheByProperty(['channelUsers', 'get'], 'channelId', channel.channelId);
5465
5581
  if (channelUsers && (channelUsers === null || channelUsers === void 0 ? void 0 : channelUsers.length) > 0) {
5466
- previewMembers = ((_a = channelUsers === null || channelUsers === void 0 ? void 0 : channelUsers.filter(({ data }) => data.channelId === channel.channelId && data.membership !== 'none')) !== null && _a !== void 0 ? _a : [])
5582
+ previewMembers = ((_a = channelUsers === null || channelUsers === void 0 ? void 0 : channelUsers.filter(({ data }) => data.membership !== 'none')) !== null && _a !== void 0 ? _a : [])
5467
5583
  // sort in ascending order by userInternalId
5468
5584
  .sort((a, b) => a.data.userInternalId.localeCompare(b.data.userInternalId))
5469
5585
  // Select only first 4 members
@@ -28116,13 +28232,13 @@ const deleteComment = async (commentId, permanent = false) => {
28116
28232
  permanent,
28117
28233
  },
28118
28234
  });
28119
- // to support hard deletion; the server empties a deleted comment's attachments
28120
- const deleted = Object.assign(Object.assign({}, comment.data), { isDeleted: true, attachments: [] });
28235
+ // to support hard deletion
28236
+ const deleted = Object.assign(Object.assign({}, comment.data), { isDeleted: true });
28121
28237
  if (permanent) {
28122
28238
  scheduleTask(() => pushToTombstone('comment', commentId));
28123
28239
  }
28124
28240
  else {
28125
- upsertInCache(['comment', 'get', commentId], { isDeleted: true, attachments: [] });
28241
+ upsertInCache(['comment', 'get', commentId], { isDeleted: true });
28126
28242
  }
28127
28243
  if (comment.data.referenceType === 'story') {
28128
28244
  const story = await getStoryByStoryId$1(comment.data.referenceId);
@@ -30452,6 +30568,19 @@ getPostByIds.locally = (postIds) => {
30452
30568
  };
30453
30569
  };
30454
30570
 
30571
+ /*
30572
+ * The `attachments` a post create or edit sends. A GIF goes with its known keys only and never
30573
+ * with product tags (both a 400). A file takes its product tags from the map, as before.
30574
+ */
30575
+ const preparePostAttachments = (attachments, attachmentProductTags) => attachments === null || attachments === void 0 ? void 0 : attachments.map(attachment => {
30576
+ if (attachment.type === 'gif') {
30577
+ return toGifAttachmentPayload(attachment);
30578
+ }
30579
+ if (!attachmentProductTags || !attachment.fileId)
30580
+ return attachment;
30581
+ return Object.assign(Object.assign({}, attachment), { productTags: attachmentProductTags.get(attachment.fileId) });
30582
+ });
30583
+
30455
30584
  /* begin_public_function
30456
30585
  id: post.create.text_post, post.create.image_post, post.create.file_post, post.create.video_post, post.create.poll_post, post.create.livestream_post, post.create.custom_post
30457
30586
  */
@@ -30481,17 +30610,7 @@ const createPost = async (bundle) => {
30481
30610
  delete bundle.dataType;
30482
30611
  }
30483
30612
  const { attachments, attachmentProductTags } = bundle, rest = __rest(bundle, ["attachments", "attachmentProductTags"]);
30484
- const processedAttachments = attachments && attachments.length > 0 && bundle.attachmentProductTags
30485
- ? attachments.map(attachment => {
30486
- var _a;
30487
- /* c8 ignore next */
30488
- const productTags = (_a = bundle.attachmentProductTags) === null || _a === void 0 ? void 0 : _a.get(attachment.fileId);
30489
- if (productTags)
30490
- return Object.assign(Object.assign({}, attachment), { productTags });
30491
- /* c8 ignore next */
30492
- return attachment;
30493
- })
30494
- : attachments;
30613
+ const processedAttachments = preparePostAttachments(attachments, attachmentProductTags);
30495
30614
  const { data: payload } = await client.http.post('/api/v4/posts', Object.assign(Object.assign({}, rest), { attachments: processedAttachments }));
30496
30615
  fireEvent('post.created', payload);
30497
30616
  const data = preparePostPayload(payload);
@@ -30517,7 +30636,9 @@ const createPost = async (bundle) => {
30517
30636
  * })
30518
30637
  * ```
30519
30638
  *
30520
- * Updates an {@link Amity.Post}
30639
+ * Updates an {@link Amity.Post}. `attachments` is the full list to keep: a GIF is matched by its
30640
+ * `url`, so send each kept GIF back with its link and size as read from `childrenPosts`; an
30641
+ * attachment left out is deleted, `[]` removes them all, and leaving `attachments` out keeps them.
30521
30642
  *
30522
30643
  * @param postId The ID of the {@link Amity.Post} to edit
30523
30644
  * @param patch The patch data to apply
@@ -30530,17 +30651,7 @@ const editPost = async (postId, patch) => {
30530
30651
  const client = getActiveClient();
30531
30652
  client.log('user/editPost', patch);
30532
30653
  const { attachments } = patch;
30533
- const processedAttachments = attachments && attachments.length > 0 && patch.attachmentProductTags
30534
- ? attachments.map(attachment => {
30535
- var _a;
30536
- /* c8 ignore next */
30537
- const productTags = (_a = patch.attachmentProductTags) === null || _a === void 0 ? void 0 : _a.get(attachment.fileId);
30538
- if (productTags)
30539
- return Object.assign(Object.assign({}, attachment), { productTags });
30540
- /* c8 ignore next */
30541
- return attachment;
30542
- })
30543
- : attachments;
30654
+ const processedAttachments = preparePostAttachments(attachments, patch.attachmentProductTags);
30544
30655
  const { data: payload } = await client.http.put(`/api/v4/posts/${encodeURIComponent(postId)}`, Object.assign(Object.assign({}, patch), { attachments: processedAttachments }));
30545
30656
  const data = prepareMembershipPayload(payload, 'communityUsers');
30546
30657
  const cachedAt = client.cache && Date.now();
@@ -31024,11 +31135,14 @@ const createAudioPost = async (bundle) => {
31024
31135
  * { fileId: "file1", type: "image" },
31025
31136
  * { fileId: "file2", type: "video" },
31026
31137
  * { fileId: "file3", type: "audio" },
31027
- * { fileId: "file4", type: "file" }
31138
+ * { fileId: "file4", type: "file" },
31139
+ * { type: "gif", url: "https://media.giphy.com/media/l0MYt5jPR6QX5pnqM/giphy.gif", width: 480, height: 270 }
31028
31140
  * ],
31029
31141
  * })
31030
31142
  * ```
31031
31143
  *
31144
+ * A post holds 1 to 10 attachments. A GIF is a link, not a file, and takes no product tags.
31145
+ *
31032
31146
  * Creates an {@link Amity.Post}
31033
31147
  *
31034
31148
  * @param bundle The data necessary to create a new {@link Amity.Post}
@@ -31042,17 +31156,7 @@ const createMixedMediaPost = async (bundle) => {
31042
31156
  const client = getActiveClient();
31043
31157
  client.log('post/createMixedMediaPost', bundle);
31044
31158
  const { attachments, attachmentProductTags } = bundle, rest = __rest(bundle, ["attachments", "attachmentProductTags"]);
31045
- const processedAttachments = attachments && attachments.length > 0 && bundle.attachmentProductTags
31046
- ? attachments.map(attachment => {
31047
- var _a;
31048
- /* c8 ignore next */
31049
- const productTags = (_a = bundle.attachmentProductTags) === null || _a === void 0 ? void 0 : _a.get(attachment.fileId);
31050
- if (productTags)
31051
- return Object.assign(Object.assign({}, attachment), { productTags });
31052
- /* c8 ignore next */
31053
- return attachment;
31054
- })
31055
- : /* c8 ignore next */ attachments;
31159
+ const processedAttachments = preparePostAttachments(attachments, attachmentProductTags);
31056
31160
  const { data: payload } = await client.http.post('/api/v4/posts', Object.assign(Object.assign({}, rest), { attachments: processedAttachments }));
31057
31161
  fireEvent('post.created', payload);
31058
31162
  const data = preparePostPayload(payload);
@@ -31569,6 +31673,10 @@ class PostLiveCollectionController extends LiveCollectionController {
31569
31673
  *
31570
31674
  * Observe all mutations on a list of {@link Amity.Post} for a given target object
31571
31675
  *
31676
+ * GIF posts come back on every API version. This query is on v5, so `dataTypes: ['gif']` filters
31677
+ * them; a post with GIFs beside other media is `mixed` and is hidden unless
31678
+ * `includeMixedStructure` is `true`.
31679
+ *
31572
31680
  * @param params.targetType the type of the target
31573
31681
  * @param params.targetId the ID of the target
31574
31682
  * @param callback the function to call when new data are available