@amityco/ts-sdk-react-native 7.13.0 → 7.14.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 (102) hide show
  1. package/dist/@types/domains/client.d.ts +10 -1
  2. package/dist/@types/domains/client.d.ts.map +1 -1
  3. package/dist/@types/domains/feed.d.ts +17 -1
  4. package/dist/@types/domains/feed.d.ts.map +1 -1
  5. package/dist/@types/domains/message.d.ts +1 -1
  6. package/dist/@types/domains/message.d.ts.map +1 -1
  7. package/dist/@types/domains/post.d.ts +5 -0
  8. package/dist/@types/domains/post.d.ts.map +1 -1
  9. package/dist/client/api/accessTokenExpiryWatcher.d.ts +1 -1
  10. package/dist/client/api/accessTokenExpiryWatcher.d.ts.map +1 -1
  11. package/dist/client/api/index.d.ts +2 -0
  12. package/dist/client/api/index.d.ts.map +1 -1
  13. package/dist/client/api/isSameUserId.d.ts +2 -0
  14. package/dist/client/api/isSameUserId.d.ts.map +1 -0
  15. package/dist/client/api/login.d.ts.map +1 -1
  16. package/dist/client/api/loginAsBot.d.ts.map +1 -1
  17. package/dist/client/api/loginAsVisitor.d.ts.map +1 -1
  18. package/dist/client/api/loginWithAccessToken.d.ts +28 -0
  19. package/dist/client/api/loginWithAccessToken.d.ts.map +1 -0
  20. package/dist/client/api/logout.d.ts.map +1 -1
  21. package/dist/client/api/renewTokenWithHandler.d.ts +11 -0
  22. package/dist/client/api/renewTokenWithHandler.d.ts.map +1 -0
  23. package/dist/client/api/renewWithAccessToken.d.ts +2 -0
  24. package/dist/client/api/renewWithAccessToken.d.ts.map +1 -0
  25. package/dist/client/api/resumeSession.d.ts.map +1 -1
  26. package/dist/client/api/setAccessTokenHandler.d.ts +31 -0
  27. package/dist/client/api/setAccessTokenHandler.d.ts.map +1 -0
  28. package/dist/client/api/setupLoginSubscriptions.d.ts +11 -0
  29. package/dist/client/api/setupLoginSubscriptions.d.ts.map +1 -0
  30. package/dist/client/api/tests/loginWithAccessToken.test.d.ts +2 -0
  31. package/dist/client/api/tests/loginWithAccessToken.test.d.ts.map +1 -0
  32. package/dist/client/api/validateAccessToken.d.ts +11 -0
  33. package/dist/client/api/validateAccessToken.d.ts.map +1 -0
  34. package/dist/commentRepository/api/createComment.d.ts.map +1 -1
  35. package/dist/commentRepository/api/deleteComment.d.ts.map +1 -1
  36. package/dist/commentRepository/events/utils.d.ts.map +1 -1
  37. package/dist/core/subscription.d.ts +2 -2
  38. package/dist/core/subscription.d.ts.map +1 -1
  39. package/dist/feedRepository/index.d.ts +1 -1
  40. package/dist/feedRepository/index.d.ts.map +1 -1
  41. package/dist/feedRepository/observers/getCommunityFeed/LiveCollectionController.d.ts +13 -0
  42. package/dist/feedRepository/observers/getCommunityFeed/LiveCollectionController.d.ts.map +1 -0
  43. package/dist/feedRepository/observers/getCommunityFeed/PaginationController.d.ts +5 -0
  44. package/dist/feedRepository/observers/getCommunityFeed/PaginationController.d.ts.map +1 -0
  45. package/dist/feedRepository/observers/getCommunityFeed/QueryStreamController.d.ts +15 -0
  46. package/dist/feedRepository/observers/getCommunityFeed/QueryStreamController.d.ts.map +1 -0
  47. package/dist/feedRepository/observers/getCommunityFeed.d.ts +34 -0
  48. package/dist/feedRepository/observers/getCommunityFeed.d.ts.map +1 -0
  49. package/dist/feedRepository/observers/index.d.ts +1 -0
  50. package/dist/feedRepository/observers/index.d.ts.map +1 -1
  51. package/dist/feedRepository/observers/utils.d.ts.map +1 -1
  52. package/dist/index.cjs.js +995 -385
  53. package/dist/index.esm.js +979 -369
  54. package/dist/index.umd.js +3 -3
  55. package/dist/postRepository/events/utils.d.ts.map +1 -1
  56. package/dist/postRepository/observers/utils.d.ts.map +1 -1
  57. package/dist/postRepository/utils/PostCommentCountEngine/CommentChange.d.ts +11 -0
  58. package/dist/postRepository/utils/PostCommentCountEngine/CommentChange.d.ts.map +1 -0
  59. package/dist/postRepository/utils/PostCommentCountEngine/CreateTask.d.ts +8 -0
  60. package/dist/postRepository/utils/PostCommentCountEngine/CreateTask.d.ts.map +1 -0
  61. package/dist/postRepository/utils/PostCommentCountEngine/DeleteTask.d.ts +7 -0
  62. package/dist/postRepository/utils/PostCommentCountEngine/DeleteTask.d.ts.map +1 -0
  63. package/dist/postRepository/utils/PostCommentCountEngine/PostCommentCountEngine.d.ts +19 -0
  64. package/dist/postRepository/utils/PostCommentCountEngine/PostCommentCountEngine.d.ts.map +1 -0
  65. package/dist/postRepository/utils/PostCommentCountEngine/ResetTask.d.ts +8 -0
  66. package/dist/postRepository/utils/PostCommentCountEngine/ResetTask.d.ts.map +1 -0
  67. package/dist/postRepository/utils/payload.d.ts.map +1 -1
  68. package/package.json +3 -1
  69. package/dist/commentRepository/internalApi/createComment.d.ts +0 -2
  70. package/dist/commentRepository/internalApi/createComment.d.ts.map +0 -1
  71. package/dist/commentRepository/internalApi/deleteComment.d.ts +0 -2
  72. package/dist/commentRepository/internalApi/deleteComment.d.ts.map +0 -1
  73. package/dist/commentRepository/internalApi/flagComment.d.ts +0 -2
  74. package/dist/commentRepository/internalApi/flagComment.d.ts.map +0 -1
  75. package/dist/commentRepository/internalApi/hardDeleteComment.d.ts +0 -2
  76. package/dist/commentRepository/internalApi/hardDeleteComment.d.ts.map +0 -1
  77. package/dist/commentRepository/internalApi/isCommentFlaggedByMe.d.ts +0 -2
  78. package/dist/commentRepository/internalApi/isCommentFlaggedByMe.d.ts.map +0 -1
  79. package/dist/commentRepository/internalApi/queryComments.d.ts +0 -2
  80. package/dist/commentRepository/internalApi/queryComments.d.ts.map +0 -1
  81. package/dist/commentRepository/internalApi/softDeleteComment.d.ts +0 -2
  82. package/dist/commentRepository/internalApi/softDeleteComment.d.ts.map +0 -1
  83. package/dist/commentRepository/internalApi/tests/createComment.test.d.ts +0 -2
  84. package/dist/commentRepository/internalApi/tests/createComment.test.d.ts.map +0 -1
  85. package/dist/commentRepository/internalApi/tests/deleteComment.test.d.ts +0 -2
  86. package/dist/commentRepository/internalApi/tests/deleteComment.test.d.ts.map +0 -1
  87. package/dist/commentRepository/internalApi/tests/getCommentByIds.test.d.ts +0 -2
  88. package/dist/commentRepository/internalApi/tests/getCommentByIds.test.d.ts.map +0 -1
  89. package/dist/commentRepository/internalApi/tests/getComments.test.d.ts +0 -2
  90. package/dist/commentRepository/internalApi/tests/getComments.test.d.ts.map +0 -1
  91. package/dist/commentRepository/internalApi/tests/hardDeleteComment.test.d.ts +0 -2
  92. package/dist/commentRepository/internalApi/tests/hardDeleteComment.test.d.ts.map +0 -1
  93. package/dist/commentRepository/internalApi/tests/queryComments.test.d.ts +0 -2
  94. package/dist/commentRepository/internalApi/tests/queryComments.test.d.ts.map +0 -1
  95. package/dist/commentRepository/internalApi/tests/softDeleteComment.test.d.ts +0 -2
  96. package/dist/commentRepository/internalApi/tests/softDeleteComment.test.d.ts.map +0 -1
  97. package/dist/commentRepository/internalApi/tests/updateComment.test.d.ts +0 -2
  98. package/dist/commentRepository/internalApi/tests/updateComment.test.d.ts.map +0 -1
  99. package/dist/commentRepository/internalApi/unflagComment.d.ts +0 -2
  100. package/dist/commentRepository/internalApi/unflagComment.d.ts.map +0 -1
  101. package/dist/commentRepository/internalApi/updateComment.d.ts +0 -2
  102. package/dist/commentRepository/internalApi/updateComment.d.ts.map +0 -1
package/dist/index.cjs.js CHANGED
@@ -224,6 +224,12 @@ exports.FeedSourceEnum = void 0;
224
224
  FeedSourceEnum["Community"] = "community";
225
225
  FeedSourceEnum["User"] = "user";
226
226
  })(exports.FeedSourceEnum || (exports.FeedSourceEnum = {}));
227
+ exports.FeedTypeEnum = void 0;
228
+ (function (FeedTypeEnum) {
229
+ FeedTypeEnum["Reviewing"] = "reviewing";
230
+ FeedTypeEnum["Published"] = "published";
231
+ FeedTypeEnum["Declined"] = "declined";
232
+ })(exports.FeedTypeEnum || (exports.FeedTypeEnum = {}));
227
233
 
228
234
  exports.AmityEventType = void 0;
