@amityco/ts-sdk 7.22.1-fa4a2e0.0 → 7.23.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 (51) hide show
  1. package/dist/@types/core/errors.d.ts +2 -1
  2. package/dist/@types/core/errors.d.ts.map +1 -1
  3. package/dist/@types/core/payload.d.ts +0 -1
  4. package/dist/@types/core/payload.d.ts.map +1 -1
  5. package/dist/@types/domains/channel.d.ts +11 -0
  6. package/dist/@types/domains/channel.d.ts.map +1 -1
  7. package/dist/@types/domains/client.d.ts +6 -0
  8. package/dist/@types/domains/client.d.ts.map +1 -1
  9. package/dist/channelRepository/observers/index.d.ts +1 -0
  10. package/dist/channelRepository/observers/index.d.ts.map +1 -1
  11. package/dist/channelRepository/observers/searchChannels/SearchChannelLiveCollectionController.d.ts +14 -0
  12. package/dist/channelRepository/observers/searchChannels/SearchChannelLiveCollectionController.d.ts.map +1 -0
  13. package/dist/channelRepository/observers/searchChannels/SearchChannelPaginationController.d.ts +10 -0
  14. package/dist/channelRepository/observers/searchChannels/SearchChannelPaginationController.d.ts.map +1 -0
  15. package/dist/channelRepository/observers/searchChannels/SearchChannelQueryStreamController.d.ts +13 -0
  16. package/dist/channelRepository/observers/searchChannels/SearchChannelQueryStreamController.d.ts.map +1 -0
  17. package/dist/channelRepository/observers/searchChannels.d.ts +21 -0
  18. package/dist/channelRepository/observers/searchChannels.d.ts.map +1 -0
  19. package/dist/channelRepository/observers/tests/integrations/searchChannels.integration.test.d.ts +2 -0
  20. package/dist/channelRepository/observers/tests/integrations/searchChannels.integration.test.d.ts.map +1 -0
  21. package/dist/client/api/createClient.d.ts.map +1 -1
  22. package/dist/client/api/getForYouFeedSetting.d.ts +17 -0
  23. package/dist/client/api/getForYouFeedSetting.d.ts.map +1 -0
  24. package/dist/client/api/index.d.ts +1 -0
  25. package/dist/client/api/index.d.ts.map +1 -1
  26. package/dist/client/api/setupLoginSubscriptions.d.ts.map +1 -1
  27. package/dist/feedRepository/observers/getForYouFeed/CursorController.d.ts +2 -0
  28. package/dist/feedRepository/observers/getForYouFeed/CursorController.d.ts.map +1 -1
  29. package/dist/feedRepository/observers/getForYouFeed/PaginationController.d.ts +5 -4
  30. package/dist/feedRepository/observers/getForYouFeed/PaginationController.d.ts.map +1 -1
  31. package/dist/index.cjs.js +829 -197
  32. package/dist/index.esm.js +822 -190
  33. package/dist/index.umd.js +2 -2
  34. package/dist/userRepository/api/getAllBlockedUsers.d.ts.map +1 -1
  35. package/dist/userRepository/api/getAllBlockingUsers.d.ts +20 -0
  36. package/dist/userRepository/api/getAllBlockingUsers.d.ts.map +1 -0
  37. package/dist/userRepository/api/index.d.ts +1 -0
  38. package/dist/userRepository/api/index.d.ts.map +1 -1
  39. package/dist/userRepository/api/tests/integration/getAllBlockingUsers.integration.test.d.ts +2 -0
  40. package/dist/userRepository/api/tests/integration/getAllBlockingUsers.integration.test.d.ts.map +1 -0
  41. package/dist/userRepository/relationship/block/utils/blockRealtimeOnLoginHandler.d.ts +12 -0
  42. package/dist/userRepository/relationship/block/utils/blockRealtimeOnLoginHandler.d.ts.map +1 -0
  43. package/dist/userRepository/services/BlockedUserSyncEngine/blockedUserSyncEngine.d.ts +13 -13
  44. package/dist/userRepository/services/BlockedUserSyncEngine/blockedUserSyncEngine.d.ts.map +1 -1
  45. package/dist/userRepository/services/BlockingUserSyncEngine/blockingUserSyncEngine.d.ts +56 -0
  46. package/dist/userRepository/services/BlockingUserSyncEngine/blockingUserSyncEngine.d.ts.map +1 -0
  47. package/dist/userRepository/services/BlockingUserSyncEngine/blockingUserSyncEngineOnLoginHandler.d.ts +3 -0
  48. package/dist/userRepository/services/BlockingUserSyncEngine/blockingUserSyncEngineOnLoginHandler.d.ts.map +1 -0
  49. package/dist/utils/tests/dummy/block.d.ts +16 -0
  50. package/dist/utils/tests/dummy/block.d.ts.map +1 -1
  51. package/package.json +1 -1
package/dist/index.cjs.js CHANGED
@@ -313,8 +313,8 @@ exports.NotificationRolesFilterTypeEnum = void 0;
313
313
 
314
314
  function getVersion() {
315
315
  try {
316
- // the string ''v7.22.0-cjs'' should be replaced by actual value by @rollup/plugin-replace
317
- return 'v7.22.0-cjs';
316
+ // the string ''v7.23.0-cjs'' should be replaced by actual value by @rollup/plugin-replace
317
+ return 'v7.23.0-cjs';
318
318
  }
319
319
  catch (error) {
320
320
  return '__dev__';
@@ -1971,13 +1971,13 @@ class NetworkActivitiesWatcher {
1971
1971
  this._listener.clear();
1972
1972
  }
1973
1973
  }
1974
- let instance$a;
1974
+ let instance$b;
1975
1975
  var NetworkActivitiesWatcher$1 = {
1976
1976
  getInstance: () => {
1977
- if (!instance$a) {
1978
- instance$a = new NetworkActivitiesWatcher();
1977
+ if (!instance$b) {
1978
+ instance$b = new NetworkActivitiesWatcher();
1979
1979
  }
1980
- return instance$a;
1980
+ return instance$b;
1981
1981
  },
1982
1982
  };
1983
1983
 
@@ -4746,13 +4746,13 @@ class SessionWatcher {
4746
4746
  this._listener.clear();
4747
4747
  }
4748
4748
  }
4749
- let instance$9;
4749
+ let instance$a;
4750
4750
  var SessionWatcher$1 = {
4751
4751
  getInstance: () => {
4752
- if (!instance$9) {
4753
- instance$9 = new SessionWatcher();
4752
+ if (!instance$a) {
4753
+ instance$a = new SessionWatcher();
4754
4754
  }
4755
- return instance$9;
4755
+ return instance$a;
4756
4756
  },
4757
4757
  };
4758
4758
 
@@ -4849,11 +4849,11 @@ class AutoSubscriptionManager {
4849
4849
  this.seed();
4850
4850
  }
4851
4851
  }
4852
- let instance$8 = null;
4852
+ let instance$9 = null;
4853
4853
  const getAutoSubscriptionManager = () => {
4854
- if (!instance$8)
4855
- instance$8 = new AutoSubscriptionManager();
4856
- return instance$8;
4854
+ if (!instance$9)
4855
+ instance$9 = new AutoSubscriptionManager();
4856
+ return instance$9;
4857
4857
  };
4858
4858
  /** Resets the registry to its default baseline when the session ends. */
4859
4859
  const resetAutoSubscriptionsOnLogout = () => onSessionStateChange(state => {
@@ -6061,7 +6061,7 @@ getSubChannel$1.locally = (subChannelId) => {
6061
6061
  };
6062
6062
  };
6063
6063
 
6064
- const getMessagePreviewSetting$1 = async () => {
6064
+ const getMessagePreviewSetting$2 = async () => {
6065
6065
  const client = getActiveClient();
6066
6066
  return client.getMessagePreviewSetting(false);
6067
6067
  };
@@ -6096,7 +6096,7 @@ const isLastestMessageOnChannel = (message) => {
6096
6096
  convertDateStringToTimestamp(cache.createdAt) <= convertDateStringToTimestamp(message.createdAt));
6097
6097
  };
6098
6098
  const handleMessageCreatedOnSubChannel = async (message) => {
6099
- const messagePreviewSetting = await getMessagePreviewSetting$1();
6099
+ const messagePreviewSetting = await getMessagePreviewSetting$2();
6100
6100
  const { channelId, messageId: messagePreviewId, creatorId, createdAt, updatedAt, data, dataType, subChannelId, channelSegment: segment, isDeleted, } = message;
6101
6101
  // 1. get subChannel from cache, if not exist fetch from server
6102
6102
  const subChannelCache = await getSubChannelCache(subChannelId);
@@ -7447,7 +7447,7 @@ const convertMessagePreviewSetting = (chatSetting) => {
7447
7447
  * @category private
7448
7448
  * @async
7449
7449
  */
7450
- const getMessagePreviewSetting = async (refresh = true) => {
7450
+ const getMessagePreviewSetting$1 = async (refresh = true) => {
7451
7451
  var _a;
7452
7452
  const messagePreviewSetting = (_a = pullFromCache([
7453
7453
  'MessagePreviewSetting',
@@ -7468,7 +7468,7 @@ const getMessagePreviewSetting = async (refresh = true) => {
7468
7468
  */
7469
7469
  const initializeMessagePreviewSetting = async () => {
7470
7470
  var _a;
7471
- const newMessagePreviewSetting = await getMessagePreviewSetting();
7471
+ const newMessagePreviewSetting = await getMessagePreviewSetting$1();
7472
7472
  const messagePreviewSetting = (_a = pullFromCache([
7473
7473
  'MessagePreviewSetting',
7474
7474
  ])) === null || _a === void 0 ? void 0 : _a.data;
@@ -7582,6 +7582,33 @@ const getProductCatalogueSetting = async () => {
7582
7582
  };
7583
7583
  /* end_public_function */
7584
7584
 
7585
+ /* begin_public_function
7586
+ id: client.getForYouFeedSetting
7587
+ */
7588
+ /**
7589
+ * ```js
7590
+ * import { Client } from '@amityco/ts-sdk'
7591
+ * const forYouFeedSetting = await Client.getForYouFeedSetting()
7592
+ * ```
7593
+ *
7594
+ * Fetches the network-level {@link Amity.ForYouFeedSetting} — whether the For You
7595
+ * feed feature is enabled for the current network. Use `forYouFeed.enabled` as a
7596
+ * feature gate before querying {@link getForYouFeed}.
7597
+ *
7598
+ * @returns A Promise of {@link Amity.ForYouFeedSetting} object
7599
+ *
7600
+ * @category Client API
7601
+ * @async
7602
+ */
7603
+ const getForYouFeedSetting = async () => {
7604
+ var _a, _b;
7605
+ const client = getActiveClient();
7606
+ const { data } = await client.http.get(`/api/v3/network-settings/for-you-feed`);
7607
+ // Surface only `enabled`; ignore server-side ranking config (scoringConfig, etc.)
7608
+ return { forYouFeed: { enabled: (_b = (_a = data.forYouFeed) === null || _a === void 0 ? void 0 : _a.enabled) !== null && _b !== void 0 ? _b : false } };
7609
+ };
7610
+ /* end_public_function */
7611
+
7585
7612
  const checkUserPermission = (userId, permission) => {
7586
7613
  var _a;
7587
7614
  const user = (_a = pullFromCache(['user', 'get', userId])) === null || _a === void 0 ? void 0 : _a.data;
@@ -8369,13 +8396,13 @@ class AnalyticsEngine {
8369
8396
  this._eventCapturer.resetAllBuckets();
8370
8397
  }
8371
8398
  }
8372
- let instance$7;
8399
+ let instance$8;
8373
8400
  var AnalyticsEngine$1 = {
8374
8401
  getInstance: () => {
8375
- if (!instance$7) {
8376
- instance$7 = new AnalyticsEngine();
8402
+ if (!instance$8) {
8403
+ instance$8 = new AnalyticsEngine();
8377
8404
  }
8378
- return instance$7;
8405
+ return instance$8;
8379
8406
  },
8380
8407
  };
8381
8408
 
@@ -8602,12 +8629,12 @@ class MessageReadReceiptSyncEngine {
8602
8629
  }
8603
8630
  }
8604
8631
  }
8605
- let instance$6 = null;
8632
+ let instance$7 = null;
8606
8633
  var ReadReceiptSyncEngine = {
8607
8634
  getInstance: () => {
8608
- if (!instance$6)
8609
- instance$6 = new MessageReadReceiptSyncEngine();
8610
- return instance$6;
8635
+ if (!instance$7)
8636
+ instance$7 = new MessageReadReceiptSyncEngine();
8637
+ return instance$7;
8611
8638
  },
8612
8639
  };
8613
8640
 
@@ -8861,12 +8888,12 @@ class LegacyMessageReadReceiptSyncEngine {
8861
8888
  }
8862
8889
  }
8863
8890
  }
8864
- let instance$5 = null;
8891
+ let instance$6 = null;
8865
8892
  var LegacyReadReceiptSyncEngine = {
8866
8893
  getInstance: () => {
8867
- if (!instance$5)
8868
- instance$5 = new LegacyMessageReadReceiptSyncEngine();
8869
- return instance$5;
8894
+ if (!instance$6)
8895
+ instance$6 = new LegacyMessageReadReceiptSyncEngine();
8896
+ return instance$6;
8870
8897
  },
8871
8898
  };
8872
8899
 
@@ -9112,12 +9139,12 @@ class ObjectResolverEngine {
9112
9139
  this.stopResolver();
9113
9140
  }
9114
9141
  }
9115
- let instance$4 = null;
9142
+ let instance$5 = null;
9116
9143
  var ObjectResolverEngine$1 = {
9117
9144
  getInstance: () => {
9118
- if (!instance$4)
9119
- instance$4 = new ObjectResolverEngine();
9120
- return instance$4;
9145
+ if (!instance$5)
9146
+ instance$5 = new ObjectResolverEngine();
9147
+ return instance$5;
9121
9148
  },
9122
9149
  };
9123
9150
 
@@ -9267,13 +9294,13 @@ class LiveReactionSyncEngine {
9267
9294
  this.stopReactionsSync();
9268
9295
  }
9269
9296
  }
9270
- let instance$3;
9297
+ let instance$4;
9271
9298
  var ReactionSyncEngine = {
9272
9299
  getInstance: () => {
9273
- if (!instance$3) {
9274
- instance$3 = new LiveReactionSyncEngine();
9300
+ if (!instance$4) {
9301
+ instance$4 = new LiveReactionSyncEngine();
9275
9302
  }
9276
- return instance$3;
9303
+ return instance$4;
9277
9304
  },
9278
9305
  };
9279
9306
 
@@ -9303,19 +9330,20 @@ function prepareBlockedUserPayload(response) {
9303
9330
  }), users: users.map(convertRawUserToInternalUser) });
9304
9331
  }
9305
9332
 
9306
- const BLOCK_LIST_LIMIT = 100;
9307
- const TTL_MS = 5 * 60 * 1000; // 5 minutes
9333
+ const BLOCK_LIST_LIMIT$1 = 100;
9334
+ const TTL_MS$1 = 5 * 60 * 1000; // 5 minutes
9308
9335
  /**
9309
9336
  * Session-scoped singleton engine that manages TTL-based fetching and caching
9310
9337
  * of blocked users. Provides a lazy `ensureFetched()` gate for the
9311
9338
  * `getAllBlockedUsers()` API.
9312
9339
  *
9313
9340
  * Key behaviours:
9314
- * - `establish()` is a no-op — fetch is lazy, triggered only by consumer call
9315
- * - `destroy()` resets `lastFetchedAt` to null (ensures fresh fetch on next session)
9341
+ * - `onSessionEstablished()` is a no-op — fetch is lazy, triggered only by consumer call
9342
+ * - `onSessionDestroyed()` resets `lastFetchedAt` to null (ensures fresh fetch on next session)
9316
9343
  * - `ensureFetched()` fetches from server only when cache is expired or never fetched
9317
9344
  * - `lastFetchedAt` is only updated on successful fetch
9318
- * - `blockedUserIds` is kept in sync with each successful fetch
9345
+ * - reads query the follow cache live, so block/unblock changes are reflected
9346
+ * even within the TTL window
9319
9347
  *
9320
9348
  * @internal
9321
9349
  */
@@ -9323,12 +9351,7 @@ class BlockedUserSyncEngine {
9323
9351
  constructor() {
9324
9352
  /** Epoch ms of last successful fetch. null = never fetched in this session. */
9325
9353
  this.lastFetchedAt = null;
9326
- /** Ordered list of userId strings from the most recent successful fetch. */
9327
- this.blockedUserIds = [];
9328
9354
  }
9329
- // ---------------------------------------------------------------------------
9330
- // SessionComponent lifecycle
9331
- // ---------------------------------------------------------------------------
9332
9355
  /** No-op — fetch is lazy, triggered by consumer calling getAllBlockedUsers(). */
9333
9356
  // eslint-disable-next-line class-methods-use-this
9334
9357
  onSessionEstablished() {
@@ -9337,24 +9360,17 @@ class BlockedUserSyncEngine {
9337
9360
  /** Resets state so the next session starts with a cold cache. */
9338
9361
  onSessionDestroyed() {
9339
9362
  this.lastFetchedAt = null;
9340
- this.blockedUserIds = [];
9341
9363
  }
9342
9364
  /** No-op for this engine. */
9343
9365
  // eslint-disable-next-line class-methods-use-this
9344
9366
  onTokenExpired() {
9345
9367
  // intentionally empty
9346
9368
  }
9347
- // ---------------------------------------------------------------------------
9348
- // Cache helpers
9349
- // ---------------------------------------------------------------------------
9350
9369
  isCacheExpired() {
9351
9370
  if (this.lastFetchedAt === null)
9352
9371
  return true;
9353
- return Date.now() - this.lastFetchedAt > TTL_MS;
9372
+ return Date.now() - this.lastFetchedAt > TTL_MS$1;
9354
9373
  }
9355
- // ---------------------------------------------------------------------------
9356
- // Core fetch gate
9357
- // ---------------------------------------------------------------------------
9358
9374
  /**
9359
9375
  * Ensures the local store is populated with fresh blocked-user data.
9360
9376
  *
@@ -9370,52 +9386,50 @@ class BlockedUserSyncEngine {
9370
9386
  if (!this.isCacheExpired())
9371
9387
  return;
9372
9388
  const client = getActiveClient();
9373
- const { data } = await client.http.get('/api/v4/me/user-blocks', { params: { options: { limit: BLOCK_LIST_LIMIT }, isDeleted: false } });
9389
+ const { data } = await client.http.get('/api/v4/me/user-blocks', { params: { options: { limit: BLOCK_LIST_LIMIT$1 }, isDeleted: false } });
9374
9390
  const cachedAt = client.cache && Date.now();
9375
9391
  const payload = prepareBlockedUserPayload(data);
9376
9392
  if (client.cache) {
9377
9393
  ingestInCache(payload, { cachedAt });
9378
9394
  }
9379
- // Keep the ordered list of blocked user IDs for getCachedUsers()
9380
- this.blockedUserIds = payload.users.map(u => u.userId);
9381
9395
  // Only update timestamp on success — failure leaves it unchanged so the
9382
9396
  // next call retries.
9383
9397
  this.lastFetchedAt = Date.now();
9384
9398
  }
9385
- // ---------------------------------------------------------------------------
9386
- // Local store read
9387
- // ---------------------------------------------------------------------------
9388
9399
  /**
9389
- * Returns blocked {@link Amity.InternalUser} objects from the in-memory cache.
9390
- *
9391
- * Applies the spec-mandated filter:
9392
- * - Only users whose `isDeleted` is false or null
9393
- * - Hard limit of 100 results
9400
+ * Returns the blocked users by querying the follow cache live — the outgoing
9401
+ * direction (`from === me`, `status === 'blocked'`). Reading the cache (not a
9402
+ * fetched snapshot) means `blockUser()` / `unblockUser()` changes are reflected
9403
+ * even within the TTL window.
9394
9404
  *
9395
- * This always reads the latest local state, so changes made by `blockUser()`
9396
- * and `unblockUser()` are reflected even within the TTL window.
9405
+ * Applies the spec-mandated query:
9406
+ * - filter `from === currentUserId`, `status === 'blocked'`, blocked user not deleted
9407
+ * - sort by `updatedAt` DESC
9408
+ * - hard limit of 100 results
9397
9409
  */
9410
+ // eslint-disable-next-line class-methods-use-this
9398
9411
  getCachedUsers() {
9412
+ var _a;
9399
9413
  const client = getActiveClient();
9400
- if (!client.cache) {
9401
- // No cache available — fall back to the in-memory list from the last fetch
9414
+ if (!client.cache)
9402
9415
  return [];
9403
- }
9404
- return this.blockedUserIds
9405
- .map(id => { var _a; return (_a = pullFromCache(['user', 'get', id])) === null || _a === void 0 ? void 0 : _a.data; })
9416
+ const { userId } = client;
9417
+ return ((_a = queryCache(['follow', 'get'])) !== null && _a !== void 0 ? _a : [])
9418
+ .map(entry => entry.data)
9419
+ .filter(follow => (follow === null || follow === void 0 ? void 0 : follow.status) === 'blocked' && follow.from === userId)
9420
+ .sort((a, b) => { var _a, _b; return (((_a = a.updatedAt) !== null && _a !== void 0 ? _a : '') < ((_b = b.updatedAt) !== null && _b !== void 0 ? _b : '') ? 1 : -1); })
9421
+ .map(follow => { var _a; return (_a = pullFromCache(['user', 'get', follow.to])) === null || _a === void 0 ? void 0 : _a.data; })
9406
9422
  .filter((user) => user != null && user.isDeleted !== true)
9407
- .slice(0, BLOCK_LIST_LIMIT);
9423
+ .slice(0, BLOCK_LIST_LIMIT$1)
9424
+ .map(userLinkedObject);
9408
9425
  }
9409
9426
  }
9410
- // ---------------------------------------------------------------------------
9411
- // Module-level singleton
9412
- // ---------------------------------------------------------------------------
9413
- let instance$2 = null;
9427
+ let instance$3 = null;
9414
9428
  var BlockedUserSyncEngine$1 = {
9415
9429
  getInstance: () => {
9416
- if (!instance$2)
9417
- instance$2 = new BlockedUserSyncEngine();
9418
- return instance$2;
9430
+ if (!instance$3)
9431
+ instance$3 = new BlockedUserSyncEngine();
9432
+ return instance$3;
9419
9433
  },
9420
9434
  };
9421
9435
 
@@ -9437,6 +9451,224 @@ var blockedUserSyncEngineOnLoginHandler = () => {
9437
9451
  };
9438
9452
  };
9439
9453
 
9454
+ function prepareBlockingUserPayload(response) {
9455
+ const { users, follows } = response, rest = __rest(response, ["users", "follows"]);
9456
+ return Object.assign(Object.assign({}, rest), { follows: follows.map(follow => {
9457
+ const followUser = users.find(user => user.userId === follow.from);
9458
+ return Object.assign(Object.assign({}, follow), { user: convertRawUserToInternalUser(followUser) });
9459
+ }), users: users.map(convertRawUserToInternalUser) });
9460
+ }
9461
+
9462
+ const BLOCK_LIST_LIMIT = 100;
9463
+ const TTL_MS = 5 * 60 * 1000; // 5 minutes
9464
+ /**
9465
+ * Session-scoped singleton engine that manages TTL-based fetching and caching
9466
+ * of blocking users (the "blocked-by" list — users who blocked the current
9467
+ * user). Provides a lazy `ensureFetched()` gate for the `getAllBlockingUsers()`
9468
+ * API. Directional inverse of {@link BlockedUserSyncEngine}.
9469
+ *
9470
+ * Key behaviours:
9471
+ * - `onSessionEstablished()` is a no-op — fetch is lazy, triggered only by consumer call
9472
+ * - `onSessionDestroyed()` resets `lastFetchedAt` to null (ensures fresh fetch on next session)
9473
+ * - `ensureFetched()` fetches from server only when cache is expired or never fetched
9474
+ * - `lastFetchedAt` is only updated on successful fetch
9475
+ * - reads query the follow cache live, so block/unblock changes are reflected
9476
+ * even within the TTL window
9477
+ *
9478
+ * @internal
9479
+ */
9480
+ class BlockingUserSyncEngine {
9481
+ constructor() {
9482
+ /** Epoch ms of last successful fetch. null = never fetched in this session. */
9483
+ this.lastFetchedAt = null;
9484
+ }
9485
+ /** No-op — fetch is lazy, triggered by consumer calling getAllBlockingUsers(). */
9486
+ // eslint-disable-next-line class-methods-use-this
9487
+ onSessionEstablished() {
9488
+ // intentionally empty
9489
+ }
9490
+ /** Resets state so the next session starts with a cold cache. */
9491
+ onSessionDestroyed() {
9492
+ this.lastFetchedAt = null;
9493
+ }
9494
+ /** No-op for this engine. */
9495
+ // eslint-disable-next-line class-methods-use-this
9496
+ onTokenExpired() {
9497
+ // intentionally empty
9498
+ }
9499
+ isCacheExpired() {
9500
+ if (this.lastFetchedAt === null)
9501
+ return true;
9502
+ return Date.now() - this.lastFetchedAt > TTL_MS;
9503
+ }
9504
+ /**
9505
+ * Ensures the local store is populated with fresh blocking-user data.
9506
+ *
9507
+ * - If the cache is still within the 5-minute TTL window: resolves immediately
9508
+ * (no server call).
9509
+ * - If the cache is expired (or never fetched): fetches from the server,
9510
+ * persists the payload to the cache, and updates `lastFetchedAt`.
9511
+ *
9512
+ * On failure the error propagates to the caller and `lastFetchedAt` is NOT
9513
+ * updated, so the next call will retry.
9514
+ */
9515
+ async ensureFetched() {
9516
+ if (!this.isCacheExpired())
9517
+ return;
9518
+ const client = getActiveClient();
9519
+ const { data } = await client.http.get('/api/v4/me/blockers', { params: { options: { limit: BLOCK_LIST_LIMIT }, isDeleted: false } });
9520
+ const cachedAt = client.cache && Date.now();
9521
+ const payload = prepareBlockingUserPayload(data);
9522
+ if (client.cache) {
9523
+ ingestInCache(payload, { cachedAt });
9524
+ }
9525
+ // Only update timestamp on success — failure leaves it unchanged so the
9526
+ // next call retries.
9527
+ this.lastFetchedAt = Date.now();
9528
+ }
9529
+ /**
9530
+ * Returns the "blocked-by" users by querying the follow cache live — the
9531
+ * incoming direction (`to === me`, `status === 'blocked'`). Reading the cache
9532
+ * (not a fetched snapshot) means RTE `user.didBlock` / `user.didUnblock`
9533
+ * changes are reflected even within the TTL window.
9534
+ *
9535
+ * Applies the spec-mandated query (REQ-012/013/014):
9536
+ * - filter `to === currentUserId`, `status === 'blocked'`, blocker not deleted
9537
+ * - sort by `updatedAt` DESC
9538
+ * - hard limit of 100 results
9539
+ */
9540
+ // eslint-disable-next-line class-methods-use-this
9541
+ getCachedUsers() {
9542
+ var _a;
9543
+ const client = getActiveClient();
9544
+ if (!client.cache)
9545
+ return [];
9546
+ const { userId } = client;
9547
+ return ((_a = queryCache(['follow', 'get'])) !== null && _a !== void 0 ? _a : [])
9548
+ .map(entry => entry.data)
9549
+ .filter(follow => (follow === null || follow === void 0 ? void 0 : follow.status) === 'blocked' && follow.to === userId)
9550
+ .sort((a, b) => { var _a, _b; return (((_a = a.updatedAt) !== null && _a !== void 0 ? _a : '') < ((_b = b.updatedAt) !== null && _b !== void 0 ? _b : '') ? 1 : -1); })
9551
+ .map(follow => { var _a; return (_a = pullFromCache(['user', 'get', follow.from])) === null || _a === void 0 ? void 0 : _a.data; })
9552
+ .filter((user) => user != null && user.isDeleted !== true)
9553
+ .slice(0, BLOCK_LIST_LIMIT)
9554
+ .map(userLinkedObject);
9555
+ }
9556
+ }
9557
+ let instance$2 = null;
9558
+ var BlockingUserSyncEngine$1 = {
9559
+ getInstance: () => {
9560
+ if (!instance$2)
9561
+ instance$2 = new BlockingUserSyncEngine();
9562
+ return instance$2;
9563
+ },
9564
+ };
9565
+
9566
+ var blockingUserSyncEngineOnLoginHandler = () => {
9567
+ const blockingUserSyncEngine = BlockingUserSyncEngine$1.getInstance();
9568
+ onSessionStateChange(state => {
9569
+ if (state === "established" /* Amity.SessionStates.ESTABLISHED */) {
9570
+ blockingUserSyncEngine.onSessionEstablished();
9571
+ }
9572
+ else if (state === "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */) {
9573
+ blockingUserSyncEngine.onTokenExpired();
9574
+ }
9575
+ else {
9576
+ blockingUserSyncEngine.onSessionDestroyed();
9577
+ }
9578
+ });
9579
+ return () => {
9580
+ blockingUserSyncEngine.onSessionDestroyed();
9581
+ };
9582
+ };
9583
+
9584
+ function prepareFollowersPayload(response) {
9585
+ const { users, follows } = response, rest = __rest(response, ["users", "follows"]);
9586
+ return Object.assign(Object.assign({}, rest), { follows, users: users.map(convertRawUserToInternalUser) });
9587
+ }
9588
+ function prepareFollowingsPayload(response) {
9589
+ const { users, follows } = response, rest = __rest(response, ["users", "follows"]);
9590
+ return Object.assign(Object.assign({}, rest), { follows, users: users.map(convertRawUserToInternalUser) });
9591
+ }
9592
+ function prepareFollowStatusPayload(response) {
9593
+ const { follows } = response, rest = __rest(response, ["follows"]);
9594
+ return Object.assign(Object.assign({}, rest), { follows });
9595
+ }
9596
+
9597
+ /**
9598
+ * Subscribes to a block MQTT event (`user.didBlock` / `user.didUnblock`),
9599
+ * ingests the carried `FollowersPayload` (follows + users) into the cache so
9600
+ * the block-relationship rows (`['follow', 'get', from#to]`) and user rows are
9601
+ * kept current, then forwards the affected {@link Amity.FollowStatus} to the
9602
+ * caller.
9603
+ *
9604
+ * @hidden
9605
+ */
9606
+ const createBlockEventSubscriber = (event, callback) => {
9607
+ const client = getActiveClient();
9608
+ const filter = (data) => {
9609
+ const payload = prepareFollowersPayload(data);
9610
+ if (client.cache) {
9611
+ ingestInCache(payload);
9612
+ }
9613
+ callback(payload.follows[0]);
9614
+ };
9615
+ return createEventSubscriber(client, event, event, filter);
9616
+ };
9617
+
9618
+ /**
9619
+ * ```js
9620
+ * import { onUserDidBlock } from '@amityco/ts-sdk'
9621
+ * const dispose = onUserDidBlock(status => {
9622
+ * // ...
9623
+ * })
9624
+ * ```
9625
+ *
9626
+ * Fired when a block relationship is created in real time — either a user
9627
+ * blocks the current user (incoming) or the current user blocks someone on
9628
+ * another device (outgoing). The local block-relationship store is updated
9629
+ * before the callback runs.
9630
+ *
9631
+ * @param callback The function to call when the event was fired
9632
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
9633
+ *
9634
+ * @category Block Events
9635
+ */
9636
+ const onUserDidBlock = (callback) => createBlockEventSubscriber('user.didBlock', callback);
9637
+
9638
+ /**
9639
+ * ```js
9640
+ * import { onUserDidUnblock } from '@amityco/ts-sdk'
9641
+ * const dispose = onUserDidUnblock(status => {
9642
+ * // ...
9643
+ * })
9644
+ * ```
9645
+ *
9646
+ * Fired when a block relationship is removed in real time — either a user
9647
+ * unblocks the current user (incoming) or the current user unblocks someone on
9648
+ * another device (outgoing). The local block-relationship store is updated
9649
+ * before the callback runs.
9650
+ *
9651
+ * @param callback The function to call when the event was fired
9652
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
9653
+ *
9654
+ * @category Block Events
9655
+ */
9656
+ const onUserDidUnblock = (callback) => createBlockEventSubscriber('user.didUnblock', callback);
9657
+
9658
+ /**
9659
+ * Keeps the follow cache current for incoming/outgoing block changes for the
9660
+ * whole session — the ingest happens as a side effect inside
9661
+ * createBlockEventSubscriber, so the one-shot `getAllBlockedUsers()` /
9662
+ * `getAllBlockingUsers()` reflect RTE block/unblock within the TTL window.
9663
+ *
9664
+ * Registered via `setupLoginSubscriptions`; the returned unsubscriber is torn
9665
+ * down on logout.
9666
+ */
9667
+ var blockRealtimeOnLoginHandler = () => {
9668
+ const disposers = [onUserDidBlock(() => { }), onUserDidUnblock(() => { })];
9669
+ return () => disposers.forEach(dispose => dispose());
9670
+ };
9671
+
9440
9672
  class CursorController {
9441
9673
  constructor(networkId, userId) {
9442
9674
  this.networkId = networkId;
@@ -9476,7 +9708,11 @@ class CursorController {
9476
9708
  const parsed = JSON.parse(raw);
9477
9709
  if (typeof (parsed === null || parsed === void 0 ? void 0 : parsed.cursor) !== 'string' || typeof (parsed === null || parsed === void 0 ? void 0 : parsed.expiredAt) !== 'number')
9478
9710
  return null;
9479
- return { cursor: parsed.cursor, expiredAt: parsed.expiredAt };
9711
+ return {
9712
+ cursor: parsed.cursor,
9713
+ expiredAt: parsed.expiredAt,
9714
+ reachedEndOfFeed: parsed.reachedEndOfFeed === true,
9715
+ };
9480
9716
  }
9481
9717
  catch (_a) {
9482
9718
  return null;
@@ -9485,6 +9721,15 @@ class CursorController {
9485
9721
  async set(value) {
9486
9722
  await setItem(this.key, JSON.stringify(value));
9487
9723
  }
9724
+ async markReachedEndOfFeed(expiredAt) {
9725
+ var _a;
9726
+ const stored = await this.get();
9727
+ await this.set({
9728
+ cursor: (_a = stored === null || stored === void 0 ? void 0 : stored.cursor) !== null && _a !== void 0 ? _a : '',
9729
+ expiredAt,
9730
+ reachedEndOfFeed: true,
9731
+ });
9732
+ }
9488
9733
  async clear() {
9489
9734
  await setItem(this.key, '');
9490
9735
  }
@@ -9679,7 +9924,7 @@ const setupLoginSubscriptions = (unsubWatcher) => {
9679
9924
  // NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
9680
9925
  // the channel because currently backend can't handle this, so every time a user is banned from
9681
9926
  // a channel or the channel is deleted the channel's unread count will not be reset to zero
9682
- onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler(), reactionSyncEngineOnLoginHandler(), blockedUserSyncEngineOnLoginHandler(), resetAutoSubscriptionsOnLogout(), CursorController.clearOnLogout());
9927
+ onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler(), reactionSyncEngineOnLoginHandler(), blockedUserSyncEngineOnLoginHandler(), blockingUserSyncEngineOnLoginHandler(), blockRealtimeOnLoginHandler(), resetAutoSubscriptionsOnLogout(), CursorController.clearOnLogout());
9683
9928
  if (client.useLegacyUnreadCount) {
9684
9929
  subscriptions.push(readReceiptSyncEngineOnLoginHandler());
9685
9930
  }
@@ -10764,12 +11009,13 @@ const createClient = (apiKey, apiRegion = API_REGIONS.SG, { debugSession = DEFAU
10764
11009
  getSocialSettings,
10765
11010
  getChatSettings,
10766
11011
  getVisitorDeviceId: getDeviceId,
10767
- getMessagePreviewSetting,
11012
+ getMessagePreviewSetting: getMessagePreviewSetting$1,
10768
11013
  use: () => setActiveClient(client),
10769
11014
  isUnreadCountEnabled,
10770
11015
  useLegacyUnreadCount,
10771
11016
  getMarkerSyncConsistentMode,
10772
11017
  getProductCatalogueSetting,
11018
+ getForYouFeedSetting,
10773
11019
  /**
10774
11020
  * Prefix for the deviceId key in the local storage or async storage.
10775
11021
  * This is allow user to have multiple SDK client and Mqtt client within the same app.
@@ -11845,6 +12091,7 @@ var index$s = /*#__PURE__*/Object.freeze({
11845
12091
  getSocialSettings: getSocialSettings,
11846
12092
  getShareableLinkConfiguration: getShareableLinkConfiguration,
11847
12093
  getProductCatalogueSetting: getProductCatalogueSetting,
12094
+ getForYouFeedSetting: getForYouFeedSetting,
11848
12095
  loginAsVisitor: loginAsVisitor,
11849
12096
  loginAsBot: loginAsBot,
11850
12097
  getCurrentUser: getCurrentUser,
@@ -11874,19 +12121,6 @@ var index$s = /*#__PURE__*/Object.freeze({
11874
12121
  getMarkerSyncConsistentMode: getMarkerSyncConsistentMode
11875
12122
  });
11876
12123
 
11877
- function prepareFollowersPayload(response) {
11878
- const { users, follows } = response, rest = __rest(response, ["users", "follows"]);
11879
- return Object.assign(Object.assign({}, rest), { follows, users: users.map(convertRawUserToInternalUser) });
11880
- }
11881
- function prepareFollowingsPayload(response) {
11882
- const { users, follows } = response, rest = __rest(response, ["users", "follows"]);
11883
- return Object.assign(Object.assign({}, rest), { follows, users: users.map(convertRawUserToInternalUser) });
11884
- }
11885
- function prepareFollowStatusPayload(response) {
11886
- const { follows } = response, rest = __rest(response, ["follows"]);
11887
- return Object.assign(Object.assign({}, rest), { follows });
11888
- }
11889
-
11890
12124
  /* begin_public_function
11891
12125
  id: user.relationship.block
11892
12126
  */
@@ -11962,75 +12196,14 @@ const unBlockUser = async (userId) => {
11962
12196
  };
11963
12197
  /* end_public_function */
11964
12198
 
12199
+ /* begin_public_function
12200
+ id: user.relationship.follow
12201
+ */
11965
12202
  /**
11966
- * Subscribes to a block MQTT event (`user.didBlock` / `user.didUnblock`),
11967
- * ingests the carried `FollowersPayload` (follows + users) into the cache so
11968
- * the block-relationship rows (`['follow', 'get', from#to]`) and user rows are
11969
- * kept current, then forwards the affected {@link Amity.FollowStatus} to the
11970
- * caller.
11971
- *
11972
- * @hidden
11973
- */
11974
- const createBlockEventSubscriber = (event, callback) => {
11975
- const client = getActiveClient();
11976
- const filter = (data) => {
11977
- const payload = prepareFollowersPayload(data);
11978
- if (client.cache) {
11979
- ingestInCache(payload);
11980
- }
11981
- callback(payload.follows[0]);
11982
- };
11983
- return createEventSubscriber(client, event, event, filter);
11984
- };
11985
-
11986
- /**
11987
- * ```js
11988
- * import { onUserDidBlock } from '@amityco/ts-sdk'
11989
- * const dispose = onUserDidBlock(status => {
11990
- * // ...
11991
- * })
11992
- * ```
11993
- *
11994
- * Fired when a block relationship is created in real time — either a user
11995
- * blocks the current user (incoming) or the current user blocks someone on
11996
- * another device (outgoing). The local block-relationship store is updated
11997
- * before the callback runs.
11998
- *
11999
- * @param callback The function to call when the event was fired
12000
- * @returns an {@link Amity.Unsubscriber} function to stop listening
12001
- *
12002
- * @category Block Events
12003
- */
12004
- const onUserDidBlock = (callback) => createBlockEventSubscriber('user.didBlock', callback);
12005
-
12006
- /**
12007
- * ```js
12008
- * import { onUserDidUnblock } from '@amityco/ts-sdk'
12009
- * const dispose = onUserDidUnblock(status => {
12010
- * // ...
12011
- * })
12012
- * ```
12013
- *
12014
- * Fired when a block relationship is removed in real time — either a user
12015
- * unblocks the current user (incoming) or the current user unblocks someone on
12016
- * another device (outgoing). The local block-relationship store is updated
12017
- * before the callback runs.
12018
- *
12019
- * @param callback The function to call when the event was fired
12020
- * @returns an {@link Amity.Unsubscriber} function to stop listening
12021
- *
12022
- * @category Block Events
12023
- */
12024
- const onUserDidUnblock = (callback) => createBlockEventSubscriber('user.didUnblock', callback);
12025
-
12026
- /* begin_public_function
12027
- id: user.relationship.follow
12028
- */
12029
- /**
12030
- * ```js
12031
- * import { follow } from '@amityco/ts-sdk'
12032
- * const status = await follow('foobar')
12033
- * ```
12203
+ * ```js
12204
+ * import { follow } from '@amityco/ts-sdk'
12205
+ * const status = await follow('foobar')
12206
+ * ```
12034
12207
  *
12035
12208
  * Follow the user
12036
12209
  *
@@ -13956,7 +14129,7 @@ const getWatchSessionStorage = () => {
13956
14129
  return storageInstance;
13957
14130
  };
13958
14131
 
13959
- const privateKey = "MIIEpQIBAAKCAQEAwAEc/oZgYIvKSUG/C3mONYLR4ZPgAjMEX4bJ+xqqakUDRtqlNO+eZs2blQ1Ko0DBkqPExyQezvjibH5W2UZBV5RaBTlTcNVKTToMBEGesAfaEcM3qUyQHxdbFYZv6P4sb14dcwxTQ8usmaV8ooiR1Fcaso5ZWYcZ8Hb46FbQ7OoVumsBtPWwfZ4f003o5VCl6AIM6lcLv9UDLlFVYhE+PeXpRHtfWlGqxMvqC9oinlwhL6nWv6VjQXW4nhcib72dPBzfHT7k/PMKto2SxALYdb68ENiAGuJLWi3AUHSyYCJK2w7wIlWfJUAI0v26ub10IpExr6D5QuW2577jjP93iwIDAQABAoIBAFWfqXhwIIatkFY+9Z1+ZcbDQimgsmMIsUiQaX6Lk7e0cxOj6czDlxYtVtaPiNtow2pLkjNkjkCqiP7tEHnwdK9DvylZOTa2R15NJpK3WLcTqVIGhsn/FL5owfvFah6zSsmXZParZm5zY9NZE03ALZhOB9/cz0e3kf/EbpfeL2mW7MApyiUt5i09ycchroOpcWp73ipIxvgigtZyUGFmsQicWhUs28F0D7w4Qfk76yG3nqXeb+BAMhCaIaa/k/aAxhiZG/ygEQWQrcC8gfe+jyicMAQPDEVS9YuUMGsLjIjKuVLZzp2xirQnhc2i2zVNEIvG6soprPOBEMQugzrtX5ECgYEA3b7KAbBIbDl1e4ZSCWhHdHkiWVZHaopsR/LhqDDNhXjWjq3AesgV6k0j9EdziMn/HmmOso0bz99GTV3JZf4A9ztTLumJlkHbdVtlgOqSjrFLj12rH9KXTheyIhWSpUmm8+WB1xasFbqpvJaGo7F3pd2Fqj1XR4mp5BO7c/t7LJ0CgYEA3aouEzXQ9THRKYocdfY69EI1Il1t/d/RSqqd9BxEjxBgxkM13ZiYIn/R4WW/nCUrlmhxG44Aa2Gob4Ahfsui2xKTg/g/3Zk/rAxAEGkfOLGoenaJMD41fH4wUq3FRYwkvnaMb9Hd6f/TlBHslIRa2NN58bSBGJCyBP2b59+2+EcCgYEAixDVRXvV37GlYUOa/XVdosk5Zoe6oDGRuQm0xbNdoUBoZvDHDvme7ONWEiQha/8qtVsD+CyQ7awcPfb8kK9c0bBt+bTS6d4BkTcxkEkMgtrkBVR8Nqfu5jXsLH4VCv4G61zbMhZw8+ut+az5YX2yCN7Frj9sFlxapMRPQmzMEe0CgYEAumsAzM8ZqNv4mAK65Mnr0rhLj1cbxcKRdUYACOgtEFQpzxN/HZnTeFAe5nx3pI3uFlRHq3DFEYnT6dHMWaJQmAULYpVIwMi9L6gtyJ9fzoI6uqMtxRDMUqKdaSsTGOY/kJ6KhQ/unXi1K3XXjR+yd1+C0q+HUm1+CYxvrZYLfskCgYEArsEy+IQOiqniJ0NE2vVUF+UK/IRZaic9YKcpov5Ot7Vvzm/MnnW4N1ljVskocETBWMmPUvNSExVjPebi+rxd8fa5kY8BJScPTzMFbunZn/wjtGdcM10qdlVQ9doG61A/9P3ezFKCfS4AvF/H/59LcSx2Bh28fp3/efiVIOpVd4Y=";
14132
+ const privateKey = "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHo80SecH7FuF2\nhFYnb+l26/VN8UMLXAQFLnxciNTEwkGVFMpdezlH8rU2HtUJL4RETogbAOLVY0XM\njs6sPn8G1nALmh9qeDpUtVqFOVtBHxEZ910TLOtQiunjqJKO5nWdqZ71EC3OFluR\niGQkO84BiIFbv37ub7xl3S8XarbtKoLcyVpkDHi+1wx1pgCAn6gtBUgckPL5NR8j\nLseabl3HAXQfhTCKo4tmOFM2Dxwl1IUMmIJrJg/aIU/U0tj/1Eoo7mG0JcNWX19l\nW3EecCbi0ncCJOrkUdwlBrcjaMayaX/ubEwyUeTGiLdyc4L3GRLHjyK8xgVNXRMH\nbZWJ2a5NAgMBAAECggEASxuE+35zTFO/XydKgmvIGcWL9FbgMlXb7Vcf0nBoG945\nbiz0NVc2paraIhJXc608xbYF3qLmtAE1MVBI0ORyRdBHNxY024l/6H6SH60Ed+uI\nM4ysp5ourY6Vj+DLwpdRiI9YDjqYAQDIUmhNxJP7XPhOMoZI6st+xZQBM34ic/bv\nAMSJm9OZphSp3+qXVkFZztr2mxD2EZSJJLYxi8BCdgM2qhazalbcJ6zDKHCZWVWm\n8RRxDGldyMb/237JxETzP40tAlzOZDmBAbUgEnurDJ93RVDIE3rbZUshwgeQd18a\nem096mWgvB1AIKYgsTAR3pw+V19YWAjq/glP6fz8wQKBgQD/oQq+ukKF0PRgBeM5\ngeTjSwsdGppQLmf5ndujvoiz/TpdjDEPu6R8kigQr1rG2t4K/yfdZoI8RdmJD1al\n3Q7N9hofooSy4rj6E3txzWZCHJjHad2cnCp/O26HiReGAl7wTcfTmNdiFHhZQzm5\nJBkvWAiwuvQMNfEbnXxw6/vIDwKBgQDH7fX8gsc77JLvAWgp1MaQN/sbqVb6JeT1\nFQfR8E/WFCSmzQBtNzd5KgYuCeelwr/8DyYytvN2BzCYZXp73gI1jF3YlW5jVn74\nOY6TwQ095digwo6Z0yuxopdIOApKgAkL9PRKgNrqAf3NAyMua6lOGifzjDojC3KU\nfylQmxMn4wKBgHp2B9O/H0dEBw5JQ8W0+JX6yWQz7mEjGiR2/1W+XXb8hQ1zr709\nw1r6Gb+EghRpnZ3fBpYGGbYOMFx8wKHM+N6qW3F0ReX8v2juFGE8aRSa5oYBrWzt\nU16Idjbv8hj84cZ1PJmdyvDtpYn9rpWHOZl4rxEbPvbqkIsOMyNVqdT5AoGAOSge\nmwIIU2le2FVeohbibXiToWTYKMuMmURZ5/r72AgKMmWJKbAPe+Q3wBG01/7FRBpQ\noU8Ma0HC8s6QJbliiEyIx9JwrJWd1vkdecBHONrtA4ibm/5zD2WcOllLF+FitLhi\n3qnX6+6F0IaFGFBPJrTzlv0P4dTz/OAdv52V7GECgYEA2TttOKBAqWllgOaZOkql\nLVMJVmgR7s6tLi1+cEP8ZcapV9aRbRzTAKXm4f8AEhtlG9F9kCOvHYCYGi6JaiWJ\nZkHjeex3T+eE6Di6y5Bm/Ift5jtVhJ4jCVwHOKTMej79NPUFTJfv8hCo29haBDv6\nRXFrv+T21KCcw8k3sJeJWWQ=\n-----END PRIVATE KEY-----";
13960
14133
  /*
13961
14134
  * The crypto algorithm used for importing key and signing string
13962
14135
  */
@@ -16505,7 +16678,38 @@ const getAllBlockedUsers = async () => {
16505
16678
  const engine = BlockedUserSyncEngine$1.getInstance();
16506
16679
  // Ensure data is fetched (no-op if cache is fresh)
16507
16680
  await engine.ensureFetched();
16508
- // Read from the engine's bounded ID list — O(blocked users) not O(cache size)
16681
+ return engine.getCachedUsers();
16682
+ };
16683
+ /* end_public_function */
16684
+
16685
+ /* begin_public_function
16686
+ id: user.get_all_blocking_users
16687
+ */
16688
+ /**
16689
+ * ```js
16690
+ * import { UserRepository } from '@amityco/ts-sdk'
16691
+ * const blockingUsers = await UserRepository.getAllBlockingUsers()
16692
+ * ```
16693
+ *
16694
+ * Returns all users who have blocked the current user
16695
+ * non-paginated list (limit 100) with TTL-based caching (5-minute window). Within the
16696
+ * TTL window, subsequent calls resolve instantly from the local store without any
16697
+ * server call. After the TTL expires, the next call triggers a fresh server fetch.
16698
+ *
16699
+ * The directional inverse of {@link getAllBlockedUsers}.
16700
+ *
16701
+ * @returns A promise that resolves to an array of blocking {@link Amity.User} objects
16702
+ *
16703
+ * @category Block API
16704
+ * @async
16705
+ */
16706
+ const getAllBlockingUsers = async () => {
16707
+ const client = getActiveClient();
16708
+ client.log('user/getAllBlockingUsers');
16709
+ const engine = BlockingUserSyncEngine$1.getInstance();
16710
+ // Ensure data is fetched (no-op if cache is fresh)
16711
+ await engine.ensureFetched();
16712
+ // Read from the engine's bounded ID list — O(blocking users) not O(cache size)
16509
16713
  return engine.getCachedUsers();
16510
16714
  };
16511
16715
  /* end_public_function */
@@ -17084,14 +17288,6 @@ const getBlockedUsers = (params, callback, config) => {
17084
17288
  };
17085
17289
  /* end_public_function */
17086
17290
 
17087
- function prepareBlockingUserPayload(response) {
17088
- const { users, follows } = response, rest = __rest(response, ["users", "follows"]);
17089
- return Object.assign(Object.assign({}, rest), { follows: follows.map(follow => {
17090
- const followUser = users.find(user => user.userId === follow.from);
17091
- return Object.assign(Object.assign({}, follow), { user: convertRawUserToInternalUser(followUser) });
17092
- }), users: users.map(convertRawUserToInternalUser) });
17093
- }
17094
-
17095
17291
  class BlockingUserQueryStreamController extends QueryStreamController {
17096
17292
  constructor(query, cacheKey, notifyChange, preparePayload) {
17097
17293
  super(query, cacheKey);
@@ -17536,6 +17732,7 @@ var index$q = /*#__PURE__*/Object.freeze({
17536
17732
  unflagUser: unflagUser,
17537
17733
  isUserFlaggedByMe: isUserFlaggedByMe,
17538
17734
  getAllBlockedUsers: getAllBlockedUsers,
17735
+ getAllBlockingUsers: getAllBlockingUsers,
17539
17736
  onUserUpdated: onUserUpdated,
17540
17737
  onUserDeleted: onUserDeleted$2,
17541
17738
  onUserFlagged: onUserFlagged,
@@ -23645,6 +23842,405 @@ const getArchivedChannels = (params, callback) => {
23645
23842
  };
23646
23843
  /* end_public_function */
23647
23844
 
23845
+ class SearchChannelPaginationController extends PaginationController {
23846
+ async getRequest(queryParams, token) {
23847
+ const { query, exactMatch, isMemberOnly, types, tags, sortBy, orderBy, limit = COLLECTION_DEFAULT_PAGINATION_SIZE, } = queryParams;
23848
+ const options = token ? { token } : { limit: Math.min(100, Math.max(1, limit)) };
23849
+ const { data: queryResponse } = await this.http.get(`/api/v2/search/channels`, {
23850
+ params: {
23851
+ query,
23852
+ exactMatch,
23853
+ isMemberOnly,
23854
+ types,
23855
+ tags,
23856
+ sortBy,
23857
+ orderBy,
23858
+ options,
23859
+ },
23860
+ });
23861
+ return queryResponse;
23862
+ }
23863
+ }
23864
+
23865
+ class SearchChannelQueryStreamController extends QueryStreamController {
23866
+ constructor(query, cacheKey, notifyChange) {
23867
+ super(query, cacheKey);
23868
+ this.notifyChange = notifyChange;
23869
+ }
23870
+ async saveToMainDB(response) {
23871
+ const processedPayload = await prepareChannelPayload(response);
23872
+ const client = getActiveClient();
23873
+ const cachedAt = client.cache && Date.now();
23874
+ if (client.cache) {
23875
+ ingestInCache(processedPayload, { cachedAt });
23876
+ }
23877
+ }
23878
+ appendToQueryStream(response, direction, refresh = false) {
23879
+ var _a, _b;
23880
+ if (refresh) {
23881
+ pushToCache(this.cacheKey, {
23882
+ data: response.channels.map(getResolver('channel')),
23883
+ });
23884
+ return;
23885
+ }
23886
+ const collection = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
23887
+ const existing = (_b = collection === null || collection === void 0 ? void 0 : collection.data) !== null && _b !== void 0 ? _b : [];
23888
+ const incoming = response.channels.map(getResolver('channel'));
23889
+ pushToCache(this.cacheKey, Object.assign(Object.assign({}, collection), { data: direction === "prev" /* Amity.LiveCollectionPageDirection.PREV */
23890
+ ? [...new Set([...incoming, ...existing])]
23891
+ : [...new Set([...existing, ...incoming])] }));
23892
+ }
23893
+ reactor() {
23894
+ return () => this.notifyChange({ origin: "event" /* Amity.LiveDataOrigin.EVENT */, loading: false });
23895
+ }
23896
+ subscribeRTE(createSubscriber) {
23897
+ return createSubscriber.map(subscriber => subscriber.fn(this.reactor()));
23898
+ }
23899
+ }
23900
+
23901
+ /* eslint-disable no-use-before-define */
23902
+ const getMessagePreviewSetting = () => {
23903
+ const client = getActiveClient();
23904
+ return client.getMessagePreviewSetting(false);
23905
+ };
23906
+ class SearchChannelLiveCollectionController extends LiveCollectionController {
23907
+ constructor(query, callback) {
23908
+ const queryStreamId = hash__default["default"](query);
23909
+ const cacheKey = ['channel', 'search', queryStreamId];
23910
+ const paginationController = new SearchChannelPaginationController(query);
23911
+ super(paginationController, queryStreamId, cacheKey, callback);
23912
+ this.queryStreamController = new SearchChannelQueryStreamController(query, this.cacheKey, this.notifyChange.bind(this));
23913
+ this.callback = callback.bind(this);
23914
+ this.loadPage({ initial: true });
23915
+ }
23916
+ startSubscription() {
23917
+ return this.queryStreamController.subscribeRTE(this.getSubscriptions());
23918
+ }
23919
+ notifyChange({ origin, loading, error }) {
23920
+ var _a, _b;
23921
+ const collection = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
23922
+ if (!collection)
23923
+ return;
23924
+ const data = (_b = collection.data
23925
+ .map(channelId => pullFromCache(['channel', 'get', channelId]))
23926
+ .filter(Boolean)
23927
+ .map(cached => constructChannelObject(cached.data))) !== null && _b !== void 0 ? _b : [];
23928
+ if (!this.shouldNotify(data) && origin === 'event')
23929
+ return;
23930
+ this.callback({
23931
+ onNextPage: () => this.loadPage({ direction: "next" /* Amity.LiveCollectionPageDirection.NEXT */ }),
23932
+ data,
23933
+ hasNextPage: !!this.paginationController.getNextToken(),
23934
+ loading,
23935
+ error,
23936
+ });
23937
+ }
23938
+ setup() {
23939
+ var _a;
23940
+ const collection = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
23941
+ if (!collection) {
23942
+ pushToCache(this.cacheKey, {
23943
+ data: [],
23944
+ params: {},
23945
+ });
23946
+ }
23947
+ }
23948
+ async persistModel(response) {
23949
+ await this.queryStreamController.saveToMainDB(response);
23950
+ }
23951
+ persistQueryStream({ response, direction, refresh, }) {
23952
+ this.queryStreamController.appendToQueryStream(response, direction, refresh);
23953
+ }
23954
+ shouldAbort(targetChannelId) {
23955
+ var _a;
23956
+ const collection = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
23957
+ if (!collection)
23958
+ return true;
23959
+ return !collection.data.find(channelId => channelId === targetChannelId);
23960
+ }
23961
+ getSubscriptions() {
23962
+ return [
23963
+ {
23964
+ fn: convertEventPayload((callback) => {
23965
+ return onMessageCreatedMqtt(message => {
23966
+ var _a;
23967
+ const cacheData = (_a = pullFromCache([
23968
+ 'channel',
23969
+ 'get',
23970
+ message.channelId,
23971
+ ])) === null || _a === void 0 ? void 0 : _a.data;
23972
+ if (!cacheData)
23973
+ return;
23974
+ updateChannelCache(cacheData, {
23975
+ lastActivity: message.createdAt,
23976
+ });
23977
+ callback(message);
23978
+ });
23979
+ }, 'channelId', 'channel'),
23980
+ action: "onUpdate" /* Amity.ChannelActionType.OnUpdate */,
23981
+ },
23982
+ {
23983
+ fn: convertEventPayload((callback) => {
23984
+ return onMessageCreatedLocal(message => {
23985
+ var _a;
23986
+ const cacheData = (_a = pullFromCache([
23987
+ 'channel',
23988
+ 'get',
23989
+ message.channelId,
23990
+ ])) === null || _a === void 0 ? void 0 : _a.data;
23991
+ if (!cacheData)
23992
+ return;
23993
+ updateChannelCache(cacheData, {
23994
+ lastActivity: message.createdAt,
23995
+ });
23996
+ callback(message);
23997
+ });
23998
+ }, 'channelId', 'channel'),
23999
+ action: "onUpdate" /* Amity.ChannelActionType.OnUpdate */,
24000
+ },
24001
+ { fn: onChannelDeleted, action: "onDelete" /* Amity.ChannelActionType.OnDelete */ },
24002
+ { fn: onChannelUpdated, action: "onUpdate" /* Amity.ChannelActionType.OnUpdate */ },
24003
+ { fn: onChannelSetMuted, action: "onMute" /* Amity.ChannelActionType.OnMute */ },
24004
+ {
24005
+ fn: (reactor) => onChannelArchived(payload => {
24006
+ const channels = payload.archives
24007
+ .map(archive => {
24008
+ var _a;
24009
+ return (_a = pullFromCache(['channel', 'get', archive.channelId])) === null || _a === void 0 ? void 0 : _a.data;
24010
+ })
24011
+ .filter((c) => Boolean(c));
24012
+ if (channels.length > 0)
24013
+ reactor(channels);
24014
+ }),
24015
+ action: "onArchived" /* Amity.ChannelActionType.OnArchived */,
24016
+ },
24017
+ {
24018
+ fn: (reactor) => onChannelUnarchived(payload => {
24019
+ const channels = payload.archives
24020
+ .map(archive => {
24021
+ var _a;
24022
+ return (_a = pullFromCache(['channel', 'get', archive.channelId])) === null || _a === void 0 ? void 0 : _a.data;
24023
+ })
24024
+ .filter((c) => Boolean(c));
24025
+ if (channels.length > 0)
24026
+ reactor(channels);
24027
+ }),
24028
+ action: "onUnarchived" /* Amity.ChannelActionType.OnUnarchived */,
24029
+ },
24030
+ {
24031
+ fn: convertEventPayload(onChannelMarkerFetched, 'entityId', 'channel'),
24032
+ action: "onUpdate" /* Amity.ChannelActionType.OnUpdate */,
24033
+ },
24034
+ {
24035
+ fn: convertEventPayload(onChannelMarkerUpdated, 'entityId', 'channel'),
24036
+ action: "onUpdate" /* Amity.ChannelActionType.OnUpdate */,
24037
+ },
24038
+ {
24039
+ fn: convertEventPayload((callback) => {
24040
+ const updateMessagePreview = async (message) => {
24041
+ var _a;
24042
+ const messagePreviewSetting = await getMessagePreviewSetting();
24043
+ if (messagePreviewSetting === "no-message-preview" /* Amity.MessagePreviewSetting.NO_MESSAGE_PREVIEW */)
24044
+ return;
24045
+ handleMessageCreated(message);
24046
+ if (this.shouldAbort(message.channelId))
24047
+ return;
24048
+ const channel = (_a = pullFromCache([
24049
+ 'channel',
24050
+ 'get',
24051
+ message.channelId,
24052
+ ])) === null || _a === void 0 ? void 0 : _a.data;
24053
+ if (!channel)
24054
+ return;
24055
+ updateChannelCache(channel, {
24056
+ messagePreviewId: message.messageId,
24057
+ });
24058
+ callback(message);
24059
+ };
24060
+ return onMessageCreatedMqtt(updateMessagePreview);
24061
+ }, 'channelId', 'channel'),
24062
+ action: "onUpdate" /* Amity.ChannelActionType.OnUpdate */,
24063
+ },
24064
+ {
24065
+ fn: convertEventPayload((callback) => {
24066
+ const updateMessagePreview = async (message) => {
24067
+ var _a;
24068
+ const messagePreviewSetting = await getMessagePreviewSetting();
24069
+ if (messagePreviewSetting === "no-message-preview" /* Amity.MessagePreviewSetting.NO_MESSAGE_PREVIEW */)
24070
+ return;
24071
+ handleMessageCreated(message);
24072
+ if (this.shouldAbort(message.channelId))
24073
+ return;
24074
+ const channel = (_a = pullFromCache([
24075
+ 'channel',
24076
+ 'get',
24077
+ message.channelId,
24078
+ ])) === null || _a === void 0 ? void 0 : _a.data;
24079
+ if (!channel)
24080
+ return;
24081
+ updateChannelCache(channel, {
24082
+ messagePreviewId: message.messageId,
24083
+ });
24084
+ callback(message);
24085
+ };
24086
+ return onMessageCreatedLocal(updateMessagePreview);
24087
+ }, 'channelId', 'channel'),
24088
+ action: "onUpdate" /* Amity.ChannelActionType.OnUpdate */,
24089
+ },
24090
+ {
24091
+ fn: convertEventPayload((callback) => {
24092
+ const updateMessagePreview = async (message) => {
24093
+ const messagePreviewSetting = await getMessagePreviewSetting();
24094
+ if (messagePreviewSetting === "no-message-preview" /* Amity.MessagePreviewSetting.NO_MESSAGE_PREVIEW */)
24095
+ return;
24096
+ handleMessageUpdated(message);
24097
+ if (this.shouldAbort(message.channelId))
24098
+ return;
24099
+ callback(message);
24100
+ };
24101
+ return onMessageUpdated(updateMessagePreview);
24102
+ }, 'channelId', 'channel'),
24103
+ action: "onUpdate" /* Amity.ChannelActionType.OnUpdate */,
24104
+ },
24105
+ {
24106
+ fn: convertEventPayload((callback) => {
24107
+ const updateMessagePreview = async (message) => {
24108
+ const messagePreviewSetting = await getMessagePreviewSetting();
24109
+ if (messagePreviewSetting === "no-message-preview" /* Amity.MessagePreviewSetting.NO_MESSAGE_PREVIEW */)
24110
+ return;
24111
+ if (messagePreviewSetting ===
24112
+ "message-preview-include-deleted" /* Amity.MessagePreviewSetting.MESSAGE_PREVIEW_INCLUDE_DELETED */)
24113
+ await handleMessageUpdated(message);
24114
+ if (this.shouldAbort(message.channelId))
24115
+ return;
24116
+ if (messagePreviewSetting ===
24117
+ "message-preview-not-include-deleted" /* Amity.MessagePreviewSetting.MESSAGE_PREVIEW_NOT_INCLUDE_DELETED */) {
24118
+ await getChannel$1(message.channelId);
24119
+ }
24120
+ callback(message);
24121
+ };
24122
+ return onMessageDeleted(updateMessagePreview);
24123
+ }, 'channelId', 'channel'),
24124
+ action: "onUpdate" /* Amity.ChannelActionType.OnUpdate */,
24125
+ },
24126
+ {
24127
+ fn: convertEventPayload((callback) => {
24128
+ const updateMessagePreview = async (subChannel) => {
24129
+ var _a;
24130
+ const collections = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
24131
+ if (!collections)
24132
+ return;
24133
+ const channelId = collections.data.find(channel => {
24134
+ const messagePreviewCache = getChannelMessagePreview(channel);
24135
+ return (messagePreviewCache === null || messagePreviewCache === void 0 ? void 0 : messagePreviewCache.subChannelId) === subChannel.subChannelId;
24136
+ });
24137
+ if (!channelId)
24138
+ return;
24139
+ await getChannel$1(subChannel.channelId);
24140
+ callback(subChannel);
24141
+ };
24142
+ return onSubChannelDeleted(updateMessagePreview);
24143
+ }, 'channelId', 'channel'),
24144
+ action: "onUpdate" /* Amity.ChannelActionType.OnUpdate */,
24145
+ },
24146
+ {
24147
+ fn: convertEventPayload((callback) => {
24148
+ const updateMessagePreview = async (subChannel) => {
24149
+ var _a;
24150
+ const messagePreviewSetting = await getMessagePreviewSetting();
24151
+ if (messagePreviewSetting === "no-message-preview" /* Amity.MessagePreviewSetting.NO_MESSAGE_PREVIEW */)
24152
+ return;
24153
+ const collections = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
24154
+ if (!collections)
24155
+ return;
24156
+ await handleSubChannelUpdated(subChannel);
24157
+ if (this.shouldAbort(subChannel.channelId))
24158
+ return;
24159
+ callback(subChannel);
24160
+ };
24161
+ return onSubChannelUpdated(updateMessagePreview);
24162
+ }, 'channelId', 'channel'),
24163
+ action: "onUpdate" /* Amity.ChannelActionType.OnUpdate */,
24164
+ },
24165
+ {
24166
+ fn: (callback) => {
24167
+ const handleUserFeedMarkerResolved = async (marker) => {
24168
+ if (marker.feedMarkers) {
24169
+ const channels = marker.feedMarkers
24170
+ .map(feedMarker => {
24171
+ var _a;
24172
+ return (_a = pullFromCache([
24173
+ 'channel',
24174
+ 'get',
24175
+ feedMarker.entityId,
24176
+ ])) === null || _a === void 0 ? void 0 : _a.data;
24177
+ })
24178
+ .filter(Boolean);
24179
+ callback(channels);
24180
+ }
24181
+ };
24182
+ return onUserMessageFeedMarkerResolved(handleUserFeedMarkerResolved);
24183
+ },
24184
+ action: "OnResolveUnread" /* Amity.ChannelActionType.OnResolveUnread */,
24185
+ },
24186
+ {
24187
+ fn: convertEventPayload(onChannelUnreadInfoUpdatedLocal, 'channelId', 'channel'),
24188
+ action: "onUpdate" /* Amity.ChannelActionType.OnUpdate */,
24189
+ },
24190
+ {
24191
+ fn: convertEventPayload((callback) => {
24192
+ const processPayload = (payloads) => {
24193
+ payloads.forEach(callback);
24194
+ };
24195
+ return onChannelUnreadUpdatedLocal(processPayload);
24196
+ }, 'channelId', 'channel'),
24197
+ action: "onUpdate" /* Amity.ChannelActionType.OnUpdate */,
24198
+ },
24199
+ ];
24200
+ }
24201
+ }
24202
+
24203
+ /* begin_public_function
24204
+ id: channel.search
24205
+ */
24206
+ /**
24207
+ * ```js
24208
+ * import { ChannelRepository } from '@amityco/ts-sdk';
24209
+ *
24210
+ * const unsubscribe = ChannelRepository.searchChannels(
24211
+ * { query: 'hello' },
24212
+ * response => response.data,
24213
+ * );
24214
+ * ```
24215
+ *
24216
+ * Live collection of {@link Amity.Channel}s matching the search query. Backed by
24217
+ * `GET /api/v2/search/channels`.
24218
+ *
24219
+ * @param params Search parameters.
24220
+ * @param callback Called whenever new data are available.
24221
+ * @returns An {@link Amity.Unsubscriber} function to stop observing.
24222
+ *
24223
+ * @category Channel Live Collection
24224
+ */
24225
+ const searchChannels = (params, callback) => {
24226
+ const { log, cache } = getActiveClient();
24227
+ if (!cache)
24228
+ console.log(ENABLE_CACHE_MESSAGE);
24229
+ const timestamp = Date.now();
24230
+ log(`searchChannels(tmpid: ${timestamp}) > listen`);
24231
+ const searchChannelLiveCollection = new SearchChannelLiveCollectionController(params, callback);
24232
+ const disposers = searchChannelLiveCollection.startSubscription();
24233
+ const cacheKey = searchChannelLiveCollection.getCacheKey();
24234
+ disposers.push(() => {
24235
+ dropFromCache(cacheKey);
24236
+ });
24237
+ return () => {
24238
+ log(`searchChannels(tmpid: ${timestamp}) > dispose`);
24239
+ disposers.forEach(fn => fn());
24240
+ };
24241
+ };
24242
+ /* end_public_function */
24243
+
23648
24244
  /* begin_public_function
23649
24245
  id: channel.member.add
23650
24246
  */
@@ -24278,6 +24874,7 @@ var index$j = /*#__PURE__*/Object.freeze({
24278
24874
  getChannels: getChannels,
24279
24875
  getTotalChannelsUnread: getTotalChannelsUnread,
24280
24876
  getArchivedChannels: getArchivedChannels,
24877
+ searchChannels: searchChannels,
24281
24878
  MARKER_INCLUDED_CHANNEL_TYPE: MARKER_INCLUDED_CHANNEL_TYPE,
24282
24879
  isUnreadCountSupport: isUnreadCountSupport,
24283
24880
  convertFromRaw: convertFromRaw,
@@ -29349,45 +29946,80 @@ class ForYouFeedPaginationController extends PaginationController {
29349
29946
  constructor(query) {
29350
29947
  super(query);
29351
29948
  this.checkedDisk = false;
29949
+ this.hasDeliveredPost = false;
29352
29950
  this.cursor = CursorController.current();
29353
29951
  }
29354
29952
  async getRequest(_queryParams, token) {
29355
- let effectiveToken = token;
29356
- if (!effectiveToken && !this.checkedDisk && this.cursor) {
29953
+ var _a, _b, _c, _d, _e;
29954
+ const isFirstPage = token === undefined;
29955
+ let stored = null;
29956
+ // On the first page of a fresh collection, load the saved cursor from disk once.
29957
+ // Drop it if the snapshot has already expired so we start fresh.
29958
+ if (isFirstPage && !this.checkedDisk && this.cursor) {
29357
29959
  this.checkedDisk = true;
29358
- const stored = await this.cursor.get();
29359
- if (stored && !CursorController.isExpired(stored.expiredAt)) {
29360
- effectiveToken = stored.cursor;
29361
- }
29362
- else if (stored) {
29960
+ stored = await this.cursor.get();
29961
+ if (stored && CursorController.isExpired(stored.expiredAt)) {
29363
29962
  await this.cursor.clear();
29963
+ stored = null;
29364
29964
  }
29365
29965
  }
29366
- const options = {
29367
- limit: COLLECTION_DEFAULT_PAGINATION_SIZE,
29368
- };
29369
- if (effectiveToken)
29370
- options.token = effectiveToken;
29371
- let queryResponse;
29966
+ // Already paged to the end before. Ask the BE fresh (no cursor) and check if it's
29967
+ // still the same snapshot the user finished.
29968
+ if (isFirstPage && (stored === null || stored === void 0 ? void 0 : stored.reachedEndOfFeed)) {
29969
+ const fresh = await this.fetch();
29970
+ const freshExpiredAt = Date.parse((_b = (_a = fresh.paging) === null || _a === void 0 ? void 0 : _a.expiredAt) !== null && _b !== void 0 ? _b : '');
29971
+ const rolledOver = !Number.isNaN(freshExpiredAt) && freshExpiredAt !== stored.expiredAt;
29972
+ // New snapshot → serve it. Same snapshot → user has seen it all, show empty.
29973
+ if (rolledOver) {
29974
+ await this.persistCursor(fresh);
29975
+ if (fresh.posts.length > 0)
29976
+ this.hasDeliveredPost = true;
29977
+ return fresh;
29978
+ }
29979
+ return Object.assign(Object.assign({}, fresh), { posts: [], paging: {} });
29980
+ }
29981
+ // Resume from the saved cursor on re-entry; otherwise use the live-collection token.
29982
+ const effectiveToken = isFirstPage && stored ? stored.cursor : token;
29983
+ const response = await this.fetch(effectiveToken);
29984
+ if (response.posts.length > 0)
29985
+ this.hasDeliveredPost = true;
29986
+ if ((_c = response.paging) === null || _c === void 0 ? void 0 : _c.next) {
29987
+ // More pages left → advance the saved cursor.
29988
+ await this.persistCursor(response);
29989
+ }
29990
+ else if (this.hasDeliveredPost && this.cursor) {
29991
+ // Reached the end and mark end of feed
29992
+ const expiredAt = Date.parse((_e = (_d = response.paging) === null || _d === void 0 ? void 0 : _d.expiredAt) !== null && _e !== void 0 ? _e : '');
29993
+ if (!Number.isNaN(expiredAt))
29994
+ await this.cursor.markReachedEndOfFeed(expiredAt);
29995
+ }
29996
+ return response;
29997
+ }
29998
+ async fetch(token) {
29999
+ const options = { limit: COLLECTION_DEFAULT_PAGINATION_SIZE };
30000
+ if (token)
30001
+ options.token = token;
29372
30002
  try {
29373
- ({ data: queryResponse } = await this.http.get(API_ENDPOINTS.forYouFeed, { params: { options } }));
30003
+ const { data } = await this.http.get(API_ENDPOINTS.forYouFeed, {
30004
+ params: { options },
30005
+ });
30006
+ return data;
29374
30007
  }
29375
30008
  catch (error) {
29376
30009
  if (error instanceof ASCApiError && error.code === 400300 /* Amity.ServerError.FORBIDDEN */) {
29377
30010
  throw new AmityForYouFeedDisabledError();
29378
30011
  }
29379
30012
  const recoverable = error instanceof ASCApiError && error.code === 400322 /* Amity.ServerError.FEED_SNAPSHOT_EXPIRED */;
29380
- if (recoverable && effectiveToken !== undefined) {
30013
+ if (recoverable && token !== undefined) {
29381
30014
  if (this.cursor)
29382
30015
  await this.cursor.clear();
29383
- ({ data: queryResponse } = await this.http.get(API_ENDPOINTS.forYouFeed, { params: { options: { limit: COLLECTION_DEFAULT_PAGINATION_SIZE } } }));
29384
- }
29385
- else {
29386
- throw error;
30016
+ const { data } = await this.http.get(API_ENDPOINTS.forYouFeed, {
30017
+ params: { options: { limit: COLLECTION_DEFAULT_PAGINATION_SIZE } },
30018
+ });
30019
+ return data;
29387
30020
  }
30021
+ throw error;
29388
30022
  }
29389
- await this.persistCursor(queryResponse);
29390
- return queryResponse;
29391
30023
  }
29392
30024
  async persistCursor(response) {
29393
30025
  var _a, _b;