229
235
  (function (AmityEventType) {
@@ -260,8 +266,8 @@ exports.AmityEventOrderOption = void 0;
260
266
 
261
267
  function getVersion() {
262
268
  try {
263
- // the string ''v7.13.0-cjs'' should be replaced by actual value by @rollup/plugin-replace
264
- return 'v7.13.0-cjs';
269
+ // the string ''v7.14.0-cjs'' should be replaced by actual value by @rollup/plugin-replace
270
+ return 'v7.14.0-cjs';
265
271
  }
266
272
  catch (error) {
267
273
  return '__dev__';
@@ -1715,10 +1721,14 @@ const getLiveReactionTopic = (post) => {
1715
1721
  };
1716
1722
  const getRoomWatcherTopic = (room) => {
1717
1723
  const user = getCurrentUser();
1724
+ if (!user)
1725
+ return;
1718
1726
  return `${getNetworkId(user)}/room/${room._id}`;
1719
1727
  };
1720
1728
  const getRoomStreamerTopic = (room) => {
1721
1729
  const user = getCurrentUser();
1730
+ if (!user)
1731
+ return;
1722
1732
  return `${getNetworkId(user)}/room/${room.roomId}/streamer`;
1723
1733
  };
1724
1734
  function subscribeTopic(topic, callback) {
@@ -1813,13 +1823,13 @@ class NetworkActivitiesWatcher {
1813
1823
  this._listener.clear();
1814
1824
  }
1815
1825
  }
1816
- let instance$7;
1826
+ let instance$8;
1817
1827
  var NetworkActivitiesWatcher$1 = {
1818
1828
  getInstance: () => {
1819
- if (!instance$7) {
1820
- instance$7 = new NetworkActivitiesWatcher();
1829
+ if (!instance$8) {
1830
+ instance$8 = new NetworkActivitiesWatcher();
1821
1831
  }
1822
- return instance$7;
1832
+ return instance$8;
1823
1833
  },
1824
1834
  };
1825
1835
 
@@ -6914,13 +6924,13 @@ class SessionWatcher {
6914
6924
  this._listener.clear();
6915
6925
  }
6916
6926
  }
6917
- let instance$6;
6927
+ let instance$7;
6918
6928
  var SessionWatcher$1 = {
6919
6929
  getInstance: () => {
6920
- if (!instance$6) {
6921
- instance$6 = new SessionWatcher();
6930
+ if (!instance$7) {
6931
+ instance$7 = new SessionWatcher();
6922
6932
  }
6923
- return instance$6;
6933
+ return instance$7;
6924
6934
  },
6925
6935
  };
6926
6936
 
@@ -7497,6 +7507,108 @@ const setVisitorClientToken = async (params) => {
7497
7507
  return { accessToken, users, userType };
7498
7508
  };
7499
7509
 
7510
+ /* begin_public_function
7511
+ id: client.logout
7512
+ */
7513
+ /**
7514
+ * ```js
7515
+ * import { Client } from '@amityco/ts-sdk-react-native';
7516
+ * const success = await Client.logout()
7517
+ * ```
7518
+ *
7519
+ * Disconnects an {@link Amity.Client} instance from ASC servers
7520
+ *
7521
+ * @returns a success boolean if disconnected
7522
+ *
7523
+ * @category Client API
7524
+ * @async
7525
+ */
7526
+ const logout = async () => {
7527
+ var _a;
7528
+ const client = getActiveClient();
7529
+ client.log('client/api/disconnectClient');
7530
+ if (client.mqtt && client.mqtt.connected) {
7531
+ client.mqtt.disconnect();
7532
+ }
7533
+ /*
7534
+ * for cases when session state is terminated (example on ban) or token expired,
7535
+ * the terminating block will set session state to terminated or for the or
7536
+ * in the case of expired token the same happens
7537
+ *
7538
+ * establishing state also ignored in cases where accessTokenExpiryWatcher
7539
+ * calls renewal. There is a possibility that renewal will be called before
7540
+ * disconnectClient finishes execution
7541
+ *
7542
+ * IMPORTANT: call this before `emitter.all.clear()`, otherwise the session
7543
+ * event will never be triggered
7544
+ */
7545
+ if (client.sessionState === "established" /* Amity.SessionStates.ESTABLISHED */)
7546
+ setSessionState("notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */);
7547
+ client.emitter.all.clear();
7548
+ (_a = client.mqtt) === null || _a === void 0 ? void 0 : _a.removeAllListeners();
7549
+ client.userId = undefined;
7550
+ client.token = undefined;
7551
+ client.loginType = undefined;
7552
+ client.http.defaults.headers.common.Authorization = '';
7553
+ client.http.defaults.metadata = {
7554
+ tokenExpiry: '',
7555
+ isGlobalBanned: false,
7556
+ isUserDeleted: false,
7557
+ };
7558
+ if (typeof document !== 'undefined') {
7559
+ document.cookie = '_ascSession=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
7560
+ }
7561
+ /*
7562
+ * Cache should be usable if tokenExpired
7563
+ * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#SDK-usability-based-on-Session-State
7564
+ */
7565
+ if (client.sessionState !== "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */ && client.cache) {
7566
+ client.cache = { data: {} };
7567
+ }
7568
+ return true;
7569
+ };
7570
+ /* end_public_function */
7571
+
7572
+ /**
7573
+ * Terminates {@link Amity.Client} instance
7574
+ *
7575
+ *
7576
+ *
7577
+ * @category private
7578
+ */
7579
+ const terminateClient = (terminationReason) => {
7580
+ const client = getActiveClient();
7581
+ setSessionState("terminated" /* Amity.SessionStates.TERMINATED */, terminationReason);
7582
+ if (client.http.defaults.metadata) {
7583
+ if (terminationReason === "globalBan" /* Amity.TokenTerminationReason.GLOBAL_BAN */)
7584
+ client.http.defaults.metadata.isGlobalBanned = true;
7585
+ if (terminationReason === "userDeleted" /* Amity.TokenTerminationReason.USER_DELETED */)
7586
+ client.http.defaults.metadata.isUserDeleted = true;
7587
+ }
7588
+ client.sessionHandler = undefined;
7589
+ logout();
7590
+ };
7591
+
7592
+ let currentUserType = null;
7593
+ /* begin_public_function
7594
+ id: client.get_current_user_type
7595
+ */
7596
+ const getCurrentUserType = () => {
7597
+ if (!currentUserType) {
7598
+ throw new ASCError('Connect client first', 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "fatal" /* Amity.ErrorLevel.FATAL */);
7599
+ }
7600
+ return currentUserType;
7601
+ };
7602
+ /* end_public_function */
7603
+ const setCurrentUserType = (userType) => {
7604
+ currentUserType = userType;
7605
+ };
7606
+
7607
+ const setCurrentUser = ({ user, userType, }) => {
7608
+ setActiveUser(user);
7609
+ setCurrentUserType(userType);
7610
+ };
7611
+
7500
7612
  const createUserEventSubscriber = (event, callback) => {
7501
7613
  const client = getActiveClient();
7502
7614
  const filter = (data) => {
@@ -7815,13 +7927,13 @@ class AnalyticsEngine {
7815
7927
  this._eventCapturer.resetAllBuckets();
7816
7928
  }
7817
7929
  }
7818
- let instance$5;
7930
+ let instance$6;
7819
7931
  var AnalyticsEngine$1 = {
7820
7932
  getInstance: () => {
7821
- if (!instance$5) {
7822
- instance$5 = new AnalyticsEngine();
7933
+ if (!instance$6) {
7934
+ instance$6 = new AnalyticsEngine();
7823
7935
  }
7824
- return instance$5;
7936
+ return instance$6;
7825
7937
  },
7826
7938
  };
7827
7939
 
@@ -8049,12 +8161,12 @@ class MessageReadReceiptSyncEngine {
8049
8161
  }
8050
8162
  }
8051
8163
  }
8052
- let instance$4 = null;
8164
+ let instance$5 = null;
8053
8165
  var ReadReceiptSyncEngine = {
8054
8166
  getInstance: () => {
8055
- if (!instance$4)
8056
- instance$4 = new MessageReadReceiptSyncEngine();
8057
- return instance$4;
8167
+ if (!instance$5)
8168
+ instance$5 = new MessageReadReceiptSyncEngine();
8169
+ return instance$5;
8058
8170
  },
8059
8171
  };
8060
8172
 
@@ -8308,12 +8420,12 @@ class LegacyMessageReadReceiptSyncEngine {
8308
8420
  }
8309
8421
  }
8310
8422
  }
8311
- let instance$3 = null;
8423
+ let instance$4 = null;
8312
8424
  var LegacyReadReceiptSyncEngine = {
8313
8425
  getInstance: () => {
8314
- if (!instance$3)
8315
- instance$3 = new LegacyMessageReadReceiptSyncEngine();
8316
- return instance$3;
8426
+ if (!instance$4)
8427
+ instance$4 = new LegacyMessageReadReceiptSyncEngine();
8428
+ return instance$4;
8317
8429
  },
8318
8430
  };
8319
8431
 
@@ -8589,12 +8701,12 @@ class ObjectResolverEngine {
8589
8701
  this.stopResolver();
8590
8702
  }
8591
8703
  }
8592
- let instance$2 = null;
8704
+ let instance$3 = null;
8593
8705
  var ObjectResolverEngine$1 = {
8594
8706
  getInstance: () => {
8595
- if (!instance$2)
8596
- instance$2 = new ObjectResolverEngine();
8597
- return instance$2;
8707
+ if (!instance$3)
8708
+ instance$3 = new ObjectResolverEngine();
8709
+ return instance$3;
8598
8710
  },
8599
8711
  };
8600
8712
 
@@ -8744,13 +8856,13 @@ class LiveReactionSyncEngine {
8744
8856
  this.stopReactionsSync();
8745
8857
  }
8746
8858
  }
8747
- let instance$1;
8859
+ let instance$2;
8748
8860
  var ReactionSyncEngine = {
8749
8861
  getInstance: () => {
8750
- if (!instance$1) {
8751
- instance$1 = new LiveReactionSyncEngine();
8862
+ if (!instance$2) {
8863
+ instance$2 = new LiveReactionSyncEngine();
8752
8864
  }
8753
- return instance$1;
8865
+ return instance$2;
8754
8866
  },
8755
8867
  };
8756
8868
 
@@ -8772,87 +8884,6 @@ var reactionSyncEngineOnLoginHandler = () => {
8772
8884
  };
8773
8885
  };
8774
8886
 
8775
- /* begin_public_function
8776
- id: client.logout
8777
- */
8778
- /**
8779
- * ```js
8780
- * import { Client } from '@amityco/ts-sdk-react-native';
8781
- * const success = await Client.logout()
8782
- * ```
8783
- *
8784
- * Disconnects an {@link Amity.Client} instance from ASC servers
8785
- *
8786
- * @returns a success boolean if disconnected
8787
- *
8788
- * @category Client API
8789
- * @async
8790
- */
8791
- const logout = async () => {
8792
- var _a;
8793
- const client = getActiveClient();
8794
- client.log('client/api/disconnectClient');
8795
- if (client.mqtt && client.mqtt.connected) {
8796
- client.mqtt.disconnect();
8797
- }
8798
- /*
8799
- * for cases when session state is terminated (example on ban) or token expired,
8800
- * the terminating block will set session state to terminated or for the or
8801
- * in the case of expired token the same happens
8802
- *
8803
- * establishing state also ignored in cases where accessTokenExpiryWatcher
8804
- * calls renewal. There is a possibility that renewal will be called before
8805
- * disconnectClient finishes execution
8806
- *
8807
- * IMPORTANT: call this before `emitter.all.clear()`, otherwise the session
8808
- * event will never be triggered
8809
- */
8810
- if (client.sessionState === "established" /* Amity.SessionStates.ESTABLISHED */)
8811
- setSessionState("notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */);
8812
- client.emitter.all.clear();
8813
- (_a = client.mqtt) === null || _a === void 0 ? void 0 : _a.removeAllListeners();
8814
- client.userId = undefined;
8815
- client.token = undefined;
8816
- client.http.defaults.headers.common.Authorization = '';
8817
- client.http.defaults.metadata = {
8818
- tokenExpiry: '',
8819
- isGlobalBanned: false,
8820
- isUserDeleted: false,
8821
- };
8822
- if (typeof document !== 'undefined') {
8823
- document.cookie = '_ascSession=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
8824
- }
8825
- /*
8826
- * Cache should be usable if tokenExpired
8827
- * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#SDK-usability-based-on-Session-State
8828
- */
8829
- if (client.sessionState !== "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */ && client.cache) {
8830
- client.cache = { data: {} };
8831
- }
8832
- return true;
8833
- };
8834
- /* end_public_function */
8835
-
8836
- /**
8837
- * Terminates {@link Amity.Client} instance
8838
- *
8839
- *
8840
- *
8841
- * @category private
8842
- */
8843
- const terminateClient = (terminationReason) => {
8844
- const client = getActiveClient();
8845
- setSessionState("terminated" /* Amity.SessionStates.TERMINATED */, terminationReason);
8846
- if (client.http.defaults.metadata) {
8847
- if (terminationReason === "globalBan" /* Amity.TokenTerminationReason.GLOBAL_BAN */)
8848
- client.http.defaults.metadata.isGlobalBanned = true;
8849
- if (terminationReason === "userDeleted" /* Amity.TokenTerminationReason.USER_DELETED */)
8850
- client.http.defaults.metadata.isUserDeleted = true;
8851
- }
8852
- client.sessionHandler = undefined;
8853
- logout();
8854
- };
8855
-
8856
8887
  const EVENTS = [
8857
8888
  'disconnected',
8858
8889
  'error',
@@ -8982,24 +9013,50 @@ const removeChannelMarkerCache = (channel) => {
8982
9013
  dropFromCache(['channelMarker', 'get', id], true);
8983
9014
  };
8984
9015
 
8985
- let currentUserType = null;
8986
- /* begin_public_function
8987
- id: client.get_current_user_type
8988
- */
8989
- const getCurrentUserType = () => {
8990
- if (!currentUserType) {
8991
- throw new ASCError('Connect client first', 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "fatal" /* Amity.ErrorLevel.FATAL */);
9016
+ /**
9017
+ * Sets up all login-related event subscriptions
9018
+ * This includes handlers for user bans, deletions, token events, and various engine initializations
9019
+ *
9020
+ * @param unsubWatcher - The unsubscriber function for the access token expiry watcher
9021
+ * @returns Array of unsubscriber functions for all registered subscriptions
9022
+ *
9023
+ * @category private
9024
+ */
9025
+ const setupLoginSubscriptions = (unsubWatcher) => {
9026
+ const client = getActiveClient();
9027
+ const subscriptions = [];
9028
+ subscriptions.push(
9029
+ // GLOBAL_BAN
9030
+ onClientBanned((_) => {
9031
+ terminateClient("globalBan" /* Amity.TokenTerminationReason.GLOBAL_BAN */);
9032
+ subscriptions.forEach(fn => fn());
9033
+ unsubWatcher();
9034
+ }), onTokenTerminated(_ => {
9035
+ terminateClient();
9036
+ subscriptions.forEach(fn => fn());
9037
+ unsubWatcher();
9038
+ }), onUserDeleted$2((user) => {
9039
+ if (user.userId === client.userId) {
9040
+ terminateClient("userDeleted" /* Amity.TokenTerminationReason.USER_DELETED */);
9041
+ subscriptions.forEach(fn => fn());
9042
+ unsubWatcher();
9043
+ }
9044
+ }), onTokenExpired(state => {
9045
+ SessionWatcher$1.getInstance().setSessionState(state);
9046
+ logout();
9047
+ subscriptions.forEach(fn => fn());
9048
+ }),
9049
+ // NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
9050
+ // the channel because currently backend can't handle this, so every time a user is banned from
9051
+ // a channel or the channel is deleted the channel's unread count will not be reset to zero
9052
+ onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler(), reactionSyncEngineOnLoginHandler());
9053
+ if (client.useLegacyUnreadCount) {
9054
+ subscriptions.push(readReceiptSyncEngineOnLoginHandler());
8992
9055
  }
8993
- return currentUserType;
8994
- };
8995
- /* end_public_function */
8996
- const setCurrentUserType = (userType) => {
8997
- currentUserType = userType;
8998
- };
8999
-
9000
- const setCurrentUser = ({ user, userType, }) => {
9001
- setActiveUser(user);
9002
- setCurrentUserType(userType);
9056
+ else {
9057
+ subscriptions.push(legacyReadReceiptSyncEngineOnLoginHandler());
9058
+ }
9059
+ return subscriptions;
9003
9060
  };
9004
9061
 
9005
9062
  /* eslint-disable no-param-reassign */
@@ -9008,8 +9065,8 @@ const setCurrentUser = ({ user, userType, }) => {
9008
9065
  * than the one already connected, in which case the existing subscriptions need
9009
9066
  * to be cleared
9010
9067
  */
9011
- let subscriptions$3 = [];
9012
- async function runMqtt$1() {
9068
+ let subscriptions$4 = [];
9069
+ async function runMqtt$2() {
9013
9070
  await modifyMqttConnection();
9014
9071
  }
9015
9072
  /* begin_public_function
@@ -9043,8 +9100,8 @@ const login = async (params, sessionHandler, config) => {
9043
9100
  if (client.userId && client.userId !== params.userId) {
9044
9101
  await logout();
9045
9102
  // Remove subscription to ban and delete
9046
- subscriptions$3.forEach(fn => fn());
9047
- subscriptions$3 = [];
9103
+ subscriptions$4.forEach(fn => fn());
9104
+ subscriptions$4 = [];
9048
9105
  }
9049
9106
  // default values
9050
9107
  const defaultDeviceId = await getDeviceId();
@@ -9069,11 +9126,12 @@ const login = async (params, sessionHandler, config) => {
9069
9126
  }
9070
9127
  client.userId = user.userId;
9071
9128
  client.sessionHandler = sessionHandler;
9129
+ client.loginType = 'userId';
9072
9130
  /*
9073
9131
  * Cannot push to subscriptions as watcher needs to continue working even if
9074
9132
  * token expires
9075
9133
  */
9076
- unsubWatcher = client.accessTokenExpiryWatcher(sessionHandler);
9134
+ unsubWatcher = client.accessTokenExpiryWatcher();
9077
9135
  setCurrentUser({ user, userType });
9078
9136
  }
9079
9137
  catch (error) {
@@ -9086,40 +9144,11 @@ const login = async (params, sessionHandler, config) => {
9086
9144
  throw error;
9087
9145
  }
9088
9146
  if ((config === null || config === void 0 ? void 0 : config.disableRTE) !== true) {
9089
- runMqtt$1();
9147
+ runMqtt$2();
9090
9148
  }
9091
9149
  await initializeMessagePreviewSetting();
9092
- if (subscriptions$3.length === 0) {
9093
- subscriptions$3.push(
9094
- // GLOBAL_BAN
9095
- onClientBanned((_) => {
9096
- terminateClient("globalBan" /* Amity.TokenTerminationReason.GLOBAL_BAN */);
9097
- subscriptions$3.forEach(fn => fn());
9098
- unsubWatcher();
9099
- }), onTokenTerminated(_ => {
9100
- terminateClient();
9101
- subscriptions$3.forEach(fn => fn());
9102
- unsubWatcher();
9103
- }), onUserDeleted$2((user) => {
9104
- if (user.userId === client.userId) {
9105
- terminateClient("userDeleted" /* Amity.TokenTerminationReason.USER_DELETED */);
9106
- subscriptions$3.forEach(fn => fn());
9107
- unsubWatcher();
9108
- }
9109
- }), onTokenExpired(state => {
9110
- SessionWatcher$1.getInstance().setSessionState(state);
9111
- logout();
9112
- subscriptions$3.forEach(fn => fn());
9113
- }),
9114
- // NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
9115
- // the channel because currently backend can't handle this, so every time a user is banned from
9116
- // a channel or the channel is deleted the channel's unread count will not be reset to zero
9117
- onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler(), reactionSyncEngineOnLoginHandler());
9118
- if (client.useLegacyUnreadCount) {
9119
- subscriptions$3.push(readReceiptSyncEngineOnLoginHandler());
9120
- }
9121
- else
9122
- subscriptions$3.push(legacyReadReceiptSyncEngineOnLoginHandler());
9150
+ if (subscriptions$4.length === 0) {
9151
+ subscriptions$4 = setupLoginSubscriptions(unsubWatcher);
9123
9152
  }
9124
9153
  return true;
9125
9154
  };
@@ -9131,7 +9160,7 @@ const login = async (params, sessionHandler, config) => {
9131
9160
  * than the one already connected, in which case the existing subscriptions need
9132
9161
  * to be cleared
9133
9162
  */
9134
- const subscriptions$2 = [];
9163
+ const subscriptions$3 = [];
9135
9164
  /* begin_public_function
9136
9165
  id: client.loginAsVisitor
9137
9166
  */
@@ -9174,11 +9203,12 @@ const loginAsVisitor = async (params) => {
9174
9203
  [user] = users;
9175
9204
  client.userId = user.userId;
9176
9205
  client.sessionHandler = params.sessionHandler;
9206
+ client.loginType = 'userId';
9177
9207
  /*
9178
9208
  * Cannot push to subscriptions as watcher needs to continue working even if
9179
9209
  * token expires
9180
9210
  */
9181
- unsubWatcher = client.accessTokenExpiryWatcher(params.sessionHandler);
9211
+ unsubWatcher = client.accessTokenExpiryWatcher();
9182
9212
  setCurrentUser({ user, userType });
9183
9213
  }
9184
9214
  catch (error) {
@@ -9191,16 +9221,16 @@ const loginAsVisitor = async (params) => {
9191
9221
  throw error;
9192
9222
  }
9193
9223
  await initializeMessagePreviewSetting();
9194
- if (subscriptions$2.length === 0) {
9224
+ if (subscriptions$3.length === 0) {
9195
9225
  // handling internal SDK events
9196
- subscriptions$2.push(onTokenTerminated(_ => {
9226
+ subscriptions$3.push(onTokenTerminated(_ => {
9197
9227
  terminateClient();
9198
- subscriptions$2.forEach(fn => fn());
9228
+ subscriptions$3.forEach(fn => fn());
9199
9229
  unsubWatcher();
9200
9230
  }), onTokenExpired(state => {
9201
9231
  SessionWatcher$1.getInstance().setSessionState(state);
9202
9232
  logout();
9203
- subscriptions$2.forEach(fn => fn());
9233
+ subscriptions$3.forEach(fn => fn());
9204
9234
  }));
9205
9235
  }
9206
9236
  return true;
@@ -9229,7 +9259,7 @@ const renewal = () => {
9229
9259
  * Per instance of Renewal, only one renewal is allowed
9230
9260
  */
9231
9261
  const renewToken = async (authToken) => {
9232
- const { userId, displayName } = getCurrentUser();
9262
+ const { userId, displayName } = getActiveUser();
9233
9263
  const deviceId = await getDeviceId();
9234
9264
  const params = { userId, displayName, authToken, deviceId };
9235
9265
  if (client.sessionState === "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */ && client.sessionHandler) {
@@ -9304,6 +9334,242 @@ const renewal = () => {
9304
9334
  };
9305
9335
  /* end_public_function */
9306
9336
 
9337
+ const validateAccessToken = async ({ token, userId }) => {
9338
+ const client = getActiveClient();
9339
+ // Validate token using sessions API
9340
+ await client.http.get('/api/v3/sessions', {
9341
+ headers: {
9342
+ Authorization: `Bearer ${token.accessToken}`,
9343
+ },
9344
+ });
9345
+ // Get user details
9346
+ const { data: { users }, } = await client.http.get(`/api/v3/users/${userId}`, {
9347
+ headers: {
9348
+ Authorization: `Bearer ${token.accessToken}`,
9349
+ },
9350
+ });
9351
+ const user = users.find((u) => u.userId === userId);
9352
+ client.http.defaults.headers.common.Authorization = `Bearer ${token.accessToken}`;
9353
+ client.http.defaults.metadata = {
9354
+ tokenExpiry: token.expiresAt,
9355
+ isGlobalBanned: false,
9356
+ isUserDeleted: false,
9357
+ };
9358
+ client.upload.defaults.headers.common.Authorization = `Bearer ${token.accessToken}`;
9359
+ client.upload.defaults.metadata = {
9360
+ tokenExpiry: token.expiresAt,
9361
+ isGlobalBanned: false,
9362
+ isUserDeleted: false,
9363
+ };
9364
+ client.token = token;
9365
+ return user;
9366
+ };
9367
+
9368
+ const isSameUserId = (token) => {
9369
+ var _a;
9370
+ const client = getActiveClient();
9371
+ const decoded = jwtDecode__default["default"](token);
9372
+ return ((_a = decoded === null || decoded === void 0 ? void 0 : decoded.user) === null || _a === void 0 ? void 0 : _a.publicUserId) === client.userId;
9373
+ };
9374
+
9375
+ let subscriptions$2 = [];
9376
+ async function runMqtt$1() {
9377
+ await modifyMqttConnection();
9378
+ }
9379
+ /* begin_public_function
9380
+ id: client.loginWithAccessToken
9381
+ */
9382
+ /**
9383
+ * ```js
9384
+ * import { loginWithAccessToken } from '@amityco/ts-sdk'
9385
+ * const success = await loginWithAccessToken('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...')
9386
+ * ```
9387
+ *
9388
+ * Authenticates a user using a pre-existing access token, allowing direct login without user credentials.
9389
+ * Designed for customers who manage access tokens on their own backend.
9390
+ *
9391
+ * @param accessToken JWT access token signed by customer's backend containing user identity
9392
+ * @returns true if authentication is successful
9393
+ *
9394
+ * @category Client API
9395
+ * @async
9396
+ */
9397
+ const loginWithAccessToken = async (accessToken) => {
9398
+ var _a, _b;
9399
+ const client = getActiveClient();
9400
+ let unsubWatcher;
9401
+ client.log('client/api/loginWithAccessToken', {
9402
+ apiKey: client.apiKey,
9403
+ sessionState: client.sessionState,
9404
+ });
9405
+ // Validate input
9406
+ if (!accessToken || typeof accessToken !== 'string' || accessToken.trim() === '') {
9407
+ throw new ASCError('Access token must be a non-empty string', 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "error" /* Amity.ErrorLevel.ERROR */);
9408
+ }
9409
+ let decoded;
9410
+ try {
9411
+ decoded = jwtDecode__default["default"](accessToken);
9412
+ }
9413
+ catch (error) {
9414
+ throw new ASCError('Invalid access token format', 400100 /* Amity.ServerError.UNAUTHORIZED */, "error" /* Amity.ErrorLevel.ERROR */);
9415
+ }
9416
+ // Extract userId from token
9417
+ const userId = ((_a = decoded === null || decoded === void 0 ? void 0 : decoded.user) === null || _a === void 0 ? void 0 : _a.publicUserId) || ((_b = decoded === null || decoded === void 0 ? void 0 : decoded.user) === null || _b === void 0 ? void 0 : _b.userId);
9418
+ if (!userId) {
9419
+ throw new ASCError('Access token does not contain userId', 400100 /* Amity.ServerError.UNAUTHORIZED */, "error" /* Amity.ErrorLevel.ERROR */);
9420
+ }
9421
+ // Handle existing connected user
9422
+ if (client.userId) {
9423
+ const sameUser = isSameUserId(accessToken);
9424
+ if (!sameUser) {
9425
+ // Different user - do full logout
9426
+ await logout();
9427
+ }
9428
+ }
9429
+ try {
9430
+ // Set state to establishing
9431
+ setSessionState("establishing" /* Amity.SessionStates.ESTABLISHING */);
9432
+ // Prepare token object for validation
9433
+ const tokenObject = {
9434
+ accessToken,
9435
+ issuedAt: decoded.iat ? new Date(decoded.iat * 1000).toISOString() : new Date().toISOString(),
9436
+ expiresAt: new Date(decoded.exp * 1000).toISOString(),
9437
+ };
9438
+ // Validate token and get user
9439
+ const user = await validateAccessToken({
9440
+ token: tokenObject,
9441
+ userId,
9442
+ });
9443
+ if (user == null) {
9444
+ setSessionState("notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */);
9445
+ throw new ASCError(`User ${userId} has not been found`, 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "error" /* Amity.ErrorLevel.ERROR */);
9446
+ }
9447
+ if (user.isDeleted) {
9448
+ setSessionState("notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */);
9449
+ throw new ASCError(`User ${userId} has been deleted`, 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "error" /* Amity.ErrorLevel.ERROR */);
9450
+ }
9451
+ if (user.isGlobalBanned) {
9452
+ setSessionState("notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */);
9453
+ throw new ASCError(`User ${userId} is globally banned`, 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "error" /* Amity.ErrorLevel.ERROR */);
9454
+ }
9455
+ // Set userId and login method flag
9456
+ client.userId = user.userId;
9457
+ // Set login method flag to 'accessToken' in platform-specific login session
9458
+ client.loginType = 'accessToken';
9459
+ // This will be used by the access token handler to determine if token renewal should be invoked
9460
+ // Set active user
9461
+ setActiveUser(user);
9462
+ unsubWatcher = client.accessTokenExpiryWatcher();
9463
+ setSessionState("established" /* Amity.SessionStates.ESTABLISHED */);
9464
+ }
9465
+ catch (error) {
9466
+ // If error occurs, revert session state to not logged in
9467
+ setSessionState("notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */);
9468
+ // Re-throw if it's already an ASCError
9469
+ if (error instanceof ASCError) {
9470
+ throw error;
9471
+ }
9472
+ // Wrap other errors
9473
+ throw new ASCError((error instanceof Error ? error.message : undefined) || 'Login with access token failed', 400100 /* Amity.ServerError.UNAUTHORIZED */, "error" /* Amity.ErrorLevel.ERROR */);
9474
+ }
9475
+ runMqtt$1();
9476
+ await initializeMessagePreviewSetting();
9477
+ if (subscriptions$2.length === 0) {
9478
+ subscriptions$2 = setupLoginSubscriptions(unsubWatcher);
9479
+ }
9480
+ return true;
9481
+ };
9482
+ /* end_public_function */
9483
+
9484
+ /* begin_public_function
9485
+ id: client.renew_with_accessToken
9486
+ */
9487
+ /*
9488
+ * Renewal defintion accepted by SessionHandler interface
9489
+ *
9490
+ * Tech Spec:
9491
+ * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#Session-Handler
9492
+ *
9493
+ * @category private
9494
+ */
9495
+ const renewWithAccessToken = async (accessToken) => {
9496
+ var _a, _b;
9497
+ const client = getActiveClient();
9498
+ client.log('initiating access token renewal');
9499
+ /*
9500
+ * Renews a token if it is hasn't been renewed before. Also marks token as
9501
+ * renewed once done
9502
+ * Per instance of Renewal, only one renewal is allowed
9503
+ */
9504
+ // Validate input
9505
+ if (!accessToken || typeof accessToken !== 'string' || accessToken.trim() === '') {
9506
+ throw new ASCError('Access token must be a non-empty string', 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "error" /* Amity.ErrorLevel.ERROR */);
9507
+ }
9508
+ let decoded;
9509
+ try {
9510
+ decoded = jwtDecode__default["default"](accessToken);
9511
+ }
9512
+ catch (error) {
9513
+ throw new ASCError('Invalid access token format', 400100 /* Amity.ServerError.UNAUTHORIZED */, "error" /* Amity.ErrorLevel.ERROR */);
9514
+ }
9515
+ // Extract userId from token
9516
+ const userId = ((_a = decoded === null || decoded === void 0 ? void 0 : decoded.user) === null || _a === void 0 ? void 0 : _a.publicUserId) || ((_b = decoded === null || decoded === void 0 ? void 0 : decoded.user) === null || _b === void 0 ? void 0 : _b.userId);
9517
+ if (!userId) {
9518
+ throw new ASCError('Access token does not contain userId', 400100 /* Amity.ServerError.UNAUTHORIZED */, "error" /* Amity.ErrorLevel.ERROR */);
9519
+ }
9520
+ // Handle existing connected user
9521
+ if (client.userId) {
9522
+ const sameUser = isSameUserId(accessToken);
9523
+ if (!sameUser) {
9524
+ // Different user - do full logout
9525
+ await logout();
9526
+ }
9527
+ }
9528
+ const tokenObject = {
9529
+ accessToken,
9530
+ issuedAt: decoded.iat ? new Date(decoded.iat * 1000).toISOString() : new Date().toISOString(),
9531
+ expiresAt: new Date(decoded.exp * 1000).toISOString(),
9532
+ };
9533
+ if (client.sessionState === "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */ && client.sessionHandler) {
9534
+ await loginWithAccessToken(accessToken);
9535
+ }
9536
+ else {
9537
+ // about to expire
9538
+ await validateAccessToken({
9539
+ token: tokenObject,
9540
+ userId,
9541
+ });
9542
+ }
9543
+ };
9544
+ /* end_public_function */
9545
+
9546
+ /**
9547
+ * Helper function to renew access token using the accessTokenHandler
9548
+ * Handles error catching and logging
9549
+ *
9550
+ * @param useScheduledTask - Whether to wrap renewal in scheduleTask (for token expired case)
9551
+ * @category private
9552
+ */
9553
+ const renewTokenWithHandler = async ({ useScheduledTask = false, }) => {
9554
+ const client = getActiveClient();
9555
+ if (!client.userId || !client.accessTokenHandler) {
9556
+ return;
9557
+ }
9558
+ try {
9559
+ const newToken = await client.accessTokenHandler.onTokenRenew(client.userId);
9560
+ if (useScheduledTask) {
9561
+ scheduleTask(() => renewWithAccessToken(newToken));
9562
+ }
9563
+ else {
9564
+ renewWithAccessToken(newToken);
9565
+ }
9566
+ }
9567
+ catch (error) {
9568
+ client.log('Proactive token renewal failed, will retry when token expires', error);
9569
+ // Will fallback to expired token flow
9570
+ }
9571
+ };
9572
+
9307
9573
  const ABOUT_TO_EXPIRE_THRESHOLD = 80 / 100;
9308
9574
  const COMPENSATED_DELAY = 5 * MINUTE;
9309
9575
  /*
@@ -9339,10 +9605,11 @@ const isAboutToExpire = (params) => {
9339
9605
  *
9340
9606
  * @category private
9341
9607
  */
9342
- const accessTokenExpiryWatcher = (sessionHandler) => {
9343
- const interval = setInterval(() => {
9608
+ const accessTokenExpiryWatcher = () => {
9609
+ const interval = setInterval(async () => {
9344
9610
  const client = getActiveClient();
9345
- if (!client.token)
9611
+ const { sessionHandler, accessTokenHandler, loginType } = client;
9612
+ if (!client.token || !client.userId)
9346
9613
  return;
9347
9614
  const { issuedAt, expiresAt } = client.token;
9348
9615
  if (isExpired(expiresAt)) {
@@ -9353,18 +9620,38 @@ const accessTokenExpiryWatcher = (sessionHandler) => {
9353
9620
  */
9354
9621
  fireEvent('tokenExpired', "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */);
9355
9622
  /*
9356
- * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#Automatically-initiate-renewal-flow
9357
- *
9358
- * Why sechduled task?
9359
- * Since fireEvent is scheduled, it will be called
9360
- * after sessionHandler leading to an invalid state change from
9361
- * establishing to tokenExpired
9623
+ * Check loginType to determine which handler to use:
9624
+ * - 'accessToken' = use accessTokenHandler
9625
+ * - 'userId' = use sessionHandler
9362
9626
  */
9363
- scheduleTask(() => sessionHandler.sessionWillRenewAccessToken(renewal()));
9627
+ if (loginType === 'accessToken' && accessTokenHandler) {
9628
+ await renewTokenWithHandler({ useScheduledTask: false });
9629
+ }
9630
+ else if (loginType === 'userId' && sessionHandler) {
9631
+ /*
9632
+ * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#Automatically-initiate-renewal-flow
9633
+ *
9634
+ * Why scheduled task?
9635
+ * Since fireEvent is scheduled, it will be called
9636
+ * after sessionHandler leading to an invalid state change from
9637
+ * establishing to tokenExpired
9638
+ */
9639
+ scheduleTask(() => sessionHandler.sessionWillRenewAccessToken(renewal()));
9640
+ }
9364
9641
  return;
9365
9642
  }
9366
9643
  if (isAboutToExpire({ expiresAt, issuedAt })) {
9367
- sessionHandler.sessionWillRenewAccessToken(renewal());
9644
+ /*
9645
+ * Check loginType to determine which handler to use for proactive renewal:
9646
+ * - 'accessToken' = use accessTokenHandler
9647
+ * - 'userId' = use sessionHandler
9648
+ */
9649
+ if (loginType === 'accessToken' && accessTokenHandler) {
9650
+ await renewTokenWithHandler({ useScheduledTask: false });
9651
+ }
9652
+ else if (loginType === 'userId' && sessionHandler) {
9653
+ sessionHandler.sessionWillRenewAccessToken(renewal());
9654
+ }
9368
9655
  }
9369
9656
  }, ACCESS_TOKEN_WATCHER_INTERVAL);
9370
9657
  return () => clearInterval(interval);
@@ -9918,6 +10205,7 @@ const secureLogout = async () => {
9918
10205
  };
9919
10206
  /* end_public_function */
9920
10207
 
10208
+ /* eslint-disable no-param-reassign */
9921
10209
  /*
9922
10210
  * declared earlier to accomodate case when logging in with a different user
9923
10211
  * than the one already connected, in which case the existing subscriptions need
@@ -9927,38 +10215,6 @@ let subscriptions$1 = [];
9927
10215
  async function runMqtt() {
9928
10216
  await modifyMqttConnection();
9929
10217
  }
9930
- const isSameUserId = (token) => {
9931
- var _a;
9932
- const client = getActiveClient();
9933
- const decoded = jwtDecode__default["default"](token);
9934
- return ((_a = decoded === null || decoded === void 0 ? void 0 : decoded.user) === null || _a === void 0 ? void 0 : _a.publicUserId) === client.userId;
9935
- };
9936
- const validateAccessToken = async ({ token, userId }) => {
9937
- const client = getActiveClient();
9938
- // begin establishing session
9939
- setSessionState("establishing" /* Amity.SessionStates.ESTABLISHING */);
9940
- const { data: { users }, } = await client.http.get(`/api/v3/users/${userId}`, {
9941
- headers: {
9942
- Authorization: `Bearer ${token.accessToken}`,
9943
- },
9944
- });
9945
- const user = users.find((u) => u.userId === userId);
9946
- client.http.defaults.headers.common.Authorization = `Bearer ${token.accessToken}`;
9947
- client.http.defaults.metadata = {
9948
- tokenExpiry: token.expiresAt,
9949
- isGlobalBanned: false,
9950
- isUserDeleted: false,
9951
- };
9952
- client.upload.defaults.headers.common.Authorization = `Bearer ${token.accessToken}`;
9953
- client.upload.defaults.metadata = {
9954
- tokenExpiry: token.expiresAt,
9955
- isGlobalBanned: false,
9956
- isUserDeleted: false,
9957
- };
9958
- client.token = token;
9959
- setSessionState("established" /* Amity.SessionStates.ESTABLISHED */);
9960
- return user;
9961
- };
9962
10218
  /* begin_public_function
9963
10219
  id: client.resumeSession
9964
10220
  */
@@ -10007,6 +10263,7 @@ const resumeSession = async (params, sessionHandler, config) => {
10007
10263
  }
10008
10264
  }
10009
10265
  try {
10266
+ setSessionState("establishing" /* Amity.SessionStates.ESTABLISHING */);
10010
10267
  const user = await validateAccessToken(params);
10011
10268
  if (user == null) {
10012
10269
  throw new ASCError(`${params.userId} has not been found`, 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "error" /* Amity.ErrorLevel.ERROR */);
@@ -10113,13 +10370,13 @@ class GlobalFileAccessType {
10113
10370
  }
10114
10371
  }
10115
10372
  _GlobalFileAccessType_fileAccessType = new WeakMap();
10116
- let instance;
10373
+ let instance$1;
10117
10374
  var GlobalFileAccessType$1 = {
10118
10375
  getInstance: () => {
10119
- if (!instance) {
10120
- instance = new GlobalFileAccessType();
10376
+ if (!instance$1) {
10377
+ instance$1 = new GlobalFileAccessType();
10121
10378
  }
10122
- return instance;
10379
+ return instance$1;
10123
10380
  },
10124
10381
  };
10125
10382
 
@@ -10334,11 +10591,12 @@ const loginAsBot = async (params) => {
10334
10591
  [user] = users;
10335
10592
  client.userId = user.userId;
10336
10593
  client.sessionHandler = params.sessionHandler;
10594
+ client.loginType = 'userId';
10337
10595
  /*
10338
10596
  * Cannot push to subscriptions as watcher needs to continue working even if
10339
10597
  * token expires
10340
10598
  */
10341
- unsubWatcher = client.accessTokenExpiryWatcher(params.sessionHandler);
10599
+ unsubWatcher = client.accessTokenExpiryWatcher();
10342
10600
  setCurrentUser({ user, userType });
10343
10601
  }
10344
10602
  catch (error) {
@@ -10367,6 +10625,53 @@ const loginAsBot = async (params) => {
10367
10625
  };
10368
10626
  /* end_public_function */
10369
10627
 
10628
+ /* begin_public_function
10629
+ id: client.setAccessTokenHandler
10630
+ */
10631
+ /**
10632
+ * ```js
10633
+ * import { setAccessTokenHandler } from '@amityco/ts-sdk'
10634
+ *
10635
+ * const tokenHandler = {
10636
+ * async onTokenRenew() {
10637
+ * const response = await fetch('https://your-backend.com/api/refresh-token', {
10638
+ * method: 'POST',
10639
+ * credentials: 'include',
10640
+ * });
10641
+ * const data = await response.json();
10642
+ * return data.accessToken;
10643
+ * }
10644
+ * };
10645
+ *
10646
+ * setAccessTokenHandler(tokenHandler);
10647
+ * ```
10648
+ *
10649
+ * Registers a custom handler for managing access token renewal and expiration events.
10650
+ * This enables automatic token refresh and graceful handling of expired tokens.
10651
+ *
10652
+ * Must be called before loginWithAccessToken() to ensure the handler is available
10653
+ * when token expiry is detected.
10654
+ *
10655
+ * @param accessTokenHandler Handler object implementing token renewal callbacks
10656
+ * @returns void
10657
+ *
10658
+ * @category Client API
10659
+ */
10660
+ const setAccessTokenHandler = (accessTokenHandler) => {
10661
+ const client = getActiveClient();
10662
+ client.log('client/api/setAccessTokenHandler', {
10663
+ apiKey: client.apiKey,
10664
+ sessionState: client.sessionState,
10665
+ hasOnTokenRenew: typeof (accessTokenHandler === null || accessTokenHandler === void 0 ? void 0 : accessTokenHandler.onTokenRenew) === 'function',
10666
+ });
10667
+ // Validate handler has required method
10668
+ if (!accessTokenHandler || typeof accessTokenHandler.onTokenRenew !== 'function') {
10669
+ throw new Error('AccessTokenHandler must implement onTokenRenew() method');
10670
+ }
10671
+ // Register the handler
10672
+ client.accessTokenHandler = accessTokenHandler;
10673
+ };
10674
+
10370
10675
  /**
10371
10676
  * ```js
10372
10677
  * import { onChannelMarkerFetched } from '@amityco/ts-sdk-react-native'
@@ -10726,6 +11031,7 @@ var index$r = /*#__PURE__*/Object.freeze({
10726
11031
  setActiveUser: setActiveUser,
10727
11032
  createClient: createClient,
10728
11033
  login: login,
11034
+ loginWithAccessToken: loginWithAccessToken,
10729
11035
  logout: logout,
10730
11036
  secureLogout: secureLogout,
10731
11037
  resumeSession: resumeSession,
@@ -10747,6 +11053,7 @@ var index$r = /*#__PURE__*/Object.freeze({
10747
11053
  getCurrentUser: getCurrentUser,
10748
11054
  getCurrentUserType: getCurrentUserType,
10749
11055
  setCurrentUserType: setCurrentUserType,
11056
+ setAccessTokenHandler: setAccessTokenHandler,
10750
11057
  onConnectionError: onConnectionError,
10751
11058
  onClientDisconnected: onClientDisconnected,
10752
11059
  onClientBanned: onClientBanned,
@@ -17104,33 +17411,186 @@ removeReaction.optimistically = (referenceType, referenceId, reactionName) => {
17104
17411
  return !((_d = reaction === null || reaction === void 0 ? void 0 : reaction.myReactions) === null || _d === void 0 ? void 0 : _d.includes(reactionName));
17105
17412
  };
17106
17413
 
17107
- const updateStreamReferences = (streams, streamId, postId) => {
17108
- if (!streamId)
17109
- return streams;
17110
- return streams.map(stream => stream.streamId === streamId
17111
- ? Object.assign(Object.assign({}, stream), { referenceType: 'post', referenceId: postId, postId }) : stream);
17112
- };
17113
- const preparePostPayload = (payload) => {
17114
- const { posts: postsData, postChildren, videoStreamings } = payload, postPayload = __rest(payload, ["posts", "postChildren", "videoStreamings"]);
17115
- // Unpack community payload by mapping payload field to postSetting value.
17116
- const communitiesWithPostSetting = addPostSetting({ communities: postPayload.communities });
17117
- // map users with community
17118
- const mappedCommunityUsers = postPayload.communityUsers.map(communityUser => {
17119
- const user = postPayload.users.find(user => user.userId === communityUser.userId);
17120
- return Object.assign(Object.assign({}, communityUser), { user });
17121
- });
17122
- const communityWithMembershipStatus = updateMembershipStatus(communitiesWithPostSetting, mappedCommunityUsers);
17123
- let mappedNewStream = [];
17414
+ class ResetTask {
17415
+ constructor(postId, latestCreatedAt, serverCommentCount) {
17416
+ this.postId = postId;
17417
+ this.latestCreatedAt = latestCreatedAt;
17418
+ this.serverCommentCount = serverCommentCount;
17419
+ }
17420
+ }
17421
+
17422
+ // Task to track comment creation
17423
+ class CreateTask {
17424
+ constructor(postId, commentId, createdAt) {
17425
+ this.postId = postId;
17426
+ this.commentId = commentId;
17427
+ this.createdAt = createdAt;
17428
+ }
17429
+ }
17430
+
17431
+ // Task to track comment deletion
17432
+ class DeleteTask {
17433
+ constructor(postId, commentId) {
17434
+ this.postId = postId;
17435
+ this.commentId = commentId;
17436
+ }
17437
+ }
17438
+
17439
+ class CommentChange {
17440
+ constructor(latestCreatedAt, serverCommentCount) {
17441
+ this.latestCommentCreatedAt = latestCreatedAt;
17442
+ this.serverCommentCount = serverCommentCount;
17443
+ this.createdCommentIds = new Set();
17444
+ this.deletedCommentIds = new Set();
17445
+ }
17446
+ }
17447
+
17448
+ class PostCommentCountEngine {
17449
+ constructor() {
17450
+ this.isProcessing = false;
17451
+ this.tasks = [];
17452
+ this.commentChangeTracker = new Map();
17453
+ }
17454
+ queueCommentChangeTask(task) {
17455
+ this.tasks.push(task);
17456
+ if (!this.isProcessing) {
17457
+ this.processCommentChangeTask();
17458
+ }
17459
+ }
17460
+ processCommentChangeTask() {
17461
+ if (this.isProcessing) {
17462
+ return;
17463
+ }
17464
+ this.isProcessing = true;
17465
+ if (this.tasks.length === 0) {
17466
+ this.isProcessing = false;
17467
+ return;
17468
+ }
17469
+ // Process in capped batches, coalescing updates
17470
+ const batch = this.tasks.splice(0, PostCommentCountEngine.BATCH_SIZE);
17471
+ const modifiedPostIds = new Set();
17472
+ batch.forEach(task => {
17473
+ let modified = false;
17474
+ if (task instanceof ResetTask) {
17475
+ modified = this.processResetTaskInternal(task);
17476
+ }
17477
+ else if (task instanceof CreateTask) {
17478
+ modified = this.processCreateTaskInternal(task);
17479
+ }
17480
+ else if (task instanceof DeleteTask) {
17481
+ modified = this.processDeleteTaskInternal(task);
17482
+ }
17483
+ if (modified) {
17484
+ modifiedPostIds.add(task.postId);
17485
+ }
17486
+ });
17487
+ // Publish one update per modified post
17488
+ modifiedPostIds.forEach(postId => {
17489
+ const count = this.computeCommentCount(postId);
17490
+ PostCommentCountEngine.publishUpdate(postId, count);
17491
+ });
17492
+ this.isProcessing = false;
17493
+ // Recurse if more tasks remain
17494
+ if (this.tasks.length > 0) {
17495
+ this.processCommentChangeTask();
17496
+ }
17497
+ }
17498
+ processResetTaskInternal(task) {
17499
+ // Always creates/overwrites tracker
17500
+ this.commentChangeTracker.set(task.postId, new CommentChange(task.latestCreatedAt, task.serverCommentCount));
17501
+ return true;
17502
+ }
17503
+ processCreateTaskInternal(task) {
17504
+ const tracker = this.commentChangeTracker.get(task.postId);
17505
+ if (!tracker)
17506
+ return false; // No tracker, skip
17507
+ if (tracker.createdCommentIds.has(task.commentId))
17508
+ return false; // Deduplication
17509
+ if (task.createdAt <= tracker.latestCommentCreatedAt)
17510
+ return false; // Timestamp filtering
17511
+ tracker.createdCommentIds.add(task.commentId);
17512
+ return true;
17513
+ }
17514
+ processDeleteTaskInternal(task) {
17515
+ const tracker = this.commentChangeTracker.get(task.postId);
17516
+ if (!tracker)
17517
+ return false; // No tracker, skip
17518
+ if (tracker.deletedCommentIds.has(task.commentId))
17519
+ return false; // Deduplication
17520
+ tracker.deletedCommentIds.add(task.commentId);
17521
+ return true;
17522
+ }
17523
+ computeCommentCount(postId) {
17524
+ const tracker = this.commentChangeTracker.get(postId);
17525
+ if (!tracker)
17526
+ return 0;
17527
+ const count = tracker.serverCommentCount + tracker.createdCommentIds.size - tracker.deletedCommentIds.size;
17528
+ return Math.max(0, count);
17529
+ }
17530
+ static publishUpdate(postId, newCount) {
17531
+ var _a;
17532
+ const queryKey = ['post', 'get', postId];
17533
+ mergeInCache(queryKey, {
17534
+ localCommentCount: newCount,
17535
+ });
17536
+ const postPayload = (_a = pullFromCache(queryKey)) === null || _a === void 0 ? void 0 : _a.data;
17537
+ if (!postPayload)
17538
+ return;
17539
+ fireEvent('local.post.updated', {
17540
+ posts: [postPayload],
17541
+ });
17542
+ }
17543
+ }
17544
+ PostCommentCountEngine.BATCH_SIZE = 50;
17545
+ let instance;
17546
+ var PostCommentCountEngine$1 = {
17547
+ getInstance: () => {
17548
+ if (!instance) {
17549
+ instance = new PostCommentCountEngine();
17550
+ }
17551
+ return instance;
17552
+ },
17553
+ };
17554
+
17555
+ const updateStreamReferences = (streams, streamId, postId) => {
17556
+ if (!streamId)
17557
+ return streams;
17558
+ return streams.map(stream => stream.streamId === streamId
17559
+ ? Object.assign(Object.assign({}, stream), { referenceType: 'post', referenceId: postId, postId }) : stream);
17560
+ };
17561
+ const preparePostPayload = (payload) => {
17562
+ const { posts: postsData, postChildren, videoStreamings } = payload, postPayload = __rest(payload, ["posts", "postChildren", "videoStreamings"]);
17563
+ // Unpack community payload by mapping payload field to postSetting value.
17564
+ const communitiesWithPostSetting = addPostSetting({ communities: postPayload.communities });
17565
+ // map users with community
17566
+ const mappedCommunityUsers = postPayload.communityUsers.map(communityUser => {
17567
+ const user = postPayload.users.find(user => user.userId === communityUser.userId);
17568
+ return Object.assign(Object.assign({}, communityUser), { user });
17569
+ });
17570
+ const communityWithMembershipStatus = updateMembershipStatus(communitiesWithPostSetting, mappedCommunityUsers);
17571
+ let mappedNewStream = [];
17124
17572
  // feed type
17125
17573
  const posts = postsData.map(post => {
17126
- var _a, _b;
17574
+ var _a, _b, _c, _d;
17127
17575
  const feedType = (_a = postPayload.feeds.find(feed => feed.feedId === post.feedId)) === null || _a === void 0 ? void 0 : _a.feedType;
17128
17576
  const childPosts = payload.postChildren.filter(children => children.parentPostId === post.postId);
17129
17577
  if (childPosts.length > 0 && isAmityLivestreamPost(childPosts[0])) {
17130
17578
  mappedNewStream = updateStreamReferences(videoStreamings, (_b = childPosts[0].data) === null || _b === void 0 ? void 0 : _b.streamId, post.postId);
17131
17579
  }
17580
+ // --- Computed Comment Count: ResetTask integration ---
17581
+ // Find all comments for this post (referenceType === 'post' && referenceId === postId)
17582
+ const allComments = (payload.comments || []).filter((c) => c.referenceType === 'post' && c.referenceId === post.postId);
17583
+ // Compute latestCreatedAt
17584
+ const latestCreatedAt = allComments.length === 0
17585
+ ? new Date().toISOString()
17586
+ : allComments
17587
+ .map(c => c.createdAt)
17588
+ .sort()
17589
+ .at(-1);
17590
+ // Queue ResetTask for this post
17591
+ PostCommentCountEngine$1.getInstance().queueCommentChangeTask(new ResetTask(post.postId, latestCreatedAt, (_c = post.commentsCount) !== null && _c !== void 0 ? _c : 0));
17132
17592
  return Object.assign(Object.assign({}, post), { childPosts,
17133
- feedType });
17593
+ feedType, localCommentCount: (_d = post.localCommentCount) !== null && _d !== void 0 ? _d : post.commentsCount });
17134
17594
  });
17135
17595
  return Object.assign(Object.assign({}, postPayload), { postChildren, videoStreamings: mappedNewStream, posts, communities: communityWithMembershipStatus, communityUsers: mappedCommunityUsers });
17136
17596
  };
@@ -17232,14 +17692,12 @@ const createLocalPostEventSubscriber = (event, callback) => {
17232
17692
  callback(payload.posts[0]);
17233
17693
  }
17234
17694
  else {
17235
- const data = preparePostPayload(payload);
17236
- const { communities } = data;
17237
- ingestInCache(data);
17695
+ const { communities } = payload;
17238
17696
  if ((communities === null || communities === void 0 ? void 0 : communities[0]) && !['local.post.updated'].includes(event)) {
17239
17697
  fireEvent('community.updated', {
17240
17698
  communities,
17241
17699
  categories: [],
17242
- communityUsers: data.communityUsers,
17700
+ communityUsers: payload.communityUsers,
17243
17701
  feeds: [],
17244
17702
  files: [],
17245
17703
  users: [],
@@ -17510,7 +17968,7 @@ const createCommentEventSubscriber = (event, callback) => {
17510
17968
  const createLocalCommentEventSubscriber = (event, callback) => {
17511
17969
  const client = getActiveClient();
17512
17970
  const filter = (payload) => {
17513
- var _a, _b;
17971
+ var _a, _b, _c, _d, _e;
17514
17972
  if (!client.cache) {
17515
17973
  // TODO: here we are missing specific properties here!
17516
17974
  callback(LinkedObject.comment(payload.comments[0]));
@@ -17550,7 +18008,13 @@ const createLocalCommentEventSubscriber = (event, callback) => {
17550
18008
  }
17551
18009
  }
17552
18010
  }
17553
- const queries = (_a = queryCache(['comment', 'query'])) === null || _a === void 0 ? void 0 : _a.filter(({ key }) => { var _a; return ((_a = key[2]) === null || _a === void 0 ? void 0 : _a.referenceId) === comment.data.referenceId; });
18011
+ else {
18012
+ const postCacheKey = ['post', 'get', comments[0].referenceId];
18013
+ const postCache = (_a = pullFromCache(postCacheKey)) === null || _a === void 0 ? void 0 : _a.data;
18014
+ postCache === null || postCache === void 0 ? void 0 : postCache.comments.push((_b = comments[0]) === null || _b === void 0 ? void 0 : _b.commentId);
18015
+ pushToCache(postCacheKey, postCache);
18016
+ }
18017
+ const queries = (_c = queryCache(['comment', 'query'])) === null || _c === void 0 ? void 0 : _c.filter(({ key }) => { var _a; return ((_a = key[2]) === null || _a === void 0 ? void 0 : _a.referenceId) === comment.data.referenceId; });
17554
18018
  queries === null || queries === void 0 ? void 0 : queries.map(({ key, data }) => upsertInCache(key, data, { cachedAt: -1 }));
17555
18019
  }
17556
18020
  if (['local.comment.deleted'].includes(event)) {
@@ -17582,7 +18046,13 @@ const createLocalCommentEventSubscriber = (event, callback) => {
17582
18046
  }
17583
18047
  }
17584
18048
  }
17585
- const queries = (_b = queryCache(['comment', 'query'])) === null || _b === void 0 ? void 0 : _b.filter(({ key }) => { var _a; return ((_a = key[2]) === null || _a === void 0 ? void 0 : _a.referenceId) === comment.data.referenceId; });
18049
+ else {
18050
+ const postCacheKey = ['post', 'get', comments[0].referenceId];
18051
+ const postCache = (_d = pullFromCache(postCacheKey)) === null || _d === void 0 ? void 0 : _d.data;
18052
+ const updatedPost = Object.assign(Object.assign({}, postCache), { comments: postCache === null || postCache === void 0 ? void 0 : postCache.comments.filter(commentId => { var _a; return commentId !== ((_a = comments[0]) === null || _a === void 0 ? void 0 : _a.commentId); }) });
18053
+ pushToCache(postCacheKey, updatedPost);
18054
+ }
18055
+ const queries = (_e = queryCache(['comment', 'query'])) === null || _e === void 0 ? void 0 : _e.filter(({ key }) => { var _a; return ((_a = key[2]) === null || _a === void 0 ? void 0 : _a.referenceId) === comment.data.referenceId; });
17586
18056
  queries === null || queries === void 0 ? void 0 : queries.map(({ key, data }) => upsertInCache(key, data, { cachedAt: -1 }));
17587
18057
  }
17588
18058
  callback(LinkedObject.comment(comment.data));
@@ -25292,7 +25762,6 @@ getCommentByIds.locally = (commentIds) => {
25292
25762
  * @async
25293
25763
  */
25294
25764
  const createComment = async (bundle) => {
25295
- var _a;
25296
25765
  const client = getActiveClient();
25297
25766
  client.log('comment/createComment', bundle);
25298
25767
  const { data } = await client.http.post('/api/v3/comments', bundle);
@@ -25304,22 +25773,7 @@ const createComment = async (bundle) => {
25304
25773
  if (client.cache)
25305
25774
  ingestInCache(data, { cachedAt });
25306
25775
  if (['post', 'content'].includes(bundle.referenceType)) {
25307
- const post = (_a = pullFromCache(['post', 'get', bundle.referenceId])) === null || _a === void 0 ? void 0 : _a.data;
25308
- if (post) {
25309
- post.commentsCount += 1;
25310
- fireEvent('local.post.updated', {
25311
- posts: [post],
25312
- categories: [],
25313
- comments: [],
25314
- communities: [],
25315
- communityUsers: data.communityUsers,
25316
- feeds: [],
25317
- files: data.files,
25318
- postChildren: [],
25319
- users: data.users,
25320
- videoStreamings: [],
25321
- });
25322
- }
25776
+ PostCommentCountEngine$1.getInstance().queueCommentChangeTask(new CreateTask(bundle.referenceId, comments[0].commentId, data.comments[0].createdAt));
25323
25777
  }
25324
25778
  else if (bundle.referenceType === 'story') {
25325
25779
  const storyIndex = pullFromCache([
@@ -25478,7 +25932,7 @@ getStoryByStoryId$1.locally = (storyId) => {
25478
25932
  * @async
25479
25933
  */
25480
25934
  const deleteComment = async (commentId, permanent = false) => {
25481
- var _a;
25935
+ var _a, _b;
25482
25936
  const client = getActiveClient();
25483
25937
  const comment = await getComment$2(commentId);
25484
25938
  // API-FIX: This endpoint has not been implemented yet.
@@ -25511,26 +25965,14 @@ const deleteComment = async (commentId, permanent = false) => {
25511
25965
  else {
25512
25966
  const post = (_a = pullFromCache(['post', 'get', comment.data.referenceId])) === null || _a === void 0 ? void 0 : _a.data;
25513
25967
  if (post) {
25514
- let removeCount;
25515
- if (!deleted.parentId) {
25516
- // NOTE: delete the parent comment will remove all children comments
25517
- removeCount = deleted.childrenNumber + 1;
25968
+ const engine = PostCommentCountEngine$1.getInstance();
25969
+ engine.queueCommentChangeTask(new DeleteTask(post.postId, commentId));
25970
+ if (!deleted.parentId && ((_b = deleted.children) === null || _b === void 0 ? void 0 : _b.length) > 0) {
25971
+ // NOTE: delete the parent comment will also remove all children comments
25972
+ deleted.children.forEach((childCommentId) => {
25973
+ engine.queueCommentChangeTask(new DeleteTask(post.postId, childCommentId));
25974
+ });
25518
25975
  }
25519
- else
25520
- removeCount = 1;
25521
- post.commentsCount -= removeCount;
25522
- fireEvent('local.post.updated', {
25523
- posts: [post],
25524
- categories: [],
25525
- comments: [],
25526
- communities: [],
25527
- communityUsers: [],
25528
- feeds: [],
25529
- files: [],
25530
- postChildren: [],
25531
- users: [],
25532
- videoStreamings: [],
25533
- });
25534
25976
  }
25535
25977
  }
25536
25978
  fireEvent('local.comment.deleted', {
@@ -26096,47 +26538,6 @@ var index$d = /*#__PURE__*/Object.freeze({
26096
26538
  getComments: getComments
26097
26539
  });
26098
26540
 
26099
- const getPost$1 = async (postId) => {
26100
- const client = getActiveClient();
26101
- client.log('post/getPost', postId);
26102
- isInTombstone('post', postId);
26103
- let payload;
26104
- try {
26105
- // API-FIX: endpoint should not be /list, parameters should be querystring.
26106
- const response = await client.http.get(`/api/v3/posts/${encodeURIComponent(postId)}`);
26107
- payload = response.data;
26108
- }
26109
- catch (error) {
26110
- if (checkIfShouldGoesToTombstone(error === null || error === void 0 ? void 0 : error.code)) {
26111
- pushToTombstone('post', postId);
26112
- }
26113
- throw error;
26114
- }
26115
- const data = prepareMembershipPayload(payload, 'communityUsers');
26116
- const cachedAt = client.cache && Date.now();
26117
- if (client.cache)
26118
- ingestInCache(data, { cachedAt });
26119
- const { posts } = data;
26120
- const result = posts.find(post => post.postId === postId);
26121
- return {
26122
- data: result,
26123
- cachedAt,
26124
- };
26125
- };
26126
- getPost$1.locally = (postId) => {
26127
- const client = getActiveClient();
26128
- client.log('post/getPost.locally', postId);
26129
- if (!client.cache)
26130
- return;
26131
- const cached = pullFromCache(['post', 'get', postId]);
26132
- if (!cached)
26133
- return;
26134
- return {
26135
- data: cached.data,
26136
- cachedAt: cached.cachedAt,
26137
- };
26138
- };
26139
-
26140
26541
  /**
26141
26542
  * ```js
26142
26543
  * import { onLocalPostDeleted } from '@amityco/ts-sdk-react-native'
@@ -26235,7 +26636,6 @@ const commentEventHandler$1 = (callback, eventHandler, cacheKey) => {
26235
26636
  const currentCollection = (_a = pullFromCache(cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
26236
26637
  if (!currentCollection || !currentCollection.data.includes(comment.referenceId))
26237
26638
  return;
26238
- await getPost$1(comment.referenceId);
26239
26639
  callback(comment);
26240
26640
  });
26241
26641
  };
@@ -26243,12 +26643,8 @@ const generateCommentSubscriptions$1 = (cacheKey) => {
26243
26643
  const eventHandlers = [
26244
26644
  onCommentCreated,
26245
26645
  onCommentDeleted,
26246
- onCommentReactionAdded,
26247
- onCommentReactionRemoved,
26248
26646
  onCommentCreatedLocal,
26249
26647
  onCommentDeleteLocal,
26250
- onLocalCommentReactionAdded,
26251
- onLocalCommentReactionRemoved,
26252
26648
  ];
26253
26649
  return eventHandlers.map(handler => ({
26254
26650
  fn: convertEventPayload((callback) => commentEventHandler$1(callback, handler, cacheKey), 'referenceId', 'post'),
@@ -26612,6 +27008,47 @@ class UserFeedQueryStreamController extends QueryStreamController {
26612
27008
  }
26613
27009
  }
26614
27010
 
27011
+ const getPost$1 = async (postId) => {
27012
+ const client = getActiveClient();
27013
+ client.log('post/getPost', postId);
27014
+ isInTombstone('post', postId);
27015
+ let payload;
27016
+ try {
27017
+ // API-FIX: endpoint should not be /list, parameters should be querystring.
27018
+ const response = await client.http.get(`/api/v3/posts/${encodeURIComponent(postId)}`);
27019
+ payload = response.data;
27020
+ }
27021
+ catch (error) {
27022
+ if (checkIfShouldGoesToTombstone(error === null || error === void 0 ? void 0 : error.code)) {
27023
+ pushToTombstone('post', postId);
27024
+ }
27025
+ throw error;
27026
+ }
27027
+ const data = prepareMembershipPayload(payload, 'communityUsers');
27028
+ const cachedAt = client.cache && Date.now();
27029
+ if (client.cache)
27030
+ ingestInCache(data, { cachedAt });
27031
+ const { posts } = data;
27032
+ const result = posts.find(post => post.postId === postId);
27033
+ return {
27034
+ data: result,
27035
+ cachedAt,
27036
+ };
27037
+ };
27038
+ getPost$1.locally = (postId) => {
27039
+ const client = getActiveClient();
27040
+ client.log('post/getPost.locally', postId);
27041
+ if (!client.cache)
27042
+ return;
27043
+ const cached = pullFromCache(['post', 'get', postId]);
27044
+ if (!cached)
27045
+ return;
27046
+ return {
27047
+ data: cached.data,
27048
+ cachedAt: cached.cachedAt,
27049
+ };
27050
+ };
27051
+
26615
27052
  class UserFeedLiveCollectionController extends LiveCollectionController {
26616
27053
  constructor(query, callback) {
26617
27054
  const queryStreamId = hash__default["default"](query);
@@ -26745,12 +27182,240 @@ const getUserFeed = (params, callback, config) => {
26745
27182
  };
26746
27183
  /* end_public_function */
26747
27184
 
27185
+ class CommunityFeedPaginationController extends PaginationController {
27186
+ async getRequest(queryParams, token) {
27187
+ const { limit = COLLECTION_DEFAULT_PAGINATION_LIMIT, includeDeleted, communityId } = queryParams, params = __rest(queryParams, ["limit", "includeDeleted", "communityId"]);
27188
+ const options = token ? { token } : { limit };
27189
+ const { data: queryResponse } = await this.http.get(`/api/v5/posts`, {
27190
+ params: Object.assign(Object.assign({}, params), { targetId: communityId, targetType: 'community', isDeleted: inferIsDeleted(includeDeleted), matchingOnlyParentPost: true, options }),
27191
+ });
27192
+ return queryResponse;
27193
+ }
27194
+ }
27195
+
27196
+ class CommunityFeedQueryStreamController extends QueryStreamController {
27197
+ constructor(query, cacheKey, notifyChange, preparePayload) {
27198
+ super(query, cacheKey);
27199
+ this.notifyChange = notifyChange;
27200
+ this.preparePayload = preparePayload;
27201
+ }
27202
+ async saveToMainDB(response) {
27203
+ const processedPayload = await this.preparePayload(response);
27204
+ const client = getActiveClient();
27205
+ const cachedAt = client.cache && Date.now();
27206
+ if (client.cache) {
27207
+ ingestInCache(processedPayload, { cachedAt });
27208
+ }
27209
+ }
27210
+ appendToQueryStream(response, direction, refresh = false) {
27211
+ var _a, _b;
27212
+ if (refresh) {
27213
+ pushToCache(this.cacheKey, {
27214
+ data: response.posts.map(getResolver('post')),
27215
+ });
27216
+ }
27217
+ else {
27218
+ const collection = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
27219
+ const posts = (_b = collection === null || collection === void 0 ? void 0 : collection.data) !== null && _b !== void 0 ? _b : [];
27220
+ pushToCache(this.cacheKey, Object.assign(Object.assign({}, collection), { data: [...new Set([...posts, ...response.posts.map(getResolver('post'))])] }));
27221
+ }
27222
+ }
27223
+ reactor(action) {
27224
+ return (post) => {
27225
+ var _a, _b;
27226
+ const collection = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
27227
+ if (!collection)
27228
+ return;
27229
+ if (action === EnumPostActions.OnPostDeleted) {
27230
+ collection.data = collection.data.filter(postId => postId !== post.postId);
27231
+ }
27232
+ if (post.parentPostId && post.isDeleted) {
27233
+ const parentPost = (_b = pullFromCache([
27234
+ 'post',
27235
+ 'get',
27236
+ post.parentPostId,
27237
+ ])) === null || _b === void 0 ? void 0 : _b.data;
27238
+ if (!parentPost)
27239
+ return;
27240
+ parentPost.children = parentPost.children.filter(childId => childId !== post.postId);
27241
+ pushToCache(['post', 'get', parentPost.postId], parentPost);
27242
+ }
27243
+ if (action === EnumPostActions.OnPostDeclined) {
27244
+ collection.data = collection.data.filter(postId => postId !== post.postId);
27245
+ }
27246
+ if (action === EnumPostActions.OnPostCreated || action === EnumPostActions.OnPostApproved) {
27247
+ collection.data = [...new Set([post.postId, ...collection.data])];
27248
+ }
27249
+ pushToCache(this.cacheKey, collection);
27250
+ this.notifyChange({ origin: "event" /* Amity.LiveDataOrigin.EVENT */, loading: false });
27251
+ };
27252
+ }
27253
+ subscribeRTE(createSubscriber) {
27254
+ return createSubscriber.map(subscriber => subscriber.fn(this.reactor(subscriber.action)));
27255
+ }
27256
+ }
27257
+
27258
+ const commentEventHandler = (callback, eventHandler, cacheKey, resolveId) => {
27259
+ return eventHandler(async (comment) => {
27260
+ var _a;
27261
+ const currentCollection = (_a = pullFromCache(cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
27262
+ if (!currentCollection ||
27263
+ !currentCollection.data.includes(resolveId ? resolveId(comment.referenceId) : comment.referenceId))
27264
+ return;
27265
+ callback(comment);
27266
+ });
27267
+ };
27268
+ const generateCommentSubscriptions = ({ cacheKey, resolveId, }) => {
27269
+ const eventHandlers = [
27270
+ onCommentCreated,
27271
+ onCommentDeleted,
27272
+ onCommentCreatedLocal,
27273
+ onCommentDeleteLocal,
27274
+ ];
27275
+ return eventHandlers.map(handler => ({
27276
+ fn: convertEventPayload((callback) => commentEventHandler(callback, handler, cacheKey, resolveId), 'referenceId', 'post'),
27277
+ action: EnumPostActions.OnPostUpdated,
27278
+ }));
27279
+ };
27280
+ const getPostSubscription = (cacheKey) => {
27281
+ return [
27282
+ { fn: onPostCreated, action: EnumPostActions.OnPostCreated },
27283
+ { fn: onPostUpdated, action: EnumPostActions.OnPostUpdated },
27284
+ { fn: onPostUpdatedLocal, action: EnumPostActions.OnPostUpdated },
27285
+ { fn: onPostDeleted, action: EnumPostActions.OnPostDeleted },
27286
+ { fn: onPostFlagged, action: EnumPostActions.OnPostFlagged },
27287
+ { fn: onPostUnflagged, action: EnumPostActions.OnPostUnflagged },
27288
+ { fn: onPostApproved, action: EnumPostActions.OnPostApproved },
27289
+ { fn: onPostDeclined, action: EnumPostActions.OnPostDeclined },
27290
+ { fn: onPostReactionAdded, action: EnumPostActions.OnPostReactionAdded },
27291
+ { fn: onPostReactionRemoved, action: EnumPostActions.OnPostReactionRemoved },
27292
+ { fn: onLocalPostReactionAdded, action: EnumPostActions.OnPostReactionAdded },
27293
+ { fn: onLocalPostReactionRemoved, action: EnumPostActions.OnPostReactionRemoved },
27294
+ { fn: onLocalPostDeleted, action: EnumPostActions.OnPostDeleted },
27295
+ ...generateCommentSubscriptions({ cacheKey }),
27296
+ ];
27297
+ };
27298
+ const resolvePostIdsFromRooms = (rooms, posts) => {
27299
+ var _a;
27300
+ return ((_a = rooms
27301
+ .map(room => {
27302
+ const post = posts.find(post => post.postId === room.referenceId);
27303
+ return post ? getResolver('post')({ postId: post === null || post === void 0 ? void 0 : post.postId }) : undefined;
27304
+ })
27305
+ .filter(isNonNullable)) !== null && _a !== void 0 ? _a : []);
27306
+ };
27307
+
27308
+ class CommunityFeedLiveCollectionController extends LiveCollectionController {
27309
+ constructor(query, callback) {
27310
+ const queryStreamId = hash__default["default"](query);
27311
+ const cacheKey = ['communityFeed', 'collection', queryStreamId];
27312
+ const paginationController = new CommunityFeedPaginationController(query);
27313
+ super(paginationController, queryStreamId, cacheKey, callback);
27314
+ this.query = query;
27315
+ this.queryStreamController = new CommunityFeedQueryStreamController(this.query, this.cacheKey, this.notifyChange.bind(this), preparePostPayload);
27316
+ this.callback = callback.bind(this);
27317
+ this.loadPage({ initial: true });
27318
+ }
27319
+ setup() {
27320
+ var _a;
27321
+ const collection = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
27322
+ if (!collection) {
27323
+ pushToCache(this.cacheKey, {
27324
+ data: [],
27325
+ params: {},
27326
+ });
27327
+ }
27328
+ }
27329
+ async persistModel(queryPayload) {
27330
+ await this.queryStreamController.saveToMainDB(queryPayload);
27331
+ }
27332
+ persistQueryStream({ response, direction, refresh, }) {
27333
+ this.queryStreamController.appendToQueryStream(response, direction, refresh);
27334
+ }
27335
+ startSubscription() {
27336
+ return this.queryStreamController.subscribeRTE(getPostSubscription(this.cacheKey));
27337
+ }
27338
+ notifyChange({ origin, loading, error }) {
27339
+ var _a, _b;
27340
+ const collection = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
27341
+ if (!collection)
27342
+ return;
27343
+ const data = ((_b = collection.data
27344
+ .map(id => pullFromCache(['post', 'get', id]))
27345
+ .filter(isNonNullable)
27346
+ .map(({ data }) => data)) !== null && _b !== void 0 ? _b : []).map(LinkedObject.post);
27347
+ if (!this.shouldNotify(data) && origin === 'event')
27348
+ return;
27349
+ this.callback({
27350
+ onNextPage: () => this.loadPage({ direction: "next" /* Amity.LiveCollectionPageDirection.NEXT */ }),
27351
+ data,
27352
+ hasNextPage: !!this.paginationController.getNextToken(),
27353
+ loading,
27354
+ error,
27355
+ });
27356
+ }
27357
+ }
27358
+
27359
+ /* begin_public_function
27360
+ id: feed.query.community_feed
27361
+ */
27362
+ /**
27363
+ * ```js
27364
+ * import { FeedRepository } from '@amityco/ts-sdk'
27365
+ *
27366
+ * let posts = []
27367
+ * const unsubscribe = FeedRepository.getCommunityFeed({
27368
+ * communityId: 'community-id',
27369
+ * sortBy?: 'lastCreated' | 'firstCreated' | 'lastUpdated' | 'firstUpdated',
27370
+ * includeDeleted?: boolean,
27371
+ * feedType?: 'reviewing' | 'published' | 'declined',
27372
+ * tags?: string[],
27373
+ * includeMixedStructure?: boolean,
27374
+ * limit?: number,
27375
+ * }, response => processResponse(response))
27376
+ * ```
27377
+ *
27378
+ * Observe all mutations on a list of {@link Amity.Post} for a given community feed.
27379
+ *
27380
+ * @param params - Parameters for querying the community feed:
27381
+ * @param params.communityId The ID of the community (required)
27382
+ * @param params.sortBy The sorting order of the feed (optional)
27383
+ * @param params.includeDeleted Whether to include deleted posts (optional)
27384
+ * @param params.feedType The type of the feed: 'reviewing', 'published', or 'declined' (optional)
27385
+ * @param params.tags Array of tags to filter posts (optional)
27386
+ * @param params.includeMixedStructure Whether to include mixed structure posts (optional)
27387
+ * @param params.limit The maximum number of posts to retrieve (optional)
27388
+ * @param callback The function to call when new data are available
27389
+ * @param config Additional live collection configuration (optional)
27390
+ * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the feed
27391
+ *
27392
+ * @category Posts Live Collection
27393
+ */
27394
+ const getCommunityFeed = (params, callback, config) => {
27395
+ const { log, cache } = getActiveClient();
27396
+ if (!cache) {
27397
+ console.log(ENABLE_CACHE_MESSAGE);
27398
+ }
27399
+ const timestamp = Date.now();
27400
+ log(`getCommunityFeed(tmpid: ${timestamp}) > listen`);
27401
+ const communityFeedLiveCollection = new CommunityFeedLiveCollectionController(params, callback);
27402
+ const disposers = communityFeedLiveCollection.startSubscription();
27403
+ const cacheKey = communityFeedLiveCollection.getCacheKey();
27404
+ disposers.push(() => dropFromCache(cacheKey));
27405
+ return () => {
27406
+ log(`getCommunityFeed(tmpid: ${timestamp}) > dispose`);
27407
+ disposers.forEach(fn => fn());
27408
+ };
27409
+ };
27410
+ /* end_public_function */
27411
+
26748
27412
  var index$c = /*#__PURE__*/Object.freeze({
26749
27413
  __proto__: null,
26750
27414
  queryGlobalFeed: queryGlobalFeed,
26751
27415
  getCustomRankingGlobalFeed: getCustomRankingGlobalFeed,
26752
27416
  getGlobalFeed: getGlobalFeed,
26753
- getUserFeed: getUserFeed
27417
+ getUserFeed: getUserFeed,
27418
+ getCommunityFeed: getCommunityFeed
26754
27419
  });
26755
27420
 
26756
27421
  /* begin_public_function
@@ -27496,61 +28161,6 @@ class PostQueryStreamController extends QueryStreamController {
27496
28161
  }
27497
28162
  }
27498
28163
 
27499
- const commentEventHandler = (callback, eventHandler, cacheKey, resolveId) => {
27500
- return eventHandler(async (comment) => {
27501
- var _a;
27502
- const currentCollection = (_a = pullFromCache(cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
27503
- if (!currentCollection ||
27504
- !currentCollection.data.includes(resolveId ? resolveId(comment.referenceId) : comment.referenceId))
27505
- return;
27506
- await getPost$1(comment.referenceId);
27507
- callback(comment);
27508
- });
27509
- };
27510
- const generateCommentSubscriptions = ({ cacheKey, resolveId, }) => {
27511
- const eventHandlers = [
27512
- onCommentCreated,
27513
- onCommentDeleted,
27514
- onCommentReactionAdded,
27515
- onCommentReactionRemoved,
27516
- onCommentCreatedLocal,
27517
- onCommentDeleteLocal,
27518
- onLocalCommentReactionAdded,
27519
- onLocalCommentReactionRemoved,
27520
- ];
27521
- return eventHandlers.map(handler => ({
27522
- fn: convertEventPayload((callback) => commentEventHandler(callback, handler, cacheKey, resolveId), 'referenceId', 'post'),
27523
- action: EnumPostActions.OnPostUpdated,
27524
- }));
27525
- };
27526
- const getPostSubscription = (cacheKey) => {
27527
- return [
27528
- { fn: onPostCreated, action: EnumPostActions.OnPostCreated },
27529
- { fn: onPostUpdated, action: EnumPostActions.OnPostUpdated },
27530
- { fn: onPostUpdatedLocal, action: EnumPostActions.OnPostUpdated },
27531
- { fn: onPostDeleted, action: EnumPostActions.OnPostDeleted },
27532
- { fn: onPostFlagged, action: EnumPostActions.OnPostFlagged },
27533
- { fn: onPostUnflagged, action: EnumPostActions.OnPostUnflagged },
27534
- { fn: onPostApproved, action: EnumPostActions.OnPostApproved },
27535
- { fn: onPostDeclined, action: EnumPostActions.OnPostDeclined },
27536
- { fn: onPostReactionAdded, action: EnumPostActions.OnPostReactionAdded },
27537
- { fn: onPostReactionRemoved, action: EnumPostActions.OnPostReactionRemoved },
27538
- { fn: onLocalPostReactionAdded, action: EnumPostActions.OnPostReactionAdded },
27539
- { fn: onLocalPostReactionRemoved, action: EnumPostActions.OnPostReactionRemoved },
27540
- { fn: onLocalPostDeleted, action: EnumPostActions.OnPostDeleted },
27541
- ...generateCommentSubscriptions({ cacheKey }),
27542
- ];
27543
- };
27544
- const resolvePostIdsFromRooms = (rooms, posts) => {
27545
- var _a;
27546
- return ((_a = rooms
27547
- .map(room => {
27548
- const post = posts.find(post => post.postId === room.referenceId);
27549
- return post ? getResolver('post')({ postId: post === null || post === void 0 ? void 0 : post.postId }) : undefined;
27550
- })
27551
- .filter(isNonNullable)) !== null && _a !== void 0 ? _a : []);
27552
- };
27553
-
27554
28164
  class PostLiveCollectionController extends LiveCollectionController {
27555
28165
  constructor(query, callback) {
27556
28166
  const queryStreamId = hash__default["default"](query);