@amityco/ts-sdk 7.27.0 → 7.27.1-075c277.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 (194) hide show
  1. package/dist/@types/core/events.d.ts +5 -0
  2. package/dist/@types/core/events.d.ts.map +1 -1
  3. package/dist/@types/core/payload.d.ts +26 -0
  4. package/dist/@types/core/payload.d.ts.map +1 -1
  5. package/dist/@types/core/permissions.d.ts +10 -4
  6. package/dist/@types/core/permissions.d.ts.map +1 -1
  7. package/dist/@types/domains/analytics.d.ts +3 -1
  8. package/dist/@types/domains/analytics.d.ts.map +1 -1
  9. package/dist/@types/domains/channel.d.ts +5 -0
  10. package/dist/@types/domains/channel.d.ts.map +1 -1
  11. package/dist/@types/domains/client.d.ts +18 -0
  12. package/dist/@types/domains/client.d.ts.map +1 -1
  13. package/dist/@types/domains/comment.d.ts +27 -0
  14. package/dist/@types/domains/comment.d.ts.map +1 -1
  15. package/dist/@types/domains/community.d.ts +1 -0
  16. package/dist/@types/domains/community.d.ts.map +1 -1
  17. package/dist/@types/domains/curatedContent.d.ts +25 -0
  18. package/dist/@types/domains/curatedContent.d.ts.map +1 -0
  19. package/dist/@types/domains/pinnedMessage.d.ts +43 -0
  20. package/dist/@types/domains/pinnedMessage.d.ts.map +1 -0
  21. package/dist/@types/domains/post.d.ts +11 -0
  22. package/dist/@types/domains/post.d.ts.map +1 -1
  23. package/dist/@types/index.d.ts +3 -1
  24. package/dist/@types/index.d.ts.map +1 -1
  25. package/dist/analytic/service/analytic/AnalyticsEngine.d.ts +3 -0
  26. package/dist/analytic/service/analytic/AnalyticsEngine.d.ts.map +1 -1
  27. package/dist/analytic/service/analytic/AnalyticsEventCapturer.d.ts +3 -0
  28. package/dist/analytic/service/analytic/AnalyticsEventCapturer.d.ts.map +1 -1
  29. package/dist/channelRepository/events/index.d.ts +2 -0
  30. package/dist/channelRepository/events/index.d.ts.map +1 -1
  31. package/dist/channelRepository/events/onChannelMemberRoleAdded.d.ts +22 -0
  32. package/dist/channelRepository/events/onChannelMemberRoleAdded.d.ts.map +1 -1
  33. package/dist/channelRepository/events/onChannelMemberRoleRemoved.d.ts +22 -0
  34. package/dist/channelRepository/events/onChannelMemberRoleRemoved.d.ts.map +1 -1
  35. package/dist/channelRepository/events/onChannelMessagePinned.d.ts +24 -0
  36. package/dist/channelRepository/events/onChannelMessagePinned.d.ts.map +1 -0
  37. package/dist/channelRepository/events/onChannelMessageUnpinned.d.ts +21 -0
  38. package/dist/channelRepository/events/onChannelMessageUnpinned.d.ts.map +1 -0
  39. package/dist/channelRepository/events/onChannelPinnedMessageDeleted.d.ts +12 -0
  40. package/dist/channelRepository/events/onChannelPinnedMessageDeleted.d.ts.map +1 -0
  41. package/dist/channelRepository/events/tests/onChannelMemberRoleAdded.test.d.ts +2 -0
  42. package/dist/channelRepository/events/tests/onChannelMemberRoleAdded.test.d.ts.map +1 -0
  43. package/dist/channelRepository/events/tests/onChannelMemberRoleRemoved.test.d.ts +2 -0
  44. package/dist/channelRepository/events/tests/onChannelMemberRoleRemoved.test.d.ts.map +1 -0
  45. package/dist/channelRepository/events/tests/onChannelMessagePinned.test.d.ts +2 -0
  46. package/dist/channelRepository/events/tests/onChannelMessagePinned.test.d.ts.map +1 -0
  47. package/dist/channelRepository/observers/getChannel.d.ts.map +1 -1
  48. package/dist/channelRepository/observers/getChannels/ChannelLiveCollectionController.d.ts.map +1 -1
  49. package/dist/channelRepository/observers/getMyMembership.d.ts.map +1 -1
  50. package/dist/channelRepository/observers/searchChannels/SearchChannelLiveCollectionController.d.ts.map +1 -1
  51. package/dist/channelRepository/utils/applyChannelRolePayload.d.ts +21 -0
  52. package/dist/channelRepository/utils/applyChannelRolePayload.d.ts.map +1 -0
  53. package/dist/channelRepository/utils/index.d.ts +2 -0
  54. package/dist/channelRepository/utils/index.d.ts.map +1 -1
  55. package/dist/channelRepository/utils/tests/pinnedMessage.test.d.ts +2 -0
  56. package/dist/channelRepository/utils/tests/pinnedMessage.test.d.ts.map +1 -0
  57. package/dist/channelRepository/utils/updatePinnedMessageCache.d.ts +24 -0
  58. package/dist/channelRepository/utils/updatePinnedMessageCache.d.ts.map +1 -0
  59. package/dist/client/api/createClient.d.ts +5 -1
  60. package/dist/client/api/createClient.d.ts.map +1 -1
  61. package/dist/client/api/getSocialSettings.d.ts.map +1 -1
  62. package/dist/client/api/setupLoginSubscriptions.d.ts.map +1 -1
  63. package/dist/client/api/tests/getSocialSettings.test.d.ts +2 -0
  64. package/dist/client/api/tests/getSocialSettings.test.d.ts.map +1 -0
  65. package/dist/commentRepository/api/approveComment.d.ts +17 -0
  66. package/dist/commentRepository/api/approveComment.d.ts.map +1 -0
  67. package/dist/commentRepository/api/createComment.d.ts +12 -1
  68. package/dist/commentRepository/api/createComment.d.ts.map +1 -1
  69. package/dist/commentRepository/api/declineComment.d.ts +18 -0
  70. package/dist/commentRepository/api/declineComment.d.ts.map +1 -0
  71. package/dist/commentRepository/api/deleteComment.d.ts.map +1 -1
  72. package/dist/commentRepository/api/index.d.ts +2 -0
  73. package/dist/commentRepository/api/index.d.ts.map +1 -1
  74. package/dist/commentRepository/api/tests/approveComment.test.d.ts +2 -0
  75. package/dist/commentRepository/api/tests/approveComment.test.d.ts.map +1 -0
  76. package/dist/commentRepository/api/tests/createComment.optimistic.test.d.ts +2 -0
  77. package/dist/commentRepository/api/tests/createComment.optimistic.test.d.ts.map +1 -0
  78. package/dist/commentRepository/api/tests/declineComment.test.d.ts +2 -0
  79. package/dist/commentRepository/api/tests/declineComment.test.d.ts.map +1 -0
  80. package/dist/commentRepository/api/updateComment.d.ts.map +1 -1
  81. package/dist/commentRepository/events/index.d.ts +1 -0
  82. package/dist/commentRepository/events/index.d.ts.map +1 -1
  83. package/dist/commentRepository/events/onCommentApproved.d.ts +17 -0
  84. package/dist/commentRepository/events/onCommentApproved.d.ts.map +1 -0
  85. package/dist/commentRepository/events/tests/onCommentApproved.test.d.ts +2 -0
  86. package/dist/commentRepository/events/tests/onCommentApproved.test.d.ts.map +1 -0
  87. package/dist/commentRepository/events/tests/onCommentCreated.count.test.d.ts +2 -0
  88. package/dist/commentRepository/events/tests/onCommentCreated.count.test.d.ts.map +1 -0
  89. package/dist/commentRepository/events/utils.d.ts.map +1 -1
  90. package/dist/commentRepository/internalApi/getComment.d.ts.map +1 -1
  91. package/dist/commentRepository/observers/getComment.d.ts.map +1 -1
  92. package/dist/commentRepository/observers/getCommentReviewQueue/LiveCollectionController.d.ts +13 -0
  93. package/dist/commentRepository/observers/getCommentReviewQueue/LiveCollectionController.d.ts.map +1 -0
  94. package/dist/commentRepository/observers/getCommentReviewQueue/PaginationController.d.ts +5 -0
  95. package/dist/commentRepository/observers/getCommentReviewQueue/PaginationController.d.ts.map +1 -0
  96. package/dist/commentRepository/observers/getCommentReviewQueue/QueryStreamController.d.ts +15 -0
  97. package/dist/commentRepository/observers/getCommentReviewQueue/QueryStreamController.d.ts.map +1 -0
  98. package/dist/commentRepository/observers/getCommentReviewQueue.d.ts +22 -0
  99. package/dist/commentRepository/observers/getCommentReviewQueue.d.ts.map +1 -0
  100. package/dist/commentRepository/observers/getComments/CommentLiveCollectionController.d.ts +1 -1
  101. package/dist/commentRepository/observers/getComments/CommentLiveCollectionController.d.ts.map +1 -1
  102. package/dist/commentRepository/observers/getComments/CommentQueryStreamController.d.ts.map +1 -1
  103. package/dist/commentRepository/observers/getComments/enums.d.ts +1 -0
  104. package/dist/commentRepository/observers/getComments/enums.d.ts.map +1 -1
  105. package/dist/commentRepository/observers/index.d.ts +1 -0
  106. package/dist/commentRepository/observers/index.d.ts.map +1 -1
  107. package/dist/commentRepository/observers/tests/integrations/getCommentReviewQueue.integration.test.d.ts +2 -0
  108. package/dist/commentRepository/observers/tests/integrations/getCommentReviewQueue.integration.test.d.ts.map +1 -0
  109. package/dist/commentRepository/utils/approval.d.ts +49 -0
  110. package/dist/commentRepository/utils/approval.d.ts.map +1 -0
  111. package/dist/commentRepository/utils/payload.d.ts +6 -0
  112. package/dist/commentRepository/utils/payload.d.ts.map +1 -1
  113. package/dist/commentRepository/utils/tests/approval.test.d.ts +2 -0
  114. package/dist/commentRepository/utils/tests/approval.test.d.ts.map +1 -0
  115. package/dist/commentRepository/utils/tests/payload.test.d.ts +2 -0
  116. package/dist/commentRepository/utils/tests/payload.test.d.ts.map +1 -0
  117. package/dist/communityRepository/api/createCommunity.d.ts +1 -1
  118. package/dist/communityRepository/api/createCommunity.d.ts.map +1 -1
  119. package/dist/communityRepository/api/updateCommunity.d.ts +1 -1
  120. package/dist/communityRepository/api/updateCommunity.d.ts.map +1 -1
  121. package/dist/core/device.d.ts +1 -1
  122. package/dist/core/device.d.ts.map +1 -1
  123. package/dist/core/events.d.ts +3 -3
  124. package/dist/core/events.d.ts.map +1 -1
  125. package/dist/core/tests/device.test.d.ts +2 -0
  126. package/dist/core/tests/device.test.d.ts.map +1 -0
  127. package/dist/core/transports/http.d.ts.map +1 -1
  128. package/dist/core/transports/utils.d.ts +8 -0
  129. package/dist/core/transports/utils.d.ts.map +1 -1
  130. package/dist/curatedContentRepository/api/getPool.d.ts +2 -0
  131. package/dist/curatedContentRepository/api/getPool.d.ts.map +1 -0
  132. package/dist/curatedContentRepository/api/index.d.ts +2 -0
  133. package/dist/curatedContentRepository/api/index.d.ts.map +1 -0
  134. package/dist/curatedContentRepository/api/tests/getPool.test.d.ts +2 -0
  135. package/dist/curatedContentRepository/api/tests/getPool.test.d.ts.map +1 -0
  136. package/dist/curatedContentRepository/api/tests/integration/getPool.integration.test.d.ts +2 -0
  137. package/dist/curatedContentRepository/api/tests/integration/getPool.integration.test.d.ts.map +1 -0
  138. package/dist/curatedContentRepository/index.d.ts +2 -0
  139. package/dist/curatedContentRepository/index.d.ts.map +1 -0
  140. package/dist/index.cjs.js +1730 -477
  141. package/dist/index.d.ts +1 -0
  142. package/dist/index.d.ts.map +1 -1
  143. package/dist/index.esm.js +1705 -453
  144. package/dist/index.umd.js +2 -2
  145. package/dist/messagePreview/utils/getChannelMessagePreviewWithUser.d.ts +1 -0
  146. package/dist/messagePreview/utils/getChannelMessagePreviewWithUser.d.ts.map +1 -1
  147. package/dist/messageRepository/api/index.d.ts +2 -0
  148. package/dist/messageRepository/api/index.d.ts.map +1 -1
  149. package/dist/messageRepository/api/pinMessage.d.ts +29 -0
  150. package/dist/messageRepository/api/pinMessage.d.ts.map +1 -0
  151. package/dist/messageRepository/api/tests/pinMessage.test.d.ts +2 -0
  152. package/dist/messageRepository/api/tests/pinMessage.test.d.ts.map +1 -0
  153. package/dist/messageRepository/api/tests/unpinMessage.test.d.ts +2 -0
  154. package/dist/messageRepository/api/tests/unpinMessage.test.d.ts.map +1 -0
  155. package/dist/messageRepository/api/unpinMessage.d.ts +28 -0
  156. package/dist/messageRepository/api/unpinMessage.d.ts.map +1 -0
  157. package/dist/messageRepository/utils/refreshChannelPermissionsForMessage.d.ts +13 -0
  158. package/dist/messageRepository/utils/refreshChannelPermissionsForMessage.d.ts.map +1 -0
  159. package/dist/postRepository/utils/PostCommentCountEngine/ApproveTask.d.ts +16 -0
  160. package/dist/postRepository/utils/PostCommentCountEngine/ApproveTask.d.ts.map +1 -0
  161. package/dist/postRepository/utils/PostCommentCountEngine/PostCommentCountEngine.d.ts +25 -1
  162. package/dist/postRepository/utils/PostCommentCountEngine/PostCommentCountEngine.d.ts.map +1 -1
  163. package/dist/postRepository/utils/PostCommentCountEngine/postCommentCountEngineOnLoginHandler.d.ts +9 -0
  164. package/dist/postRepository/utils/PostCommentCountEngine/postCommentCountEngineOnLoginHandler.d.ts.map +1 -0
  165. package/dist/postRepository/utils/PostCommentCountEngine/tests/getLocalCommentCount.test.d.ts +2 -0
  166. package/dist/postRepository/utils/PostCommentCountEngine/tests/getLocalCommentCount.test.d.ts.map +1 -0
  167. package/dist/postRepository/utils/PostCommentCountEngine/tests/pendingAuthoredComments.test.d.ts +2 -0
  168. package/dist/postRepository/utils/PostCommentCountEngine/tests/pendingAuthoredComments.test.d.ts.map +1 -0
  169. package/dist/postRepository/utils/latestComments.d.ts +10 -0
  170. package/dist/postRepository/utils/latestComments.d.ts.map +1 -0
  171. package/dist/postRepository/utils/payload.d.ts.map +1 -1
  172. package/dist/postRepository/utils/tests/latestComments.test.d.ts +2 -0
  173. package/dist/postRepository/utils/tests/latestComments.test.d.ts.map +1 -0
  174. package/dist/postRepository/utils/tests/payload.test.d.ts +2 -0
  175. package/dist/postRepository/utils/tests/payload.test.d.ts.map +1 -0
  176. package/dist/utils/constants.d.ts +4 -0
  177. package/dist/utils/constants.d.ts.map +1 -1
  178. package/dist/utils/linkedObject/commentLinkedObject.d.ts.map +1 -1
  179. package/dist/utils/linkedObject/curatedContentTopicLinkedObject.d.ts +2 -0
  180. package/dist/utils/linkedObject/curatedContentTopicLinkedObject.d.ts.map +1 -0
  181. package/dist/utils/linkedObject/index.d.ts +1 -0
  182. package/dist/utils/linkedObject/index.d.ts.map +1 -1
  183. package/dist/utils/linkedObject/messageLinkedObject.d.ts.map +1 -1
  184. package/dist/utils/linkedObject/postLinkedObject.d.ts.map +1 -1
  185. package/dist/utils/linkedObject/tests/postLinkedObject.test.d.ts +2 -0
  186. package/dist/utils/linkedObject/tests/postLinkedObject.test.d.ts.map +1 -0
  187. package/dist/utils/tests/dummy/comment.d.ts +1 -0
  188. package/dist/utils/tests/dummy/comment.d.ts.map +1 -1
  189. package/dist/utils/tests/dummy/index.d.ts +1 -0
  190. package/dist/utils/tests/dummy/index.d.ts.map +1 -1
  191. package/dist/utils/tests/dummy/pinnedMessage.d.ts +2 -0
  192. package/dist/utils/tests/dummy/pinnedMessage.d.ts.map +1 -0
  193. package/dist/utils/tests/dummy/post.d.ts.map +1 -1
  194. package/package.json +3 -2
package/dist/index.cjs.js CHANGED
@@ -7,8 +7,9 @@ var mitt = require('mitt');
7
7
  var debug = require('debug');
8
8
  var axios = require('axios');
9
9
  var HttpAgent = require('agentkeepalive');
10
- var mqtt = require('mqtt');
11
10
  var uuid$1 = require('react-native-uuid');
11
+ var mqtt = require('mqtt');
12
+ var uaParserJs = require('ua-parser-js');
12
13
  var jwtDecode = require('jwt-decode');
13
14
  var hash = require('object-hash');
14
15
  var Hls = require('hls.js');
@@ -19,8 +20,8 @@ var mitt__default = /*#__PURE__*/_interopDefaultLegacy(mitt);
19
20
  var debug__default = /*#__PURE__*/_interopDefaultLegacy(debug);
20
21
  var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
21
22
  var HttpAgent__default = /*#__PURE__*/_interopDefaultLegacy(HttpAgent);
22
- var mqtt__default = /*#__PURE__*/_interopDefaultLegacy(mqtt);
23
23
  var uuid__default = /*#__PURE__*/_interopDefaultLegacy(uuid$1);
24
+ var mqtt__default = /*#__PURE__*/_interopDefaultLegacy(mqtt);
24
25
  var jwtDecode__default = /*#__PURE__*/_interopDefaultLegacy(jwtDecode);
25
26
  var hash__default = /*#__PURE__*/_interopDefaultLegacy(hash);
26
27
  var Hls__default = /*#__PURE__*/_interopDefaultLegacy(Hls);
@@ -341,6 +342,7 @@ function getVersion() {
341
342
  const VERSION = getVersion();
342
343
 
343
344
  const COLLECTION_DEFAULT_PAGINATION_LIMIT = 5;
345
+ const LATEST_COMMENTS_LIMIT = 5;
344
346
  const COLLECTION_DEFAULT_PAGINATION_SIZE = 20;
345
347
  const COLLECTION_DEFAULT_CACHING_POLICY = 'cache_then_server';
346
348
  const ENABLE_CACHE_MESSAGE = 'For using Live Collection feature you need to enable Cache!';
@@ -356,7 +358,11 @@ const YEAR = 365 * DAY;
356
358
  const ACCESS_TOKEN_WATCHER_INTERVAL = 10 * MINUTE;
357
359
  const API_ENDPOINTS = {
358
360
  forYouFeed: '/api/v1/feeds/for-you',
361
+ commentReviewQueue: (communityId) => `/api/v3/communities/${encodeURIComponent(communityId)}/comments/review-queue`,
359
362
  };
363
+ // The review-queue route defaults to 10, not the SDK-wide page size, and caps at 100
364
+ const COMMENT_REVIEW_QUEUE_DEFAULT_PAGINATION_SIZE = 10;
365
+ const COMMENT_REVIEW_QUEUE_MAX_PAGINATION_SIZE = 100;
360
366
 
361
367
  // cache constants
362
368
  const CACHE_KEY_GET = 'get';
@@ -1953,6 +1959,8 @@ function computeUrl(type, region) {
1953
1959
  return URLS[type].replace('{region}', region);
1954
1960
  }
1955
1961
 
1962
+ const uuid = () => uuid__default["default"].v4().toString();
1963
+
1956
1964
  /**
1957
1965
  * Unwraps v3 server payload (for error handling and type extraction)
1958
1966
  *
@@ -1977,6 +1985,24 @@ const unwrapPayload = (response) => {
1977
1985
  // unjust, but helps force uncast "T | undefined" to allow deconstruction without warnings
1978
1986
  return undefined;
1979
1987
  };
1988
+ /**
1989
+ * Builds a hex string of `bytes` length from the SDK's uuid helper (a v4 UUID is
1990
+ * 16 bytes / 32 hex chars once hyphens are removed) — avoids a new crypto dependency.
1991
+ */
1992
+ const randomHex = (bytes) => {
1993
+ let hex = '';
1994
+ while (hex.length < bytes * 2)
1995
+ hex += uuid().replace(/-/g, '');
1996
+ return hex.slice(0, bytes * 2);
1997
+ };
1998
+ /**
1999
+ * W3C Trace Context `traceparent`: `version-<16-byte trace-id>-<8-byte span-id>-<flags>`.
2000
+ * The SDK is the trace originator, so it mints a fresh trace + span id per request; flags `01` = sampled.
2001
+ *
2002
+ * @category Transport
2003
+ * @hidden
2004
+ */
2005
+ const generateTraceparent = () => `00-${randomHex(16)}-${randomHex(8)}-01`;
1980
2006
 
1981
2007
  class NetworkActivitiesWatcher {
1982
2008
  constructor() {
@@ -2101,6 +2127,10 @@ const createHttpTransport = (endpoint) => {
2101
2127
  });
2102
2128
  instance.defaults.withCredentials = false;
2103
2129
  instance.interceptors.request.use(config => {
2130
+ // Request tracing — set on every outbound request (login included), never persisted.
2131
+ // Must run before the session-url early-return below so /api/v5/sessions carries them too.
2132
+ config.headers.set('x-request-id', uuid());
2133
+ config.headers.set('traceparent', generateTraceparent());
2104
2134
  // do not check expiration for token creation url
2105
2135
  if (config.url === '/api/v5/sessions') {
2106
2136
  return config;
@@ -2144,20 +2174,28 @@ const createHttpTransport = (endpoint) => {
2144
2174
  });
2145
2175
  return response;
2146
2176
  }, error => {
2147
- var _a, _b, _c;
2177
+ var _a, _b, _c, _d;
2148
2178
  const { response } = error;
2149
2179
  // handle unauthorized request
2150
2180
  if ((response === null || response === void 0 ? void 0 : response.data.code) === 400100 /* Amity.ServerError.UNAUTHORIZED */) {
2151
2181
  fireEvent('tokenTerminated', "terminated" /* Amity.SessionStates.TERMINATED */);
2152
2182
  }
2183
+ // handle globally banned user. The realtime `user.didGlobalBan` event can be missed
2184
+ // (e.g. MQTT dropped before delivery), so mirror the 400100 handling above and fire
2185
+ // the same virtual event the login subscriptions already listen to. That terminates
2186
+ // the session with `TokenTerminationReason.GLOBAL_BAN` and notifies `onClientBanned`
2187
+ // consumers, instead of leaving the session `established` with a revoked token.
2188
+ if (((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.code) === 400312 /* Amity.ServerError.GLOBAL_BAN */) {
2189
+ fireEvent('user.didGlobalBan', { users: [], files: [] });
2190
+ }
2153
2191
  // handle visitor daily usage limit exceeded
2154
- if (((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.code) === 400323 /* Amity.ServerError.VISITOR_USAGE_LIMIT_EXCEEDED */) {
2192
+ if (((_b = response === null || response === void 0 ? void 0 : response.data) === null || _b === void 0 ? void 0 : _b.code) === 400323 /* Amity.ServerError.VISITOR_USAGE_LIMIT_EXCEEDED */) {
2155
2193
  throttledFireVisitorUsageLimit();
2156
2194
  throw new ASCError('Visitor usage limit exceeded', 400323 /* Amity.ServerError.VISITOR_USAGE_LIMIT_EXCEEDED */, "error" /* Amity.ErrorLevel.ERROR */);
2157
2195
  }
2158
2196
  // if it's an error with status in the response payload,
2159
2197
  // then it's an expected error.
2160
- if ((_b = response === null || response === void 0 ? void 0 : response.data) === null || _b === void 0 ? void 0 : _b.status) {
2198
+ if ((_c = response === null || response === void 0 ? void 0 : response.data) === null || _c === void 0 ? void 0 : _c.status) {
2161
2199
  unwrapPayload(response === null || response === void 0 ? void 0 : response.data);
2162
2200
  }
2163
2201
  // as on request cancellation error is returned
@@ -2165,7 +2203,7 @@ const createHttpTransport = (endpoint) => {
2165
2203
  throw error;
2166
2204
  }
2167
2205
  // unexpected error.
2168
- throw new Error((_c = response === null || response === void 0 ? void 0 : response.data) !== null && _c !== void 0 ? _c : error);
2206
+ throw new Error((_d = response === null || response === void 0 ? void 0 : response.data) !== null && _d !== void 0 ? _d : error);
2169
2207
  });
2170
2208
  instance.defaults.paramsSerializer = {
2171
2209
  encode: params => encodeURIComponent(params),
@@ -2363,8 +2401,6 @@ const getItem = async (key) => {
2363
2401
  });
2364
2402
  };
2365
2403
 
2366
- const uuid = () => uuid__default["default"].v4().toString();
2367
-
2368
2404
  // TODO: find a way to uniquely identify the device
2369
2405
  let tabIndex = 0;
2370
2406
  // This logic is running on browser only
@@ -2424,22 +2460,60 @@ const getMQTTClientId = async (userId) => {
2424
2460
  };
2425
2461
  /** @hidden */
2426
2462
  const getDeviceModel = () => {
2427
- var _a, _b;
2463
+ var _a;
2428
2464
  if ((_a = safeProcess.versions) === null || _a === void 0 ? void 0 : _a.node)
2429
2465
  return safeProcess.versions.node;
2430
- if (navigator)
2431
- return `${navigator.product.toLowerCase()}#${(_b = navigator.userAgent) !== null && _b !== void 0 ? _b : 'unknown_agent'}`;
2432
- return 'unknown_model';
2466
+ return undefined;
2467
+ };
2468
+ const hasNavigator = () => typeof navigator !== 'undefined' && !!navigator;
2469
+ const getLocale = () => (hasNavigator() ? navigator.language : undefined);
2470
+ const getTimezone = () => {
2471
+ try {
2472
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
2473
+ }
2474
+ catch (_a) {
2475
+ return undefined;
2476
+ }
2477
+ };
2478
+ const getNetworkType = () => {
2479
+ var _a;
2480
+ if (!hasNavigator())
2481
+ return undefined;
2482
+ return (_a = navigator.connection) === null || _a === void 0 ? void 0 : _a.effectiveType;
2483
+ };
2484
+ const isNode = () => { var _a; return !!((_a = safeProcess.versions) === null || _a === void 0 ? void 0 : _a.node); };
2485
+ const parseUserAgent = () => {
2486
+ if (!hasNavigator() || !navigator.userAgent)
2487
+ return {};
2488
+ const { os, browser, device } = new uaParserJs.UAParser(navigator.userAgent).getResult();
2489
+ const result = {};
2490
+ if (os.name)
2491
+ result.osName = os.name;
2492
+ if (os.version)
2493
+ result.osVersion = os.version;
2494
+ if (browser.name)
2495
+ result.browserName = browser.name;
2496
+ if (browser.version)
2497
+ result.browserVersion = browser.version;
2498
+ if (device.type === 'tablet')
2499
+ result.deviceType = 'tablet';
2500
+ else if (device.type === 'mobile')
2501
+ result.deviceType = 'mobile';
2502
+ else
2503
+ result.deviceType = 'desktop';
2504
+ return result;
2433
2505
  };
2434
2506
  /** @hidden */
2435
2507
  const getDeviceInfo = () => {
2436
- const model = getDeviceModel();
2437
- return {
2438
- // TODO: replace with: kind: process.versions ? 'node' : 'web', when backend has fixed this
2439
- kind: 'web',
2440
- model,
2441
- sdkVersion: VERSION,
2442
- };
2508
+ var _a;
2509
+ let client;
2510
+ try {
2511
+ client = getActiveClient();
2512
+ }
2513
+ catch (_b) {
2514
+ client = undefined;
2515
+ }
2516
+ return Object.assign({ kind: isNode() ? 'node' : 'web', model: getDeviceModel(), sdkVersion: VERSION, product: (_a = client === null || client === void 0 ? void 0 : client.product) !== null && _a !== void 0 ? _a : 'sdk', uikitVersion: client === null || client === void 0 ? void 0 : client.uikitVersion, appId: client === null || client === void 0 ? void 0 : client.appId, appVersion: client === null || client === void 0 ? void 0 : client.appVersion, locale: getLocale(), timezone: getTimezone(), networkType: getNetworkType() }, parseUserAgent());
2443
2517
  };
2444
2518
 
2445
2519
  /* eslint-disable no-console */
@@ -3848,6 +3922,95 @@ const prepareChannelPayload = async (rawPayload, options = { isMessagePreviewUpd
3848
3922
  channelUsers });
3849
3923
  };
3850
3924
 
3925
+ const channelCacheKey = (channel) => [
3926
+ 'channel',
3927
+ 'get',
3928
+ getResolver('channel')(channel),
3929
+ ];
3930
+ /**
3931
+ * Find a cached channel by any of its ids (internal, public, or `channelId`).
3932
+ *
3933
+ * The channel cache is keyed by the channel internal id (see `idResolvers.channel`), but
3934
+ * realtime payloads reference channels by different ids: pin events carry the **public**
3935
+ * id, messages carry `channelId`. Try the cheap direct lookup first, then scan.
3936
+ */
3937
+ const findCachedChannelByAnyId = (id) => {
3938
+ var _a, _b, _c;
3939
+ if (!id)
3940
+ return undefined;
3941
+ const direct = (_a = pullFromCache(['channel', 'get', id])) === null || _a === void 0 ? void 0 : _a.data;
3942
+ if (direct)
3943
+ return direct;
3944
+ return (_c = (_b = queryCache(['channel', 'get'])) === null || _b === void 0 ? void 0 : _b.find(({ data }) => data.channelInternalId === id || data.channelPublicId === id || data.channelId === id)) === null || _c === void 0 ? void 0 : _c.data;
3945
+ };
3946
+ /**
3947
+ * Resolve the cached channel a `channel.messagePinned` / `channel.messageUnpinned` event
3948
+ * refers to. `data.channelId` is the channel public id; a pinned message body (present on
3949
+ * `messagePinned` only) also carries the internal id, which is the direct cache key.
3950
+ */
3951
+ const findCachedChannelForPin = (payload) => {
3952
+ var _a, _b;
3953
+ return (_b = findCachedChannelByAnyId((_a = payload.pinnedMessage) === null || _a === void 0 ? void 0 : _a.channelId)) !== null && _b !== void 0 ? _b : findCachedChannelByAnyId(payload.channelId);
3954
+ };
3955
+ /**
3956
+ * Write a new pin snapshot (or `null` to clear) on a cached channel and ingest any
3957
+ * users delivered with it (the pin author and the message author), so the banner can
3958
+ * render a byline without a second fetch.
3959
+ *
3960
+ * Every `messagePinned` payload is a full snapshot — "this is the pin now" — so the last
3961
+ * one received always wins; there is no merging with the previous pin.
3962
+ */
3963
+ const updatePinnedMessageCache = (channel, pinnedMessage, users) => {
3964
+ if (users && users.length > 0) {
3965
+ ingestInCache({ users: users.map(convertRawUserToInternalUser) });
3966
+ }
3967
+ const updated = Object.assign(Object.assign({}, channel), { pinnedMessage });
3968
+ pushToCache(channelCacheKey(channel), updated);
3969
+ return updated;
3970
+ };
3971
+
3972
+ /**
3973
+ * Applies a `channel.roleAdded` / `channel.roleRemoved` realtime payload to the cache.
3974
+ *
3975
+ * The event names every member the role request affected together with the
3976
+ * roles and channel-scoped permissions each of them is left holding. That is a
3977
+ * full state, not a delta, so it is written straight over the cached membership.
3978
+ *
3979
+ * Members the SDK has not fetched yet are skipped: the payload does not carry
3980
+ * enough to build a full {@link Amity.Membership} and the live collections only
3981
+ * emit rows that already exist in cache.
3982
+ *
3983
+ * @param payload the `data` of the realtime event
3984
+ * @returns the refreshed channel (when resolvable) and the updated cached memberships
3985
+ *
3986
+ * @hidden
3987
+ */
3988
+ const applyChannelRolePayload = (payload) => {
3989
+ var _a, _b, _c;
3990
+ const { channelId, users = [], channels = [] } = payload;
3991
+ const rawChannel = (_a = channels.find(channel => channel.channelId === channelId)) !== null && _a !== void 0 ? _a : channels[0];
3992
+ let channel;
3993
+ if (rawChannel) {
3994
+ const channelKey = ['channel', 'get', getResolver('channel')(rawChannel)];
3995
+ // the role event does not touch message previews, keep whatever preview is cached
3996
+ const converted = convertFromRaw(rawChannel, { isMessagePreviewUpdated: false });
3997
+ upsertInCache(channelKey, converted);
3998
+ channel = (_c = (_b = pullFromCache(channelKey)) === null || _b === void 0 ? void 0 : _b.data) !== null && _c !== void 0 ? _c : converted;
3999
+ }
4000
+ const members = [];
4001
+ users.forEach(({ userId, roles, permissions }) => {
4002
+ var _a, _b, _c;
4003
+ const memberKey = ['channelUsers', 'get', getResolver('channelUsers')({ channelId, userId })];
4004
+ const cached = (_a = pullFromCache(memberKey)) === null || _a === void 0 ? void 0 : _a.data;
4005
+ if (!cached)
4006
+ return;
4007
+ const updated = Object.assign(Object.assign({}, cached), { roles, permissions });
4008
+ pushToCache(memberKey, updated);
4009
+ members.push((_c = (_b = pullFromCache(memberKey)) === null || _b === void 0 ? void 0 : _b.data) !== null && _c !== void 0 ? _c : updated);
4010
+ });
4011
+ return { channel, members };
4012
+ };
4013
+
3851
4014
  /**
3852
4015
  * ```js
3853
4016
  * import { getSubChannelMarkers } from '@amityco/ts-sdk'
@@ -4033,32 +4196,6 @@ const channelMemberLinkedObject = (channelMember) => {
4033
4196
  } });
4034
4197
  };
4035
4198
 
4036
- /**
4037
- *
4038
- * get current user's membership data
4039
- *
4040
- * @returns the {@link Amity.Membership<'channel'>} objects
4041
- *
4042
- * @category Channel API
4043
- * @async
4044
- */
4045
- const getMyMembership$1 = (channelId) => {
4046
- const client = getActiveClient();
4047
- client.log('channel/getMyMembership.locally');
4048
- const cacheChannelUser = pullFromCache([
4049
- 'channelUsers',
4050
- 'get',
4051
- `${channelId}#${client.userId}`,
4052
- ]);
4053
- if (!cacheChannelUser) {
4054
- throw new ASCApiError('Item not found!', 400400 /* Amity.ServerError.ITEM_NOT_FOUND */, "error" /* Amity.ErrorLevel.ERROR */);
4055
- }
4056
- return {
4057
- data: cacheChannelUser.data,
4058
- cachedAt: cacheChannelUser.cachedAt,
4059
- };
4060
- };
4061
-
4062
4199
  /**
4063
4200
  * ```js
4064
4201
  * import { isEqual } from '~/utils/isEqual'
@@ -4129,55 +4266,81 @@ function isEqual(x, y) {
4129
4266
  }));
4130
4267
  }
4131
4268
 
4132
- const callbacks$c = [];
4269
+ /**
4270
+ *
4271
+ * get current user's membership data
4272
+ *
4273
+ * @returns the {@link Amity.Membership<'channel'>} objects
4274
+ *
4275
+ * @category Channel API
4276
+ * @async
4277
+ */
4278
+ const getMyMembership$1 = (channelId) => {
4279
+ const client = getActiveClient();
4280
+ client.log('channel/getMyMembership.locally');
4281
+ const cacheChannelUser = pullFromCache([
4282
+ 'channelUsers',
4283
+ 'get',
4284
+ `${channelId}#${client.userId}`,
4285
+ ]);
4286
+ if (!cacheChannelUser) {
4287
+ throw new ASCApiError('Item not found!', 400400 /* Amity.ServerError.ITEM_NOT_FOUND */, "error" /* Amity.ErrorLevel.ERROR */);
4288
+ }
4289
+ return {
4290
+ data: cacheChannelUser.data,
4291
+ cachedAt: cacheChannelUser.cachedAt,
4292
+ };
4293
+ };
4294
+
4295
+ const callbacks$e = [];
4133
4296
  let mainDisposer$b = null;
4134
- const dispose$c = (cb) => {
4135
- const index = callbacks$c.indexOf(cb);
4297
+ const dispose$e = (cb) => {
4298
+ const index = callbacks$e.indexOf(cb);
4136
4299
  if (index > -1) {
4137
- callbacks$c.splice(index, 1);
4300
+ callbacks$e.splice(index, 1);
4138
4301
  }
4139
- if (callbacks$c.length === 0) {
4302
+ if (callbacks$e.length === 0) {
4140
4303
  mainDisposer$b === null || mainDisposer$b === void 0 ? void 0 : mainDisposer$b();
4141
4304
  }
4142
4305
  };
4143
4306
  const onChannelCreated = (callback) => {
4144
- if (callbacks$c.length === 0) {
4307
+ if (callbacks$e.length === 0) {
4145
4308
  const client = getActiveClient();
4146
4309
  const filter = async (payload) => {
4147
4310
  const data = await prepareChannelPayload(payload);
4148
4311
  ingestInCache(data);
4149
- callbacks$c.forEach(cb => cb(data.channels[0]));
4312
+ callbacks$e.forEach(cb => cb(data.channels[0]));
4150
4313
  };
4151
4314
  mainDisposer$b = createEventSubscriber(client, 'onChannelCreated', 'channel.created', filter);
4152
4315
  }
4153
- callbacks$c.push(callback);
4154
- return () => dispose$c(callback);
4316
+ callbacks$e.push(callback);
4317
+ return () => dispose$e(callback);
4155
4318
  };
4156
4319
 
4157
- const callbacks$b = [];
4158
- const mainDisposers = [];
4159
- const dispose$b = (cb) => {
4160
- const index = callbacks$b.indexOf(cb);
4320
+ const callbacks$d = [];
4321
+ const mainDisposers$2 = [];
4322
+ const dispose$d = (cb) => {
4323
+ const index = callbacks$d.indexOf(cb);
4161
4324
  if (index > -1) {
4162
- callbacks$b.splice(index, 1);
4325
+ callbacks$d.splice(index, 1);
4163
4326
  }
4164
- if (callbacks$b.length === 0) {
4165
- mainDisposers.forEach(fn => fn());
4327
+ if (callbacks$d.length === 0) {
4328
+ mainDisposers$2.forEach(fn => fn());
4166
4329
  }
4167
4330
  };
4168
4331
  const onChannelUpdated = (callback) => {
4169
4332
  const client = getActiveClient();
4170
- if (callbacks$b.length === 0) {
4333
+ if (callbacks$d.length === 0) {
4171
4334
  const filter = async (payload) => {
4172
4335
  const data = await prepareChannelPayload(payload);
4173
4336
  ingestInCache(data);
4174
- callbacks$b.forEach(cb => cb(data.channels[0]));
4337
+ callbacks$d.forEach(cb => cb(data.channels[0]));
4175
4338
  };
4176
- mainDisposers.push(createEventSubscriber(client, 'onChannelUpdated', 'channel.updated', filter));
4177
- mainDisposers.push(createEventSubscriber(client, 'onChannelUpdated', 'local.channel.updated', payload => callbacks$b.forEach(cb => cb(payload.channels[0]))));
4339
+ mainDisposers$2.push(createEventSubscriber(client, 'onChannelUpdated', 'channel.updated', filter));
4340
+ mainDisposers$2.push(createEventSubscriber(client, 'onChannelUpdated', 'local.channel.updated', payload => callbacks$d.forEach(cb => cb(payload.channels[0]))));
4178
4341
  }
4179
- callbacks$b.push(callback);
4180
- return () => dispose$b(callback);
4342
+ callbacks$d.push(callback);
4343
+ return () => dispose$d(callback);
4181
4344
  };
4182
4345
 
4183
4346
  const addFlagIsDeletedSubChannelUnreadByChannelId = (channelId) => {
@@ -4200,19 +4363,19 @@ const deleteChannelUnreadByChannelId = (channelId) => {
4200
4363
  }
4201
4364
  };
4202
4365
 
4203
- const callbacks$a = [];
4366
+ const callbacks$c = [];
4204
4367
  let mainDisposer$a = null;
4205
- const dispose$a = (cb) => {
4206
- const index = callbacks$a.indexOf(cb);
4368
+ const dispose$c = (cb) => {
4369
+ const index = callbacks$c.indexOf(cb);
4207
4370
  if (index > -1) {
4208
- callbacks$a.splice(index, 1);
4371
+ callbacks$c.splice(index, 1);
4209
4372
  }
4210
- if (callbacks$a.length === 0) {
4373
+ if (callbacks$c.length === 0) {
4211
4374
  mainDisposer$a === null || mainDisposer$a === void 0 ? void 0 : mainDisposer$a();
4212
4375
  }
4213
4376
  };
4214
4377
  const onChannelDeleted = (callback) => {
4215
- if (callbacks$a.length === 0) {
4378
+ if (callbacks$c.length === 0) {
4216
4379
  const client = getActiveClient();
4217
4380
  const filter = async (payload) => {
4218
4381
  const data = await prepareChannelPayload(payload);
@@ -4232,53 +4395,53 @@ const onChannelDeleted = (callback) => {
4232
4395
  }
4233
4396
  });
4234
4397
  ingestInCache(data);
4235
- callbacks$a.forEach(cb => cb(data.channels[0]));
4398
+ callbacks$c.forEach(cb => cb(data.channels[0]));
4236
4399
  };
4237
4400
  mainDisposer$a = createEventSubscriber(client, 'onChannelDeleted', 'channel.deleted', filter);
4238
4401
  }
4239
- callbacks$a.push(callback);
4240
- return () => dispose$a(callback);
4402
+ callbacks$c.push(callback);
4403
+ return () => dispose$c(callback);
4241
4404
  };
4242
4405
 
4243
- const callbacks$9 = [];
4406
+ const callbacks$b = [];
4244
4407
  let mainDisposer$9 = null;
4245
- const dispose$9 = (cb) => {
4246
- const index = callbacks$9.indexOf(cb);
4408
+ const dispose$b = (cb) => {
4409
+ const index = callbacks$b.indexOf(cb);
4247
4410
  if (index > -1) {
4248
- callbacks$9.splice(index, 1);
4411
+ callbacks$b.splice(index, 1);
4249
4412
  }
4250
- if (callbacks$9.length === 0) {
4413
+ if (callbacks$b.length === 0) {
4251
4414
  mainDisposer$9 === null || mainDisposer$9 === void 0 ? void 0 : mainDisposer$9();
4252
4415
  }
4253
4416
  };
4254
4417
  const onChannelJoined = (callback) => {
4255
- if (callbacks$9.length === 0) {
4418
+ if (callbacks$b.length === 0) {
4256
4419
  const client = getActiveClient();
4257
4420
  const filter = async (payload) => {
4258
4421
  const preparedPayload = await prepareChannelPayload(payload);
4259
4422
  const { channels, channelUsers } = preparedPayload;
4260
4423
  ingestInCache(preparedPayload);
4261
- callbacks$9.forEach(cb => cb(channels[0], channelUsers[0]));
4424
+ callbacks$b.forEach(cb => cb(channels[0], channelUsers[0]));
4262
4425
  };
4263
4426
  mainDisposer$9 = createEventSubscriber(client, 'onChannelJoined', 'channel.joined', filter);
4264
4427
  }
4265
- callbacks$9.push(callback);
4266
- return () => dispose$9(callback);
4428
+ callbacks$b.push(callback);
4429
+ return () => dispose$b(callback);
4267
4430
  };
4268
4431
 
4269
- const callbacks$8 = [];
4432
+ const callbacks$a = [];
4270
4433
  let mainDisposer$8 = null;
4271
- const dispose$8 = (cb) => {
4272
- const index = callbacks$8.indexOf(cb);
4434
+ const dispose$a = (cb) => {
4435
+ const index = callbacks$a.indexOf(cb);
4273
4436
  if (index > -1) {
4274
- callbacks$8.splice(index, 1);
4437
+ callbacks$a.splice(index, 1);
4275
4438
  }
4276
- if (callbacks$8.length === 0) {
4439
+ if (callbacks$a.length === 0) {
4277
4440
  mainDisposer$8 === null || mainDisposer$8 === void 0 ? void 0 : mainDisposer$8();
4278
4441
  }
4279
4442
  };
4280
4443
  const onChannelLeft = (callback) => {
4281
- if (callbacks$8.length === 0) {
4444
+ if (callbacks$a.length === 0) {
4282
4445
  const client = getActiveClient();
4283
4446
  const filter = async (payload) => {
4284
4447
  const { userId } = getActiveClient();
@@ -4302,27 +4465,27 @@ const onChannelLeft = (callback) => {
4302
4465
  }
4303
4466
  const { channels, channelUsers } = preparedPayload;
4304
4467
  ingestInCache(preparedPayload);
4305
- callbacks$8.forEach(cb => cb(channels[0], channelUsers[0]));
4468
+ callbacks$a.forEach(cb => cb(channels[0], channelUsers[0]));
4306
4469
  };
4307
4470
  mainDisposer$8 = createEventSubscriber(client, 'onChannelLeft', 'channel.left', filter);
4308
4471
  }
4309
- callbacks$8.push(callback);
4310
- return () => dispose$8(callback);
4472
+ callbacks$a.push(callback);
4473
+ return () => dispose$a(callback);
4311
4474
  };
4312
4475
 
4313
- const callbacks$7 = [];
4476
+ const callbacks$9 = [];
4314
4477
  let mainDisposer$7 = null;
4315
- const dispose$7 = (cb) => {
4316
- const index = callbacks$7.indexOf(cb);
4478
+ const dispose$9 = (cb) => {
4479
+ const index = callbacks$9.indexOf(cb);
4317
4480
  if (index > -1) {
4318
- callbacks$7.splice(index, 1);
4481
+ callbacks$9.splice(index, 1);
4319
4482
  }
4320
- if (callbacks$7.length === 0) {
4483
+ if (callbacks$9.length === 0) {
4321
4484
  mainDisposer$7 === null || mainDisposer$7 === void 0 ? void 0 : mainDisposer$7();
4322
4485
  }
4323
4486
  };
4324
4487
  const onChannelSetMuted = (callback) => {
4325
- if (callbacks$7.length === 0) {
4488
+ if (callbacks$9.length === 0) {
4326
4489
  const client = getActiveClient();
4327
4490
  const filter = async (payload) => {
4328
4491
  var _a;
@@ -4337,24 +4500,124 @@ const onChannelSetMuted = (callback) => {
4337
4500
  payload.channelId,
4338
4501
  ])) === null || _a === void 0 ? void 0 : _a.data;
4339
4502
  if (channel) {
4340
- callbacks$7.forEach(cb => cb(channel));
4503
+ callbacks$9.forEach(cb => cb(channel));
4341
4504
  }
4342
4505
  };
4343
4506
  mainDisposer$7 = createEventSubscriber(client, 'onChannelMute', 'channel.setMuted', filter);
4344
4507
  }
4508
+ callbacks$9.push(callback);
4509
+ return () => dispose$9(callback);
4510
+ };
4511
+
4512
+ const callbacks$8 = [];
4513
+ let mainDisposer$6 = null;
4514
+ const dispose$8 = (cb) => {
4515
+ const index = callbacks$8.indexOf(cb);
4516
+ if (index > -1) {
4517
+ callbacks$8.splice(index, 1);
4518
+ }
4519
+ if (callbacks$8.length === 0) {
4520
+ mainDisposer$6 === null || mainDisposer$6 === void 0 ? void 0 : mainDisposer$6();
4521
+ }
4522
+ };
4523
+ /* begin_public_function
4524
+ id: channel.event.message_pinned
4525
+ */
4526
+ /**
4527
+ * ```js
4528
+ * import { onChannelMessagePinned } from '@amityco/ts-sdk'
4529
+ * const dispose = onChannelMessagePinned(channel => {
4530
+ * // channel.pinnedMessage is the new pin
4531
+ * })
4532
+ * ```
4533
+ *
4534
+ * Fired when a message is pinned in a channel (the `channel.messagePinned` realtime
4535
+ * event). Pinning a different message while one is already pinned emits a single
4536
+ * `messagePinned` carrying the replacement — treat every payload as a full snapshot.
4537
+ *
4538
+ * The updated channel is written to cache before the callback runs, so
4539
+ * `ChannelRepository.getChannel` observers see `channel.pinnedMessage` change.
4540
+ *
4541
+ * @param callback The function to call when the event was fired
4542
+ * @returns an {@link Amity.Unsubscriber} function to run when willing to stop listening
4543
+ *
4544
+ * @category Channel Events
4545
+ */
4546
+ const onChannelMessagePinned = (callback) => {
4547
+ if (callbacks$8.length === 0) {
4548
+ const client = getActiveClient();
4549
+ const filter = (payload) => {
4550
+ var _a;
4551
+ const channel = findCachedChannelForPin(payload);
4552
+ if (!channel)
4553
+ return;
4554
+ const updated = updatePinnedMessageCache(channel, (_a = payload.pinnedMessage) !== null && _a !== void 0 ? _a : null, payload.users);
4555
+ callbacks$8.forEach(cb => cb(updated));
4556
+ };
4557
+ mainDisposer$6 = createEventSubscriber(client, 'onChannelMessagePinned', 'channel.messagePinned', filter);
4558
+ }
4559
+ callbacks$8.push(callback);
4560
+ return () => dispose$8(callback);
4561
+ };
4562
+ /* end_public_function */
4563
+
4564
+ const callbacks$7 = [];
4565
+ let mainDisposer$5 = null;
4566
+ const dispose$7 = (cb) => {
4567
+ const index = callbacks$7.indexOf(cb);
4568
+ if (index > -1) {
4569
+ callbacks$7.splice(index, 1);
4570
+ }
4571
+ if (callbacks$7.length === 0) {
4572
+ mainDisposer$5 === null || mainDisposer$5 === void 0 ? void 0 : mainDisposer$5();
4573
+ }
4574
+ };
4575
+ /* begin_public_function
4576
+ id: channel.event.message_unpinned
4577
+ */
4578
+ /**
4579
+ * ```js
4580
+ * import { onChannelMessageUnpinned } from '@amityco/ts-sdk'
4581
+ * const dispose = onChannelMessageUnpinned(channel => {
4582
+ * // channel.pinnedMessage is now null
4583
+ * })
4584
+ * ```
4585
+ *
4586
+ * Fired when a channel's pinned message is removed (the `channel.messageUnpinned`
4587
+ * realtime event) — either by an explicit unpin or automatically by the server when the
4588
+ * pinned message is deleted or its author is banned from the channel / globally.
4589
+ *
4590
+ * @param callback The function to call when the event was fired
4591
+ * @returns an {@link Amity.Unsubscriber} function to run when willing to stop listening
4592
+ *
4593
+ * @category Channel Events
4594
+ */
4595
+ const onChannelMessageUnpinned = (callback) => {
4596
+ if (callbacks$7.length === 0) {
4597
+ const client = getActiveClient();
4598
+ const filter = (payload) => {
4599
+ const channel = findCachedChannelForPin(payload);
4600
+ if (!channel)
4601
+ return;
4602
+ const updated = updatePinnedMessageCache(channel, null);
4603
+ callbacks$7.forEach(cb => cb(updated));
4604
+ };
4605
+ mainDisposer$5 = createEventSubscriber(client, 'onChannelMessageUnpinned', 'channel.messageUnpinned', filter);
4606
+ }
4345
4607
  callbacks$7.push(callback);
4346
4608
  return () => dispose$7(callback);
4347
4609
  };
4610
+ /* end_public_function */
4348
4611
 
4349
4612
  const callbacks$6 = [];
4350
- let mainDisposer$6 = null;
4613
+ let mainDisposer$4 = null;
4351
4614
  const dispose$6 = (cb) => {
4352
4615
  const index = callbacks$6.indexOf(cb);
4353
4616
  if (index > -1) {
4354
4617
  callbacks$6.splice(index, 1);
4355
4618
  }
4356
4619
  if (callbacks$6.length === 0) {
4357
- mainDisposer$6 === null || mainDisposer$6 === void 0 ? void 0 : mainDisposer$6();
4620
+ mainDisposer$4 === null || mainDisposer$4 === void 0 ? void 0 : mainDisposer$4();
4358
4621
  }
4359
4622
  };
4360
4623
  const onChannelMemberAdded = (callback) => {
@@ -4366,21 +4629,21 @@ const onChannelMemberAdded = (callback) => {
4366
4629
  ingestInCache(preparedPayload);
4367
4630
  callbacks$6.forEach(cb => cb(channels[0], channelUsers.find(cu => cu.membership === 'member')));
4368
4631
  };
4369
- mainDisposer$6 = createEventSubscriber(client, 'onChannelMemberAdded', 'channel.membersAdded', filter);
4632
+ mainDisposer$4 = createEventSubscriber(client, 'onChannelMemberAdded', 'channel.membersAdded', filter);
4370
4633
  }
4371
4634
  callbacks$6.push(callback);
4372
4635
  return () => dispose$6(callback);
4373
4636
  };
4374
4637
 
4375
4638
  const callbacks$5 = [];
4376
- let mainDisposer$5 = null;
4639
+ let mainDisposer$3 = null;
4377
4640
  const dispose$5 = (cb) => {
4378
4641
  const index = callbacks$5.indexOf(cb);
4379
4642
  if (index > -1) {
4380
4643
  callbacks$5.splice(index, 1);
4381
4644
  }
4382
4645
  if (callbacks$5.length === 0) {
4383
- mainDisposer$5 === null || mainDisposer$5 === void 0 ? void 0 : mainDisposer$5();
4646
+ mainDisposer$3 === null || mainDisposer$3 === void 0 ? void 0 : mainDisposer$3();
4384
4647
  }
4385
4648
  };
4386
4649
  const onChannelMemberRemoved = (callback) => {
@@ -4392,21 +4655,21 @@ const onChannelMemberRemoved = (callback) => {
4392
4655
  ingestInCache(preparedPayload);
4393
4656
  callbacks$5.forEach(cb => cb(channels[0], channelUsers[0]));
4394
4657
  };
4395
- mainDisposer$5 = createEventSubscriber(client, 'onChannelMemberRemoved', 'channel.membersRemoved', filter);
4658
+ mainDisposer$3 = createEventSubscriber(client, 'onChannelMemberRemoved', 'channel.membersRemoved', filter);
4396
4659
  }
4397
4660
  callbacks$5.push(callback);
4398
4661
  return () => dispose$5(callback);
4399
4662
  };
4400
4663
 
4401
4664
  const callbacks$4 = [];
4402
- let mainDisposer$4 = null;
4665
+ let mainDisposer$2 = null;
4403
4666
  const dispose$4 = (cb) => {
4404
4667
  const index = callbacks$4.indexOf(cb);
4405
4668
  if (index > -1) {
4406
4669
  callbacks$4.splice(index, 1);
4407
4670
  }
4408
4671
  if (callbacks$4.length === 0) {
4409
- mainDisposer$4 === null || mainDisposer$4 === void 0 ? void 0 : mainDisposer$4();
4672
+ mainDisposer$2 === null || mainDisposer$2 === void 0 ? void 0 : mainDisposer$2();
4410
4673
  }
4411
4674
  };
4412
4675
  const onChannelMemberBanned = (callback) => {
@@ -4426,20 +4689,20 @@ const onChannelMemberBanned = (callback) => {
4426
4689
  ingestInCache(preparedPayload);
4427
4690
  callbacks$4.forEach(cb => cb(channels[0], channelUsers.find(cu => cu.membership === 'banned')));
4428
4691
  };
4429
- mainDisposer$4 = createEventSubscriber(client, 'onChannelMemberBanned', 'channel.banned', filter);
4692
+ mainDisposer$2 = createEventSubscriber(client, 'onChannelMemberBanned', 'channel.banned', filter);
4430
4693
  callbacks$4.push(callback);
4431
4694
  return () => dispose$4(callback);
4432
4695
  };
4433
4696
 
4434
4697
  const callbacks$3 = [];
4435
- let mainDisposer$3 = null;
4698
+ let mainDisposer$1 = null;
4436
4699
  const dispose$3 = (cb) => {
4437
4700
  const index = callbacks$3.indexOf(cb);
4438
4701
  if (index > -1) {
4439
4702
  callbacks$3.splice(index, 1);
4440
4703
  }
4441
4704
  if (callbacks$3.length === 0) {
4442
- mainDisposer$3 === null || mainDisposer$3 === void 0 ? void 0 : mainDisposer$3();
4705
+ mainDisposer$1 === null || mainDisposer$1 === void 0 ? void 0 : mainDisposer$1();
4443
4706
  }
4444
4707
  };
4445
4708
  const onChannelMemberUnbanned = (callback) => {
@@ -4451,59 +4714,127 @@ const onChannelMemberUnbanned = (callback) => {
4451
4714
  ingestInCache(preparedPayload);
4452
4715
  callbacks$3.forEach(cb => cb(channels[0], channelUsers.find(cu => cu.membership === 'none')));
4453
4716
  };
4454
- mainDisposer$3 = createEventSubscriber(client, 'onChannelMemberUnbanned', 'channel.unbanned', filter);
4717
+ mainDisposer$1 = createEventSubscriber(client, 'onChannelMemberUnbanned', 'channel.unbanned', filter);
4455
4718
  }
4456
4719
  callbacks$3.push(callback);
4457
4720
  return () => dispose$3(callback);
4458
4721
  };
4459
4722
 
4460
4723
  const callbacks$2 = [];
4461
- let mainDisposer$2 = null;
4724
+ const mainDisposers$1 = [];
4462
4725
  const dispose$2 = (cb) => {
4463
4726
  const index = callbacks$2.indexOf(cb);
4464
4727
  if (index > -1) {
4465
4728
  callbacks$2.splice(index, 1);
4466
4729
  }
4467
4730
  if (callbacks$2.length === 0) {
4468
- mainDisposer$2 === null || mainDisposer$2 === void 0 ? void 0 : mainDisposer$2();
4731
+ mainDisposers$1.forEach(fn => fn());
4732
+ mainDisposers$1.length = 0;
4469
4733
  }
4470
4734
  };
4735
+ /* begin_public_function
4736
+ id: channel.event.member_role_added
4737
+ */
4738
+ /**
4739
+ * ```js
4740
+ * import { onChannelMemberRoleAdded } from '@amityco/ts-sdk'
4741
+ * const dispose = onChannelMemberRoleAdded((channel, member) => {
4742
+ * // member.roles and member.permissions are the member's resulting state
4743
+ * })
4744
+ * ```
4745
+ *
4746
+ * Fired when a role is added to a member of a {@link Amity.Channel}, either by the
4747
+ * local `ChannelRepository.Moderation.addRole` call or by the `channel.roleAdded` realtime
4748
+ * event coming from any client or the admin console.
4749
+ *
4750
+ * The realtime event reaches every subscriber of the channel topic (see `getChannelTopic`),
4751
+ * not only the actor or the affected members. It carries the affected members' resulting
4752
+ * channel roles and channel-scoped permissions, which are written to cache before the
4753
+ * callback runs. The callback is invoked once per affected member already held in cache.
4754
+ *
4755
+ * @param callback The function to call when the event was fired
4756
+ * @returns an {@link Amity.Unsubscriber} function to run when willing to stop listening
4757
+ *
4758
+ * @category Channel Events
4759
+ */
4471
4760
  const onChannelMemberRoleAdded = (callback) => {
4472
4761
  if (callbacks$2.length === 0) {
4473
4762
  const client = getActiveClient();
4474
- const filter = async (payload) => {
4763
+ const onLocal = async (payload) => {
4475
4764
  const { channels, channelUsers } = payload;
4476
4765
  callbacks$2.forEach(cb => cb(channels[0], channelUsers.find(channelUser => channelUser.membership === 'member')));
4477
4766
  };
4478
- mainDisposer$2 = createEventSubscriber(client, 'onChannelMemberRoleAdded', 'local.channel-moderator.role-added', filter);
4767
+ const onRealtime = (payload) => {
4768
+ const { channel, members } = applyChannelRolePayload(payload);
4769
+ if (!channel)
4770
+ return;
4771
+ members.forEach(member => callbacks$2.forEach(cb => cb(channel, member)));
4772
+ };
4773
+ mainDisposers$1.push(createEventSubscriber(client, 'onChannelMemberRoleAdded', 'local.channel-moderator.role-added', onLocal));
4774
+ mainDisposers$1.push(createEventSubscriber(client, 'onChannelMemberRoleAdded', 'channel.roleAdded', onRealtime));
4479
4775
  }
4480
4776
  callbacks$2.push(callback);
4481
4777
  return () => dispose$2(callback);
4482
4778
  };
4779
+ /* end_public_function */
4483
4780
 
4484
4781
  const callbacks$1 = [];
4485
- let mainDisposer$1 = null;
4782
+ const mainDisposers = [];
4486
4783
  const dispose$1 = (cb) => {
4487
4784
  const index = callbacks$1.indexOf(cb);
4488
4785
  if (index > -1) {
4489
4786
  callbacks$1.splice(index, 1);
4490
4787
  }
4491
4788
  if (callbacks$1.length === 0) {
4492
- mainDisposer$1 === null || mainDisposer$1 === void 0 ? void 0 : mainDisposer$1();
4789
+ mainDisposers.forEach(fn => fn());
4790
+ mainDisposers.length = 0;
4493
4791
  }
4494
4792
  };
4793
+ /* begin_public_function
4794
+ id: channel.event.member_role_removed
4795
+ */
4796
+ /**
4797
+ * ```js
4798
+ * import { onChannelMemberRoleRemoved } from '@amityco/ts-sdk'
4799
+ * const dispose = onChannelMemberRoleRemoved((channel, member) => {
4800
+ * // member.roles and member.permissions are the member's resulting state
4801
+ * })
4802
+ * ```
4803
+ *
4804
+ * Fired when a role is removed from a member of a {@link Amity.Channel}, either by the
4805
+ * local `ChannelRepository.Moderation.removeRole` call or by the `channel.roleRemoved` realtime
4806
+ * event coming from any client or the admin console.
4807
+ *
4808
+ * The realtime event reaches every subscriber of the channel topic (see `getChannelTopic`),
4809
+ * not only the actor or the affected members. It carries the affected members' resulting
4810
+ * channel roles and channel-scoped permissions, which are written to cache before the
4811
+ * callback runs. The callback is invoked once per affected member already held in cache.
4812
+ *
4813
+ * @param callback The function to call when the event was fired
4814
+ * @returns an {@link Amity.Unsubscriber} function to run when willing to stop listening
4815
+ *
4816
+ * @category Channel Events
4817
+ */
4495
4818
  const onChannelMemberRoleRemoved = (callback) => {
4496
4819
  if (callbacks$1.length === 0) {
4497
4820
  const client = getActiveClient();
4498
- const filter = async (payload) => {
4821
+ const onLocal = async (payload) => {
4499
4822
  const { channels, channelUsers } = payload;
4500
4823
  callbacks$1.forEach(cb => cb(channels[0], channelUsers.find(channelUser => channelUser.membership === 'member')));
4501
4824
  };
4502
- mainDisposer$1 = createEventSubscriber(client, 'onChannelMemberRoleRemoved', 'local.channel-moderator.role-removed', filter);
4825
+ const onRealtime = (payload) => {
4826
+ const { channel, members } = applyChannelRolePayload(payload);
4827
+ if (!channel)
4828
+ return;
4829
+ members.forEach(member => callbacks$1.forEach(cb => cb(channel, member)));
4830
+ };
4831
+ mainDisposers.push(createEventSubscriber(client, 'onChannelMemberRoleRemoved', 'local.channel-moderator.role-removed', onLocal));
4832
+ mainDisposers.push(createEventSubscriber(client, 'onChannelMemberRoleRemoved', 'channel.roleRemoved', onRealtime));
4503
4833
  }
4504
4834
  callbacks$1.push(callback);
4505
4835
  return () => dispose$1(callback);
4506
4836
  };
4837
+ /* end_public_function */
4507
4838
 
4508
4839
  /**
4509
4840
  * Fired when a {@link Amity.Channel} has been archived by the active user.
@@ -4627,6 +4958,8 @@ const getMyMembership = (channelId, callback) => {
4627
4958
  disposers.push(onChannelMemberBanned(realtimeRouter));
4628
4959
  disposers.push(onChannelMemberUnbanned(realtimeRouter));
4629
4960
  disposers.push(onChannelSetUserMuted(realtimeRouter));
4961
+ disposers.push(onChannelMemberRoleAdded(realtimeRouter));
4962
+ disposers.push(onChannelMemberRoleRemoved(realtimeRouter));
4630
4963
  onFetch();
4631
4964
  return () => {
4632
4965
  disposers.forEach(fn => fn());
@@ -5020,7 +5353,9 @@ const initializeMessagePreviewSetting = async () => {
5020
5353
  const getSocialSettings = async () => {
5021
5354
  const client = getActiveClient();
5022
5355
  const { data } = await client.http.get(`/api/v3/network-settings/social`);
5023
- return data.socialNetworkSetting;
5356
+ return Object.assign(Object.assign({}, data.socialNetworkSetting), {
5357
+ // Networks that never set the comment edit switch allow the edit
5358
+ isAllowEditCommentWhenReviewingEnabled: data.socialNetworkSetting.isAllowEditCommentWhenReviewingEnabled });
5024
5359
  };
5025
5360
 
5026
5361
  /**
@@ -5706,6 +6041,32 @@ class AnalyticsEventCapturer {
5706
6041
  activityType: "view" /* Amity.AnalyticEventActivityType.View */,
5707
6042
  });
5708
6043
  }
6044
+ markCuratedContentWidgetAsViewed(topicId) {
6045
+ this.markAs({
6046
+ uniqueId: `curatedContentWidget.${topicId}`,
6047
+ contentId: topicId,
6048
+ contentType: "curated_content_widget" /* Amity.AnalyticEventContentType.CuratedContentWidget */,
6049
+ activityType: "view" /* Amity.AnalyticEventActivityType.View */,
6050
+ });
6051
+ }
6052
+ markCuratedContentPostAsViewed(postId, topicId) {
6053
+ this.markAs({
6054
+ uniqueId: `curatedContentPost.${postId}.view`,
6055
+ contentId: postId,
6056
+ contentType: "curated_content_post" /* Amity.AnalyticEventContentType.CuratedContentPost */,
6057
+ activityType: "view" /* Amity.AnalyticEventActivityType.View */,
6058
+ metadata: { topicId },
6059
+ });
6060
+ }
6061
+ markCuratedContentPostAsClicked(postId, topicId) {
6062
+ this.markAs({
6063
+ uniqueId: `curatedContentPost.${postId}.click`,
6064
+ contentId: postId,
6065
+ contentType: "curated_content_post" /* Amity.AnalyticEventContentType.CuratedContentPost */,
6066
+ activityType: "linkClicked" /* Amity.AnalyticEventActivityType.Click */,
6067
+ metadata: { topicId },
6068
+ });
6069
+ }
5709
6070
  markPostAsMeaningfullyViewed(postId, feedRenderPosition) {
5710
6071
  var _a;
5711
6072
  const cached = (_a = pullFromCache(['feedMetadata', 'get', postId])) === null || _a === void 0 ? void 0 : _a.data;
@@ -5869,6 +6230,24 @@ class AnalyticsEngine {
5869
6230
  this._eventCapturer.markPostAsViewed(postId);
5870
6231
  }
5871
6232
  }
6233
+ markCuratedContentWidgetAsViewed(topicId) {
6234
+ if (this._client.sessionState === "established" /* Amity.SessionStates.ESTABLISHED */ ||
6235
+ this._client.sessionState === "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */) {
6236
+ this._eventCapturer.markCuratedContentWidgetAsViewed(topicId);
6237
+ }
6238
+ }
6239
+ markCuratedContentPostAsViewed(postId, topicId) {
6240
+ if (this._client.sessionState === "established" /* Amity.SessionStates.ESTABLISHED */ ||
6241
+ this._client.sessionState === "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */) {
6242
+ this._eventCapturer.markCuratedContentPostAsViewed(postId, topicId);
6243
+ }
6244
+ }
6245
+ markCuratedContentPostAsClicked(postId, topicId) {
6246
+ if (this._client.sessionState === "established" /* Amity.SessionStates.ESTABLISHED */ ||
6247
+ this._client.sessionState === "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */) {
6248
+ this._eventCapturer.markCuratedContentPostAsClicked(postId, topicId);
6249
+ }
6250
+ }
5872
6251
  markPostAsMeaningfullyViewed(postId, feedRenderPosition) {
5873
6252
  if (this._client.sessionState === "established" /* Amity.SessionStates.ESTABLISHED */ ||
5874
6253
  this._client.sessionState === "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */) {
@@ -7265,6 +7644,265 @@ class CursorController {
7265
7644
  }
7266
7645
  }
7267
7646
 
7647
+ class ResetTask {
7648
+ constructor(postId, latestCreatedAt, serverCommentCount) {
7649
+ this.postId = postId;
7650
+ this.latestCreatedAt = latestCreatedAt;
7651
+ this.serverCommentCount = serverCommentCount;
7652
+ }
7653
+ }
7654
+
7655
+ // Task to track comment creation
7656
+ class CreateTask {
7657
+ constructor(postId, commentId, createdAt) {
7658
+ this.postId = postId;
7659
+ this.commentId = commentId;
7660
+ this.createdAt = createdAt;
7661
+ }
7662
+ }
7663
+
7664
+ /**
7665
+ * A pending comment was approved and now counts publicly. Unlike `CreateTask` it carries no
7666
+ * `createdAt` and bypasses the age guard on purpose: the comment was written before the last post
7667
+ * payload (so its `createdAt` is old) but entered the server count only now. Same rule as iOS.
7668
+ *
7669
+ * Accepted trade-off (plan 40 §9): if a `ResetTask` that already includes this comment lands
7670
+ * between the approve response and the late `comment.approved` event, the count reads +1 until
7671
+ * the next server sync — the same self-correcting drift accepted for stale deletes.
7672
+ */
7673
+ class ApproveTask {
7674
+ constructor(postId, commentId) {
7675
+ this.postId = postId;
7676
+ this.commentId = commentId;
7677
+ }
7678
+ }
7679
+
7680
+ // Task to track comment deletion
7681
+ class DeleteTask {
7682
+ constructor(postId, commentId) {
7683
+ this.postId = postId;
7684
+ this.commentId = commentId;
7685
+ }
7686
+ }
7687
+
7688
+ class CommentChange {
7689
+ constructor(latestCreatedAt, serverCommentCount) {
7690
+ this.latestCommentCreatedAt = latestCreatedAt;
7691
+ this.serverCommentCount = serverCommentCount;
7692
+ this.createdCommentIds = new Set();
7693
+ this.deletedCommentIds = new Set();
7694
+ }
7695
+ }
7696
+
7697
+ class PostCommentCountEngine {
7698
+ constructor() {
7699
+ this.isProcessing = false;
7700
+ this.tasks = [];
7701
+ this.commentChangeTracker = new Map();
7702
+ // Ids of the LOGGED-IN USER's own pending comments, per post. Survives ResetTask, so the
7703
+ // author's +1 does not vanish on the next post refresh; emptied only when the comment leaves
7704
+ // the pending state (approval hands it to createdCommentIds, decline/delete drops it).
7705
+ this.pendingAuthoredCommentIds = new Map();
7706
+ }
7707
+ queueCommentChangeTask(task) {
7708
+ this.tasks.push(task);
7709
+ if (!this.isProcessing) {
7710
+ this.processCommentChangeTask();
7711
+ }
7712
+ }
7713
+ addPendingAuthoredComment(postId, commentId, { publish = true } = {}) {
7714
+ var _a;
7715
+ const ids = (_a = this.pendingAuthoredCommentIds.get(postId)) !== null && _a !== void 0 ? _a : new Set();
7716
+ if (ids.has(commentId))
7717
+ return;
7718
+ ids.add(commentId);
7719
+ this.pendingAuthoredCommentIds.set(postId, ids);
7720
+ if (publish)
7721
+ this.publishIfTracked(postId);
7722
+ }
7723
+ removePendingAuthoredComment(postId, commentId, { publish = true } = {}) {
7724
+ const ids = this.pendingAuthoredCommentIds.get(postId);
7725
+ if (!(ids === null || ids === void 0 ? void 0 : ids.delete(commentId)))
7726
+ return false;
7727
+ if (ids.size === 0)
7728
+ this.pendingAuthoredCommentIds.delete(postId);
7729
+ if (publish)
7730
+ this.publishIfTracked(postId);
7731
+ return true;
7732
+ }
7733
+ /** Whether a post payload has seeded this post; without one every count is `0`. */
7734
+ hasTracker(postId) {
7735
+ return this.commentChangeTracker.has(postId);
7736
+ }
7737
+ isPendingAuthoredComment(postId, commentId) {
7738
+ var _a, _b;
7739
+ return (_b = (_a = this.pendingAuthoredCommentIds.get(postId)) === null || _a === void 0 ? void 0 : _a.has(commentId)) !== null && _b !== void 0 ? _b : false;
7740
+ }
7741
+ // Drops every per-post state. Called on logout / user switch.
7742
+ reset() {
7743
+ this.tasks = [];
7744
+ this.commentChangeTracker.clear();
7745
+ this.pendingAuthoredCommentIds.clear();
7746
+ }
7747
+ publishIfTracked(postId) {
7748
+ // Without a tracker there is no server figure to add to; the next ResetTask picks it up.
7749
+ if (!this.commentChangeTracker.has(postId))
7750
+ return;
7751
+ PostCommentCountEngine.publishUpdate(postId, this.getLocalCommentCount(postId, true));
7752
+ }
7753
+ processCommentChangeTask() {
7754
+ if (this.isProcessing) {
7755
+ return;
7756
+ }
7757
+ this.isProcessing = true;
7758
+ if (this.tasks.length === 0) {
7759
+ this.isProcessing = false;
7760
+ return;
7761
+ }
7762
+ // Process in capped batches, coalescing updates
7763
+ const batch = this.tasks.splice(0, PostCommentCountEngine.BATCH_SIZE);
7764
+ const modifiedPostIds = new Set();
7765
+ batch.forEach(task => {
7766
+ let modified = false;
7767
+ if (task instanceof ResetTask) {
7768
+ modified = this.processResetTaskInternal(task);
7769
+ }
7770
+ else if (task instanceof CreateTask) {
7771
+ modified = this.processCreateTaskInternal(task);
7772
+ }
7773
+ else if (task instanceof ApproveTask) {
7774
+ modified = this.processApproveTaskInternal(task);
7775
+ }
7776
+ else if (task instanceof DeleteTask) {
7777
+ modified = this.processDeleteTaskInternal(task);
7778
+ }
7779
+ if (modified) {
7780
+ modifiedPostIds.add(task.postId);
7781
+ }
7782
+ });
7783
+ // Publish one update per modified post
7784
+ modifiedPostIds.forEach(postId => {
7785
+ PostCommentCountEngine.publishUpdate(postId, this.getLocalCommentCount(postId, true));
7786
+ });
7787
+ this.isProcessing = false;
7788
+ // Recurse if more tasks remain
7789
+ if (this.tasks.length > 0) {
7790
+ this.processCommentChangeTask();
7791
+ }
7792
+ }
7793
+ processResetTaskInternal(task) {
7794
+ // Always creates/overwrites tracker
7795
+ this.commentChangeTracker.set(task.postId, new CommentChange(task.latestCreatedAt, task.serverCommentCount));
7796
+ return true;
7797
+ }
7798
+ processCreateTaskInternal(task) {
7799
+ // The comment is public now: the author-local +1 hands over to the normal path, so the
7800
+ // count stays put instead of moving twice.
7801
+ const wasPendingAuthored = this.removePendingAuthoredComment(task.postId, task.commentId, {
7802
+ publish: false,
7803
+ });
7804
+ const tracker = this.commentChangeTracker.get(task.postId);
7805
+ if (!tracker)
7806
+ return false; // No tracker, skip
7807
+ if (tracker.deletedCommentIds.delete(task.commentId))
7808
+ return true; // Re-approval cancels the earlier removal
7809
+ if (tracker.createdCommentIds.has(task.commentId))
7810
+ return wasPendingAuthored; // Deduplication
7811
+ if (task.createdAt <= tracker.latestCommentCreatedAt)
7812
+ return wasPendingAuthored; // Timestamp filtering
7813
+ tracker.createdCommentIds.add(task.commentId);
7814
+ return true;
7815
+ }
7816
+ processApproveTaskInternal(task) {
7817
+ // The comment is public now: the author-local +1 hands over to the normal path, so the
7818
+ // count stays put instead of moving twice.
7819
+ const wasPendingAuthored = this.removePendingAuthoredComment(task.postId, task.commentId, {
7820
+ publish: false,
7821
+ });
7822
+ const tracker = this.commentChangeTracker.get(task.postId);
7823
+ if (!tracker)
7824
+ return false; // No tracker, skip
7825
+ if (tracker.deletedCommentIds.delete(task.commentId))
7826
+ return true; // Re-approval cancels the earlier re-review removal
7827
+ if (tracker.createdCommentIds.has(task.commentId))
7828
+ return wasPendingAuthored; // Approve response and comment.approved event dedupe by id
7829
+ // No timestamp filtering: approval is the moment the comment enters the count, not createdAt
7830
+ tracker.createdCommentIds.add(task.commentId);
7831
+ return true;
7832
+ }
7833
+ processDeleteTaskInternal(task) {
7834
+ const tracker = this.commentChangeTracker.get(task.postId);
7835
+ if (!tracker)
7836
+ return false; // No tracker, skip
7837
+ if (tracker.deletedCommentIds.has(task.commentId))
7838
+ return false; // Deduplication
7839
+ tracker.deletedCommentIds.add(task.commentId);
7840
+ return true;
7841
+ }
7842
+ /**
7843
+ * The count as this session sees it: the last server figure plus the local delta.
7844
+ *
7845
+ * `includePending` adds the logged-in user's own pending comments — the author's view. A
7846
+ * pending id is added only while the public delta does not already count it, so a comment
7847
+ * present in both sets counts once: a `comment.created` that arrives pending after the
7848
+ * `comment.approved` hand-over adds nothing, while an approved comment sent back to review
7849
+ * (created, deleted and pending all hold its id) keeps the author's +1.
7850
+ *
7851
+ * `0` for a post no payload has seeded yet; callers fall back to the post's own figure.
7852
+ */
7853
+ getLocalCommentCount(postId, includePending = false) {
7854
+ var _a;
7855
+ const tracker = this.commentChangeTracker.get(postId);
7856
+ if (!tracker)
7857
+ return 0;
7858
+ const publicDelta = tracker.createdCommentIds.size - tracker.deletedCommentIds.size;
7859
+ if (!includePending)
7860
+ return Math.max(0, tracker.serverCommentCount + publicDelta);
7861
+ let pendingExtra = 0;
7862
+ (_a = this.pendingAuthoredCommentIds.get(postId)) === null || _a === void 0 ? void 0 : _a.forEach(commentId => {
7863
+ const countedPublicly = tracker.createdCommentIds.has(commentId) && !tracker.deletedCommentIds.has(commentId);
7864
+ if (!countedPublicly)
7865
+ pendingExtra += 1;
7866
+ });
7867
+ return Math.max(0, tracker.serverCommentCount + publicDelta + pendingExtra);
7868
+ }
7869
+ static publishUpdate(postId, newCount) {
7870
+ var _a;
7871
+ const queryKey = ['post', 'get', postId];
7872
+ mergeInCache(queryKey, {
7873
+ localCommentCount: newCount,
7874
+ });
7875
+ const postPayload = (_a = pullFromCache(queryKey)) === null || _a === void 0 ? void 0 : _a.data;
7876
+ if (!postPayload)
7877
+ return;
7878
+ fireEvent('local.post.updated', {
7879
+ posts: [postPayload],
7880
+ });
7881
+ }
7882
+ }
7883
+ PostCommentCountEngine.BATCH_SIZE = 50;
7884
+ let instance$1;
7885
+ var PostCommentCountEngine$1 = {
7886
+ getInstance: () => {
7887
+ if (!instance$1) {
7888
+ instance$1 = new PostCommentCountEngine();
7889
+ }
7890
+ return instance$1;
7891
+ },
7892
+ };
7893
+
7894
+ /**
7895
+ * Comment-count state is per user — in particular the ids of the logged-in user's own pending
7896
+ * comments — so it must not survive a logout or a user switch.
7897
+ *
7898
+ * @category private
7899
+ */
7900
+ const postCommentCountEngineOnLoginHandler = () => onSessionStateChange(state => {
7901
+ if (state === "notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */ || state === "terminated" /* Amity.SessionStates.TERMINATED */) {
7902
+ PostCommentCountEngine$1.getInstance().reset();
7903
+ }
7904
+ });
7905
+
7268
7906
  const EVENTS = [
7269
7907
  'disconnected',
7270
7908
  'error',
@@ -7454,7 +8092,7 @@ const setupLoginSubscriptions = (unsubWatcher) => {
7454
8092
  // NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
7455
8093
  // the channel because currently backend can't handle this, so every time a user is banned from
7456
8094
  // a channel or the channel is deleted the channel's unread count will not be reset to zero
7457
- onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler(), reactionSyncEngineOnLoginHandler(), blockedUserSyncEngineOnLoginHandler(), blockingUserSyncEngineOnLoginHandler(), blockRealtimeOnLoginHandler(), resetAutoSubscriptionsOnLogout(), CursorController.clearOnLogout());
8095
+ onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler(), reactionSyncEngineOnLoginHandler(), blockedUserSyncEngineOnLoginHandler(), blockingUserSyncEngineOnLoginHandler(), blockRealtimeOnLoginHandler(), resetAutoSubscriptionsOnLogout(), CursorController.clearOnLogout(), postCommentCountEngineOnLoginHandler());
7458
8096
  if (client.useLegacyUnreadCount) {
7459
8097
  subscriptions.push(readReceiptSyncEngineOnLoginHandler());
7460
8098
  }
@@ -8490,7 +9128,7 @@ const DEFAULT_DEBUG_SESSION = 'amity';
8490
9128
  *
8491
9129
  * @category Client API
8492
9130
  * */
8493
- const createClient = (apiKey, apiRegion = API_REGIONS.SG, { debugSession = DEFAULT_DEBUG_SESSION, apiEndpoint, prefixDeviceIdKey, rteEnabled = true, } = {}) => {
9131
+ const createClient = (apiKey, apiRegion = API_REGIONS.SG, { debugSession = DEFAULT_DEBUG_SESSION, apiEndpoint, prefixDeviceIdKey, rteEnabled = true, product, uikitVersion, appId, appVersion, } = {}) => {
8494
9132
  var _a, _b, _c;
8495
9133
  const log = createLogger(debugSession);
8496
9134
  log('client/api/createClient', {
@@ -8519,6 +9157,10 @@ const createClient = (apiKey, apiRegion = API_REGIONS.SG, { debugSession = DEFAU
8519
9157
  const useLegacyUnreadCount = true;
8520
9158
  const client = {
8521
9159
  version: `${VERSION}`,
9160
+ product,
9161
+ uikitVersion,
9162
+ appId,
9163
+ appVersion,
8522
9164
  apiKey,
8523
9165
  /*
8524
9166
  * SDK Components
@@ -8757,13 +9399,13 @@ class GlobalFileAccessType {
8757
9399
  }
8758
9400
  }
8759
9401
  _GlobalFileAccessType_fileAccessType = new WeakMap();
8760
- let instance$1;
9402
+ let instance;
8761
9403
  var GlobalFileAccessType$1 = {
8762
9404
  getInstance: () => {
8763
- if (!instance$1) {
8764
- instance$1 = new GlobalFileAccessType();
9405
+ if (!instance) {
9406
+ instance = new GlobalFileAccessType();
8765
9407
  }
8766
- return instance$1;
9408
+ return instance;
8767
9409
  },
8768
9410
  };
8769
9411
 
@@ -9600,7 +10242,7 @@ const getUserUnread = (callback) => {
9600
10242
  };
9601
10243
  };
9602
10244
 
9603
- var index$s = /*#__PURE__*/Object.freeze({
10245
+ var index$t = /*#__PURE__*/Object.freeze({
9604
10246
  __proto__: null,
9605
10247
  getActiveClient: getActiveClient,
9606
10248
  getActiveUser: getActiveUser,
@@ -10826,7 +11468,7 @@ const getMyFollowInfo = (callback) => {
10826
11468
  };
10827
11469
  /* end_public_function */
10828
11470
 
10829
- var index$r = /*#__PURE__*/Object.freeze({
11471
+ var index$s = /*#__PURE__*/Object.freeze({
10830
11472
  __proto__: null,
10831
11473
  blockUser: blockUser,
10832
11474
  unBlockUser: unBlockUser,
@@ -12338,8 +12980,32 @@ const streamLinkedObject = (stream) => {
12338
12980
  } });
12339
12981
  };
12340
12982
 
12983
+ const KNOWN_APPROVAL_STATUSES = [
12984
+ "pending" /* Amity.CommentApprovalStatusEnum.Pending */,
12985
+ "approved" /* Amity.CommentApprovalStatusEnum.Approved */,
12986
+ "rejected" /* Amity.CommentApprovalStatusEnum.Rejected */,
12987
+ ];
12988
+ /**
12989
+ * The server sends `approvalStatus` only while a comment is held for review or after it was
12990
+ * rejected. Absent, `null`, or an unrecognised value all resolve to `approved` — never to
12991
+ * `pending`, which would mark every comment written before the feature as unpublished.
12992
+ */
12993
+ const resolveCommentApprovalStatus = (approvalStatus) => {
12994
+ if (approvalStatus && KNOWN_APPROVAL_STATUSES.includes(approvalStatus)) {
12995
+ return approvalStatus;
12996
+ }
12997
+ return "approved" /* Amity.CommentApprovalStatusEnum.Approved */;
12998
+ };
12999
+ const prepareComment = (comment) => (Object.assign(Object.assign(Object.assign({}, (comment.hasOwnProperty('myReactions') ? {} : { myReactions: [] })), comment), { approvalStatus: resolveCommentApprovalStatus(comment.approvalStatus) }));
13000
+ function prepareCommentPayload(commentPayload) {
13001
+ const { comments, commentChildren } = commentPayload;
13002
+ return Object.assign(Object.assign(Object.assign({}, commentPayload), { comments: comments.map(prepareComment) }), (commentChildren ? { commentChildren: commentChildren.map(prepareComment) } : {}));
13003
+ }
13004
+
12341
13005
  const commentLinkedObject = (comment) => {
12342
- return Object.assign(Object.assign({}, comment), { get target() {
13006
+ return Object.assign(Object.assign({}, comment), {
13007
+ // Non-optional on the public model: absent on the wire means approved
13008
+ approvalStatus: resolveCommentApprovalStatus(comment.approvalStatus), get target() {
12343
13009
  const commentTypes = {
12344
13010
  type: comment.targetType,
12345
13011
  };
@@ -12547,7 +13213,7 @@ const getWatchSessionStorage = () => {
12547
13213
  return storageInstance;
12548
13214
  };
12549
13215
 
12550
- 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-----";
13216
+ 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=";
12551
13217
  /*
12552
13218
  * The crypto algorithm used for importing key and signing string
12553
13219
  */
@@ -12990,6 +13656,122 @@ const fileLinkedObject = (file) => {
12990
13656
  return file;
12991
13657
  };
12992
13658
 
13659
+ const isCommentPending = (comment) => resolveCommentApprovalStatus(comment.approvalStatus) === "pending" /* Amity.CommentApprovalStatusEnum.Pending */;
13660
+ const isCommentApproved = (comment) => resolveCommentApprovalStatus(comment.approvalStatus) === "approved" /* Amity.CommentApprovalStatusEnum.Approved */;
13661
+ // Only post and content references feed PostCommentCountEngine; story counts live on the story.
13662
+ const isCountedReference = (comment) => ['post', 'content'].includes(comment.referenceType);
13663
+ const isAuthoredByCurrentUser = (comment) => comment.userId === getActiveClient().userId;
13664
+ /**
13665
+ * Whether a comment on this reference would be held for review, read from the cached post's
13666
+ * community. `true` when that cannot be known — the post or its community is not cached yet.
13667
+ * A pending guess that turns out approved is corrected by the response and the comment then
13668
+ * enters the list; an approved guess that turns out pending has already been shown in a public
13669
+ * list with nothing to take it back out. Only shapes the optimistic object — the server
13670
+ * response is the authority on the actual status.
13671
+ */
13672
+ const isCommentApprovalRequired = (bundle) => {
13673
+ var _a, _b;
13674
+ // Only comments on community posts are gated; every other reference is known-off
13675
+ if (bundle.referenceType !== 'post')
13676
+ return false;
13677
+ const post = (_a = pullFromCache(['post', 'get', bundle.referenceId])) === null || _a === void 0 ? void 0 : _a.data;
13678
+ if (!post)
13679
+ return true;
13680
+ // A user-feed post is known-off
13681
+ if (post.targetType !== 'community')
13682
+ return false;
13683
+ const community = (_b = pullFromCache([
13684
+ 'community',
13685
+ 'get',
13686
+ post.targetId,
13687
+ ])) === null || _b === void 0 ? void 0 : _b.data;
13688
+ if (!community)
13689
+ return true;
13690
+ return community.needApprovalOnCommentCreation === true;
13691
+ };
13692
+ /**
13693
+ * An approved comment arrived over realtime: one `CreateTask` carrying its own `createdAt`, so
13694
+ * the timestamp filter skips anything the last post payload already counted. The author's own
13695
+ * comment reaches here as an echo too; the engine de-duplicates it by id against the task the
13696
+ * create response queued.
13697
+ */
13698
+ const applyCommentCreatedCount = (comment) => {
13699
+ if (!isCountedReference(comment))
13700
+ return;
13701
+ PostCommentCountEngine$1.getInstance().queueCommentChangeTask(new CreateTask(comment.referenceId, comment.commentId, comment.createdAt));
13702
+ };
13703
+ /**
13704
+ * The author's own pending comment counts in their view only. Recorded as separate engine state
13705
+ * so it survives a post refresh (`ResetTask`).
13706
+ */
13707
+ const recordPendingAuthoredComment = (comment, options) => {
13708
+ if (!isCountedReference(comment) || !isAuthoredByCurrentUser(comment))
13709
+ return;
13710
+ PostCommentCountEngine$1.getInstance().addPendingAuthoredComment(comment.referenceId, comment.commentId, options);
13711
+ };
13712
+ /**
13713
+ * A pending comment was declined or deleted: it was never in the server count, so no
13714
+ * `DeleteTask` — only the author-local +1 goes away.
13715
+ */
13716
+ const releasePendingAuthoredComment = (comment) => {
13717
+ if (!isCountedReference(comment))
13718
+ return;
13719
+ PostCommentCountEngine$1.getInstance().removePendingAuthoredComment(comment.referenceId, comment.commentId);
13720
+ };
13721
+ /**
13722
+ * Reveal: exactly one `ApproveTask`. The engine de-duplicates by comment id, so the approve
13723
+ * response and the `comment.approved` event may both call this. `ApproveTask` bypasses the age
13724
+ * guard on purpose — the comment was written before the last server sync but enters the public
13725
+ * count now — matching the iOS `ApproveTask` (plan 40 §9, guard off for approve).
13726
+ */
13727
+ const applyCommentApprovedCount = (comment) => {
13728
+ if (!isCountedReference(comment))
13729
+ return;
13730
+ PostCommentCountEngine$1.getInstance().queueCommentChangeTask(new ApproveTask(comment.referenceId, comment.commentId));
13731
+ };
13732
+ /**
13733
+ * Re-review: an approved comment edited back to pending leaves the public count once. The
13734
+ * author keeps their own +1 through the pending set, so their count does not move.
13735
+ */
13736
+ const applyCommentReReviewCount = (comment) => {
13737
+ if (!isCountedReference(comment))
13738
+ return;
13739
+ recordPendingAuthoredComment(comment, { publish: false });
13740
+ PostCommentCountEngine$1.getInstance().queueCommentChangeTask(new DeleteTask(comment.referenceId, comment.commentId));
13741
+ };
13742
+ /** Adds a reply to its cached parent's `children` (and count) once. */
13743
+ const linkCommentToParent = (comment) => {
13744
+ var _a;
13745
+ if (!comment.parentId)
13746
+ return;
13747
+ const parentKey = ['comment', 'get', comment.parentId];
13748
+ const parent = (_a = pullFromCache(parentKey)) === null || _a === void 0 ? void 0 : _a.data;
13749
+ if (!parent || parent.children.includes(comment.commentId))
13750
+ return;
13751
+ pushToCache(parentKey, Object.assign(Object.assign({}, parent), { childrenNumber: parent.childrenNumber + 1, children: [...new Set([...parent.children, comment.commentId])] }));
13752
+ };
13753
+ /** Removes a reply from its cached parent's `children` (and count) once. */
13754
+ const unlinkCommentFromParent = (comment) => {
13755
+ var _a;
13756
+ if (!comment.parentId)
13757
+ return;
13758
+ const parentKey = ['comment', 'get', comment.parentId];
13759
+ const parent = (_a = pullFromCache(parentKey)) === null || _a === void 0 ? void 0 : _a.data;
13760
+ if (!parent || !parent.children.includes(comment.commentId))
13761
+ return;
13762
+ pushToCache(parentKey, Object.assign(Object.assign({}, parent), { childrenNumber: parent.childrenNumber - 1, children: parent.children.filter(id => id !== comment.commentId) }));
13763
+ };
13764
+
13765
+ /**
13766
+ * The server's list is oldest → newest, so the newest ids are the last ones. Kept unique so the
13767
+ * create response, the local event and the realtime echo cannot each add the same comment.
13768
+ */
13769
+ const capLatestCommentIds = (commentIds) => [...new Set(commentIds)].slice(-LATEST_COMMENTS_LIMIT);
13770
+ /** A comment belongs in the post preview only while it is cached, not deleted and public. */
13771
+ const isPreviewableComment = (comment) => comment !== undefined && !comment.isDeleted && isCommentApproved(comment);
13772
+ /** The post's preview list after one of the user's own top-level comments is created. */
13773
+ const appendLatestCommentId = (post, commentId) => { var _a; return capLatestCommentIds([...((_a = post.comments) !== null && _a !== void 0 ? _a : []), commentId]); };
13774
+
12993
13775
  const postLinkedObject = (post) => {
12994
13776
  return shallowClone(post, {
12995
13777
  childrenPosts: post.children
@@ -13013,19 +13795,13 @@ const postLinkedObject = (post) => {
13013
13795
  get latestComments() {
13014
13796
  if (!post.comments)
13015
13797
  return [];
13016
- return (post.comments
13017
- .map(commentId => {
13018
- var _a;
13019
- const commentCached = (_a = pullFromCache([
13020
- 'comment',
13021
- 'get',
13022
- commentId,
13023
- ])) === null || _a === void 0 ? void 0 : _a.data;
13024
- if (!commentCached)
13025
- return null;
13026
- return commentLinkedObject(commentCached);
13027
- })
13028
- .filter(Boolean) || []);
13798
+ // Same rule as the server's own list and as iOS: deleted and non-public comments are
13799
+ // dropped first, then the newest LATEST_COMMENTS_LIMIT remain
13800
+ return post.comments
13801
+ .map(commentId => { var _a; return (_a = pullFromCache(['comment', 'get', commentId])) === null || _a === void 0 ? void 0 : _a.data; })
13802
+ .filter(isPreviewableComment)
13803
+ .slice(-LATEST_COMMENTS_LIMIT)
13804
+ .map(commentLinkedObject);
13029
13805
  },
13030
13806
  get creator() {
13031
13807
  // The id is checked for a value, not just for presence: a payload can
@@ -13038,6 +13814,15 @@ const postLinkedObject = (post) => {
13038
13814
  return undefined;
13039
13815
  return userLinkedObject(cacheData.data);
13040
13816
  },
13817
+ getLocalCommentCount(includePending = false) {
13818
+ var _a;
13819
+ const engine = PostCommentCountEngine$1.getInstance();
13820
+ // Before any payload has seeded the engine for this post there is no tracker; the
13821
+ // post's own figure is the best answer then.
13822
+ return engine.hasTracker(post.postId)
13823
+ ? engine.getLocalCommentCount(post.postId, includePending)
13824
+ : (_a = post.localCommentCount) !== null && _a !== void 0 ? _a : post.commentsCount;
13825
+ },
13041
13826
  get targetCommunity() {
13042
13827
  var _a;
13043
13828
  if (!post.targetId)
@@ -13402,7 +14187,8 @@ const messageLinkedObject = (message) => {
13402
14187
  },
13403
14188
  get creator() {
13404
14189
  var _a;
13405
- return (_a = pullFromCache(['user', 'get', message.creatorId])) === null || _a === void 0 ? void 0 : _a.data;
14190
+ const creator = (_a = pullFromCache(['user', 'get', message.creatorId])) === null || _a === void 0 ? void 0 : _a.data;
14191
+ return creator ? userLinkedObject(creator) : undefined;
13406
14192
  }, markRead: () => markReadMessage(message), addReaction: (reactionName) => addReactionToMessage(reactionName, message), removeReaction: (reactionName) => removeReactionToMessage(reactionName, message) });
13407
14193
  };
13408
14194
 
@@ -14063,6 +14849,22 @@ const eventLinkedObject = (event) => {
14063
14849
  } });
14064
14850
  };
14065
14851
 
14852
+ const curatedContentTopicLinkedObject = (topic) => {
14853
+ return shallowClone(topic, {
14854
+ analytics: {
14855
+ markAsViewed: () => {
14856
+ AnalyticsEngine$1.getInstance().markCuratedContentWidgetAsViewed(topic.topicId);
14857
+ },
14858
+ markPostAsViewed: (postId) => {
14859
+ AnalyticsEngine$1.getInstance().markCuratedContentPostAsViewed(postId, topic.topicId);
14860
+ },
14861
+ markPostClick: (postId) => {
14862
+ AnalyticsEngine$1.getInstance().markCuratedContentPostAsClicked(postId, topic.topicId);
14863
+ },
14864
+ },
14865
+ });
14866
+ };
14867
+
14066
14868
  const LinkedObject = {
14067
14869
  file: fileLinkedObject,
14068
14870
  ad: adLinkedObject,
@@ -14087,6 +14889,7 @@ const LinkedObject = {
14087
14889
  eventResponse: eventResponseLinkedObject,
14088
14890
  product: productLinkedObject,
14089
14891
  poll: pollLinkedObject,
14892
+ curatedContentTopic: curatedContentTopicLinkedObject,
14090
14893
  };
14091
14894
 
14092
14895
  /* begin_public_function
@@ -15409,9 +16212,9 @@ var AmityUserSearchMatchType;
15409
16212
  AmityUserSearchMatchType["PARTIAL"] = "partial";
15410
16213
  })(AmityUserSearchMatchType || (AmityUserSearchMatchType = {}));
15411
16214
 
15412
- var index$q = /*#__PURE__*/Object.freeze({
16215
+ var index$r = /*#__PURE__*/Object.freeze({
15413
16216
  __proto__: null,
15414
- Relationship: index$r,
16217
+ Relationship: index$s,
15415
16218
  getUserByIds: getUserByIds,
15416
16219
  updateUser: updateUser,
15417
16220
  flagUser: flagUser,
@@ -15819,7 +16622,7 @@ const uploadAudio = async (formData, onProgress) => {
15819
16622
  };
15820
16623
  /* end_public_function */
15821
16624
 
15822
- var index$p = /*#__PURE__*/Object.freeze({
16625
+ var index$q = /*#__PURE__*/Object.freeze({
15823
16626
  __proto__: null,
15824
16627
  getFile: getFile,
15825
16628
  uploadFile: uploadFile,
@@ -16012,147 +16815,6 @@ getMessage$1.locally = (messageId) => {
16012
16815
  };
16013
16816
  };
16014
16817
 
16015
- class ResetTask {
16016
- constructor(postId, latestCreatedAt, serverCommentCount) {
16017
- this.postId = postId;
16018
- this.latestCreatedAt = latestCreatedAt;
16019
- this.serverCommentCount = serverCommentCount;
16020
- }
16021
- }
16022
-
16023
- // Task to track comment creation
16024
- class CreateTask {
16025
- constructor(postId, commentId, createdAt) {
16026
- this.postId = postId;
16027
- this.commentId = commentId;
16028
- this.createdAt = createdAt;
16029
- }
16030
- }
16031
-
16032
- // Task to track comment deletion
16033
- class DeleteTask {
16034
- constructor(postId, commentId) {
16035
- this.postId = postId;
16036
- this.commentId = commentId;
16037
- }
16038
- }
16039
-
16040
- class CommentChange {
16041
- constructor(latestCreatedAt, serverCommentCount) {
16042
- this.latestCommentCreatedAt = latestCreatedAt;
16043
- this.serverCommentCount = serverCommentCount;
16044
- this.createdCommentIds = new Set();
16045
- this.deletedCommentIds = new Set();
16046
- }
16047
- }
16048
-
16049
- class PostCommentCountEngine {
16050
- constructor() {
16051
- this.isProcessing = false;
16052
- this.tasks = [];
16053
- this.commentChangeTracker = new Map();
16054
- }
16055
- queueCommentChangeTask(task) {
16056
- this.tasks.push(task);
16057
- if (!this.isProcessing) {
16058
- this.processCommentChangeTask();
16059
- }
16060
- }
16061
- processCommentChangeTask() {
16062
- if (this.isProcessing) {
16063
- return;
16064
- }
16065
- this.isProcessing = true;
16066
- if (this.tasks.length === 0) {
16067
- this.isProcessing = false;
16068
- return;
16069
- }
16070
- // Process in capped batches, coalescing updates
16071
- const batch = this.tasks.splice(0, PostCommentCountEngine.BATCH_SIZE);
16072
- const modifiedPostIds = new Set();
16073
- batch.forEach(task => {
16074
- let modified = false;
16075
- if (task instanceof ResetTask) {
16076
- modified = this.processResetTaskInternal(task);
16077
- }
16078
- else if (task instanceof CreateTask) {
16079
- modified = this.processCreateTaskInternal(task);
16080
- }
16081
- else if (task instanceof DeleteTask) {
16082
- modified = this.processDeleteTaskInternal(task);
16083
- }
16084
- if (modified) {
16085
- modifiedPostIds.add(task.postId);
16086
- }
16087
- });
16088
- // Publish one update per modified post
16089
- modifiedPostIds.forEach(postId => {
16090
- const count = this.computeCommentCount(postId);
16091
- PostCommentCountEngine.publishUpdate(postId, count);
16092
- });
16093
- this.isProcessing = false;
16094
- // Recurse if more tasks remain
16095
- if (this.tasks.length > 0) {
16096
- this.processCommentChangeTask();
16097
- }
16098
- }
16099
- processResetTaskInternal(task) {
16100
- // Always creates/overwrites tracker
16101
- this.commentChangeTracker.set(task.postId, new CommentChange(task.latestCreatedAt, task.serverCommentCount));
16102
- return true;
16103
- }
16104
- processCreateTaskInternal(task) {
16105
- const tracker = this.commentChangeTracker.get(task.postId);
16106
- if (!tracker)
16107
- return false; // No tracker, skip
16108
- if (tracker.createdCommentIds.has(task.commentId))
16109
- return false; // Deduplication
16110
- if (task.createdAt <= tracker.latestCommentCreatedAt)
16111
- return false; // Timestamp filtering
16112
- tracker.createdCommentIds.add(task.commentId);
16113
- return true;
16114
- }
16115
- processDeleteTaskInternal(task) {
16116
- const tracker = this.commentChangeTracker.get(task.postId);
16117
- if (!tracker)
16118
- return false; // No tracker, skip
16119
- if (tracker.deletedCommentIds.has(task.commentId))
16120
- return false; // Deduplication
16121
- tracker.deletedCommentIds.add(task.commentId);
16122
- return true;
16123
- }
16124
- computeCommentCount(postId) {
16125
- const tracker = this.commentChangeTracker.get(postId);
16126
- if (!tracker)
16127
- return 0;
16128
- const count = tracker.serverCommentCount + tracker.createdCommentIds.size - tracker.deletedCommentIds.size;
16129
- return Math.max(0, count);
16130
- }
16131
- static publishUpdate(postId, newCount) {
16132
- var _a;
16133
- const queryKey = ['post', 'get', postId];
16134
- mergeInCache(queryKey, {
16135
- localCommentCount: newCount,
16136
- });
16137
- const postPayload = (_a = pullFromCache(queryKey)) === null || _a === void 0 ? void 0 : _a.data;
16138
- if (!postPayload)
16139
- return;
16140
- fireEvent('local.post.updated', {
16141
- posts: [postPayload],
16142
- });
16143
- }
16144
- }
16145
- PostCommentCountEngine.BATCH_SIZE = 50;
16146
- let instance;
16147
- var PostCommentCountEngine$1 = {
16148
- getInstance: () => {
16149
- if (!instance) {
16150
- instance = new PostCommentCountEngine();
16151
- }
16152
- return instance;
16153
- },
16154
- };
16155
-
16156
16818
  const updateStreamReferences = (streams, streamId, postId) => {
16157
16819
  if (!streamId)
16158
16820
  return streams;
@@ -16172,7 +16834,7 @@ const preparePostPayload = (payload) => {
16172
16834
  let mappedNewStream = [];
16173
16835
  // feed type
16174
16836
  const posts = postsData.map(post => {
16175
- var _a, _b, _c, _d;
16837
+ var _a, _b, _c;
16176
16838
  const feedType = (_a = postPayload.feeds.find(feed => feed.feedId === post.feedId)) === null || _a === void 0 ? void 0 : _a.feedType;
16177
16839
  const childPosts = payload.postChildren.filter(children => children.parentPostId === post.postId);
16178
16840
  if (childPosts.length > 0 && isAmityLivestreamPost(childPosts[0])) {
@@ -16191,7 +16853,11 @@ const preparePostPayload = (payload) => {
16191
16853
  // Queue ResetTask for this post
16192
16854
  PostCommentCountEngine$1.getInstance().queueCommentChangeTask(new ResetTask(post.postId, latestCreatedAt, (_c = post.commentsCount) !== null && _c !== void 0 ? _c : 0));
16193
16855
  return Object.assign(Object.assign({}, post), { childPosts,
16194
- feedType, localCommentCount: (_d = post.localCommentCount) !== null && _d !== void 0 ? _d : post.commentsCount, pinnedProductId: post === null || post === void 0 ? void 0 : post.pinnedProductId });
16856
+ feedType,
16857
+ // The ResetTask above has just seeded the engine, so this is the server figure plus the
16858
+ // author's own pending comments. Reading `post.commentsCount` here instead would overwrite
16859
+ // that +1 in the cache on every refresh, since the ingest that follows merges this object.
16860
+ localCommentCount: PostCommentCountEngine$1.getInstance().getLocalCommentCount(post.postId, true), pinnedProductId: post === null || post === void 0 ? void 0 : post.pinnedProductId });
16195
16861
  });
16196
16862
  return Object.assign(Object.assign({}, postPayload), { postChildren, videoStreamings: mappedNewStream, posts, communities: communityWithMembershipStatus, communityUsers: mappedCommunityUsers });
16197
16863
  };
@@ -16944,62 +17610,67 @@ const onPostReactionRemoved = (callback) => {
16944
17610
  return createEventSubscriber(client, 'post.removeReaction', 'post.removeReaction', filter);
16945
17611
  };
16946
17612
 
17613
+ /**
17614
+ * `comment.created` fires while a comment is still pending, so delivery is not visibility. The
17615
+ * two side effects that make a comment public — the parent link that feeds the reply list and
17616
+ * the comment-count task — are gated here; `comment.approved` is the only reveal signal, and a
17617
+ * decline arrives as `comment.deleted`.
17618
+ */
17619
+ const applyApprovalSideEffects = (event, comment, previous) => {
17620
+ switch (event) {
17621
+ case 'comment.created':
17622
+ if (isCommentPending(comment)) {
17623
+ recordPendingAuthoredComment(comment);
17624
+ }
17625
+ else {
17626
+ linkCommentToParent(comment);
17627
+ applyCommentCreatedCount(comment);
17628
+ }
17629
+ break;
17630
+ case 'comment.approved':
17631
+ linkCommentToParent(comment);
17632
+ applyCommentApprovedCount(comment);
17633
+ break;
17634
+ case 'comment.updated':
17635
+ // An effective edit in a gated community sends an approved comment back for review
17636
+ if (isCommentPending(comment) && previous && isCommentApproved(previous)) {
17637
+ unlinkCommentFromParent(comment);
17638
+ applyCommentReReviewCount(comment);
17639
+ }
17640
+ break;
17641
+ case 'comment.deleted':
17642
+ unlinkCommentFromParent(comment);
17643
+ // A pending comment was never counted, so its deletion (or decline) moves no count
17644
+ if (previous && isCommentPending(previous))
17645
+ releasePendingAuthoredComment(comment);
17646
+ break;
17647
+ }
17648
+ };
16947
17649
  const createCommentEventSubscriber = (event, callback) => {
16948
17650
  const client = getActiveClient();
16949
17651
  const filter = (payload) => {
16950
- var _a;
17652
+ var _a, _b;
16951
17653
  if (!client.cache) {
16952
17654
  // TODO: here we are missing specific properties here!
16953
17655
  callback(LinkedObject.comment(payload.comments[0]));
16954
17656
  }
16955
17657
  else {
16956
- const processed = ['comment.flagged', 'comment.unflagged'].includes(event)
17658
+ const flagged = ['comment.flagged', 'comment.unflagged'].includes(event)
16957
17659
  ? prepareCommentFromFlaggedEvent(payload)
16958
17660
  : payload;
16959
- // NOTE: The event data should be merge with existing cache rather than replace it
16960
- ingestInCache(processed, undefined, false);
17661
+ // Resolve `approvalStatus` before the merge: an absent value means approved and has to
17662
+ // overwrite a cached `pending`, which a plain merge would leave in place.
17663
+ const processed = prepareCommentPayload(flagged);
16961
17664
  const { comments } = processed;
16962
17665
  if (comments.length > 0) {
16963
- const comment = pullFromCache([
16964
- 'comment',
16965
- 'get',
16966
- comments[0].commentId,
16967
- ]);
16968
- if (['comment.created'].includes(event)) {
16969
- if (comments[0].parentId) {
16970
- const parentComment = pullFromCache([
16971
- 'comment',
16972
- 'get',
16973
- comments[0].parentId,
16974
- ]);
16975
- if (parentComment === null || parentComment === void 0 ? void 0 : parentComment.data) {
16976
- // Skip to update parent childComment if current comment already exists
16977
- if (!parentComment.data.children.includes(comments[0].commentId)) {
16978
- pushToCache(['comment', 'get', comments[0].parentId], Object.assign(Object.assign({}, parentComment.data), { childrenNumber: parentComment.data.childrenNumber + 1, children: [...new Set([...parentComment.data.children, comments[0].commentId])] }));
16979
- }
16980
- }
16981
- }
16982
- }
17666
+ const commentKey = ['comment', 'get', comments[0].commentId];
17667
+ const previous = (_a = pullFromCache(commentKey)) === null || _a === void 0 ? void 0 : _a.data;
17668
+ // NOTE: The event data should be merge with existing cache rather than replace it
17669
+ ingestInCache(processed, undefined, false);
17670
+ const comment = pullFromCache(commentKey);
17671
+ applyApprovalSideEffects(event, comment.data, previous);
16983
17672
  if (['comment.deleted'].includes(event)) {
16984
- if (comments[0].parentId) {
16985
- const parentComment = pullFromCache([
16986
- 'comment',
16987
- 'get',
16988
- comments[0].parentId,
16989
- ]);
16990
- if (parentComment === null || parentComment === void 0 ? void 0 : parentComment.data) {
16991
- // Remove deleted comment in parent childComment if still exists
16992
- if (parentComment.data.children.includes(comments[0].commentId)) {
16993
- const newParentComment = Object.assign(Object.assign({}, parentComment.data), { childrenNumber: parentComment.data.childrenNumber - 1, children: [
16994
- ...new Set([
16995
- ...parentComment.data.children.filter(id => id !== comments[0].commentId),
16996
- ]),
16997
- ] });
16998
- pushToCache(['comment', 'get', comments[0].parentId], newParentComment);
16999
- }
17000
- }
17001
- }
17002
- 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; });
17673
+ 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; });
17003
17674
  queries === null || queries === void 0 ? void 0 : queries.map(({ key, data }) => upsertInCache(key, data, { cachedAt: -1 }));
17004
17675
  }
17005
17676
  callback(LinkedObject.comment(comment.data));
@@ -17011,13 +17682,13 @@ const createCommentEventSubscriber = (event, callback) => {
17011
17682
  const createLocalCommentEventSubscriber = (event, callback) => {
17012
17683
  const client = getActiveClient();
17013
17684
  const filter = (payload) => {
17014
- var _a, _b, _c, _d, _e;
17685
+ var _a, _b, _c, _d;
17015
17686
  if (!client.cache) {
17016
17687
  // TODO: here we are missing specific properties here!
17017
17688
  callback(LinkedObject.comment(payload.comments[0]));
17018
17689
  }
17019
17690
  else {
17020
- const processed = payload;
17691
+ const processed = prepareCommentPayload(payload);
17021
17692
  ingestInCache(processed);
17022
17693
  const { comments } = processed;
17023
17694
  if (comments.length > 0) {
@@ -17026,7 +17697,9 @@ const createLocalCommentEventSubscriber = (event, callback) => {
17026
17697
  'get',
17027
17698
  comments[0].commentId,
17028
17699
  ]);
17029
- if (['local.comment.created'].includes(event)) {
17700
+ // A pending comment is persisted for its author but never linked into the public
17701
+ // surfaces (parent reply list, post comment list)
17702
+ if (['local.comment.created'].includes(event) && isCommentApproved(comments[0])) {
17030
17703
  if (comments[0].parentId) {
17031
17704
  const parentComment = pullFromCache([
17032
17705
  'comment',
@@ -17055,11 +17728,11 @@ const createLocalCommentEventSubscriber = (event, callback) => {
17055
17728
  const postCacheKey = ['post', 'get', comments[0].referenceId];
17056
17729
  const postCache = (_a = pullFromCache(postCacheKey)) === null || _a === void 0 ? void 0 : _a.data;
17057
17730
  if (postCache) {
17058
- const updatedPostCache = Object.assign(Object.assign({}, postCache), { comments: [...postCache.comments, (_b = comments[0]) === null || _b === void 0 ? void 0 : _b.commentId] });
17731
+ const updatedPostCache = Object.assign(Object.assign({}, postCache), { comments: appendLatestCommentId(postCache, comments[0].commentId) });
17059
17732
  pushToCache(postCacheKey, updatedPostCache);
17060
17733
  }
17061
17734
  }
17062
- 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; });
17735
+ 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; });
17063
17736
  queries === null || queries === void 0 ? void 0 : queries.map(({ key, data }) => upsertInCache(key, data, { cachedAt: -1 }));
17064
17737
  }
17065
17738
  if (['local.comment.deleted'].includes(event)) {
@@ -17093,13 +17766,13 @@ const createLocalCommentEventSubscriber = (event, callback) => {
17093
17766
  }
17094
17767
  else {
17095
17768
  const postCacheKey = ['post', 'get', comments[0].referenceId];
17096
- const postCache = (_d = pullFromCache(postCacheKey)) === null || _d === void 0 ? void 0 : _d.data;
17769
+ const postCache = (_c = pullFromCache(postCacheKey)) === null || _c === void 0 ? void 0 : _c.data;
17097
17770
  if (postCache) {
17098
17771
  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); }) });
17099
17772
  pushToCache(postCacheKey, updatedPost);
17100
17773
  }
17101
17774
  }
17102
- 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; });
17775
+ const queries = (_d = queryCache(['comment', 'query'])) === null || _d === void 0 ? void 0 : _d.filter(({ key }) => { var _a; return ((_a = key[2]) === null || _a === void 0 ? void 0 : _a.referenceId) === comment.data.referenceId; });
17103
17776
  queries === null || queries === void 0 ? void 0 : queries.map(({ key, data }) => upsertInCache(key, data, { cachedAt: -1 }));
17104
17777
  }
17105
17778
  callback(LinkedObject.comment(comment.data));
@@ -17160,6 +17833,23 @@ const onCommentUpdated = (callback) => createCommentEventSubscriber('comment.upd
17160
17833
  */
17161
17834
  const onCommentDeleted = (callback) => createCommentEventSubscriber('comment.deleted', callback);
17162
17835
 
17836
+ /**
17837
+ * ```js
17838
+ * import { onCommentApproved } from '@amityco/ts-sdk'
17839
+ * const dispose = onCommentApproved(comment => {
17840
+ * // ...
17841
+ * })
17842
+ * ```
17843
+ *
17844
+ * Fired when a {@link Amity.InternalComment} held for review has been approved
17845
+ *
17846
+ * @param callback The function to call when the event was fired
17847
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
17848
+ *
17849
+ * @category Comment Events
17850
+ */
17851
+ const onCommentApproved = (callback) => createCommentEventSubscriber('comment.approved', callback);
17852
+
17163
17853
  /**
17164
17854
  * ```js
17165
17855
  * import { onCommentFlagged } from '@amityco/ts-sdk'
@@ -17792,7 +18482,7 @@ const getReactions$1 = (params, callback, config) => {
17792
18482
  };
17793
18483
  /* end_public_function */
17794
18484
 
17795
- var index$o = /*#__PURE__*/Object.freeze({
18485
+ var index$p = /*#__PURE__*/Object.freeze({
17796
18486
  __proto__: null,
17797
18487
  addReaction: addReaction,
17798
18488
  removeReaction: removeReaction,
@@ -17803,17 +18493,6 @@ var index$o = /*#__PURE__*/Object.freeze({
17803
18493
  getReactions: getReactions$1
17804
18494
  });
17805
18495
 
17806
- function prepareCommentPayload(commentPayload) {
17807
- const { comments } = commentPayload;
17808
- return Object.assign(Object.assign({}, commentPayload), { comments: comments.map(comment => {
17809
- if (comment.hasOwnProperty('myReactions'))
17810
- return comment;
17811
- // Sometimes `myReactions` field will not come with BE response because that field is empty
17812
- // We need to put it with an empty array manually to make it show up in client side
17813
- return Object.assign({ myReactions: [] }, comment);
17814
- }) });
17815
- }
17816
-
17817
18496
  const createMessageReport = async ({ client, referenceId, }) => {
17818
18497
  const { data: payload } = await client.http.post(`/api/v5/messages/${encodeURIComponent(referenceId)}/flags`);
17819
18498
  if (client.cache) {
@@ -18277,20 +18956,20 @@ const getArchivedChannelIds = async () => {
18277
18956
 
18278
18957
  /**
18279
18958
  * ```js
18280
- * import { onMessageUpdated } from '@amityco/ts-sdk'
18281
- * const dispose = onMessageUpdated(message => {
18959
+ * import { onMessageDeleted } from '@amityco/ts-sdk'
18960
+ * const dispose = onMessageDeleted(message => {
18282
18961
  * // ...
18283
18962
  * })
18284
18963
  * ```
18285
18964
  *
18286
- * Fired when an {@link Amity.Message} has been updated
18965
+ * Fired when an {@link Amity.Message} was deleted
18287
18966
  *
18288
18967
  * @param callback The function to call when the event was fired
18289
18968
  * @returns an {@link Amity.Unsubscriber} function to stop listening
18290
18969
  *
18291
18970
  * @category Message Events
18292
18971
  */
18293
- const onMessageUpdated = (callback) => {
18972
+ const onMessageDeleted = (callback) => {
18294
18973
  const client = getActiveClient();
18295
18974
  const filter = async (rawPayload) => {
18296
18975
  const payload = await prepareMessagePayload(rawPayload);
@@ -18298,30 +18977,48 @@ const onMessageUpdated = (callback) => {
18298
18977
  callback(payload.messages[0]);
18299
18978
  };
18300
18979
  const disposers = [
18301
- createEventSubscriber(client, 'onMessageUpdated', 'message.updated', filter),
18302
- createEventSubscriber(client, 'onMessageUpdated', 'local.message.updated', payload => callback(payload.messages[0])),
18980
+ createEventSubscriber(client, 'message/onMessageDeleted', 'message.deleted', filter),
18981
+ createEventSubscriber(client, 'message/onMessageDeleted', 'local.message.deleted', payload => callback(payload.messages[0])),
18303
18982
  ];
18304
18983
  return () => {
18305
18984
  disposers.forEach(fn => fn());
18306
18985
  };
18307
18986
  };
18308
18987
 
18988
+ /**
18989
+ * Defensive clear of `channel.pinnedMessage` when the pinned message itself is deleted.
18990
+ *
18991
+ * The server also emits `channel.messageUnpinned` in this case, but realtime events are
18992
+ * fire-and-forget and their order is not guaranteed. Clearing on `message.deleted`
18993
+ * (MQTT or local) makes the banner disappear even if the unpin event is lost; the
18994
+ * server's own `messageUnpinned` then becomes a no-op.
18995
+ *
18996
+ * Internal — wired into the `getChannel` live object; not exported publicly.
18997
+ */
18998
+ const onChannelPinnedMessageDeleted = (callback) => onMessageDeleted(message => {
18999
+ var _a;
19000
+ const channel = findCachedChannelByAnyId(message.channelId);
19001
+ if (!channel || ((_a = channel.pinnedMessage) === null || _a === void 0 ? void 0 : _a.messageId) !== message.messageId)
19002
+ return;
19003
+ callback(updatePinnedMessageCache(channel, null));
19004
+ });
19005
+
18309
19006
  /**
18310
19007
  * ```js
18311
- * import { onMessageDeleted } from '@amityco/ts-sdk'
18312
- * const dispose = onMessageDeleted(message => {
19008
+ * import { onMessageUpdated } from '@amityco/ts-sdk'
19009
+ * const dispose = onMessageUpdated(message => {
18313
19010
  * // ...
18314
19011
  * })
18315
19012
  * ```
18316
19013
  *
18317
- * Fired when an {@link Amity.Message} was deleted
19014
+ * Fired when an {@link Amity.Message} has been updated
18318
19015
  *
18319
19016
  * @param callback The function to call when the event was fired
18320
19017
  * @returns an {@link Amity.Unsubscriber} function to stop listening
18321
19018
  *
18322
19019
  * @category Message Events
18323
19020
  */
18324
- const onMessageDeleted = (callback) => {
19021
+ const onMessageUpdated = (callback) => {
18325
19022
  const client = getActiveClient();
18326
19023
  const filter = async (rawPayload) => {
18327
19024
  const payload = await prepareMessagePayload(rawPayload);
@@ -18329,8 +19026,8 @@ const onMessageDeleted = (callback) => {
18329
19026
  callback(payload.messages[0]);
18330
19027
  };
18331
19028
  const disposers = [
18332
- createEventSubscriber(client, 'message/onMessageDeleted', 'message.deleted', filter),
18333
- createEventSubscriber(client, 'message/onMessageDeleted', 'local.message.deleted', payload => callback(payload.messages[0])),
19029
+ createEventSubscriber(client, 'onMessageUpdated', 'message.updated', filter),
19030
+ createEventSubscriber(client, 'onMessageUpdated', 'local.message.updated', payload => callback(payload.messages[0])),
18334
19031
  ];
18335
19032
  return () => {
18336
19033
  disposers.forEach(fn => fn());
@@ -18911,6 +19608,132 @@ softDeleteMessage.optimistically = (messageId) => {
18911
19608
  };
18912
19609
  /* end_public_function */
18913
19610
 
19611
+ /**
19612
+ * Fire-and-forget refresh of `channelUsers[...].permissions` for the channel a
19613
+ * message belongs to. Used by `pinMessage` / `unpinMessage` on `403` so the next
19614
+ * `hasPermission(PIN_MESSAGE).channel(channelId)` reflects a revoked role
19615
+ * ([`pinMessage` REQ-010](../../@types/../..) / [`unpinMessage` REQ-008](../../@types/../..)).
19616
+ *
19617
+ * The API only receives a `messageId`, so the channel is resolved through the
19618
+ * local cache: first the message row, then any cached channel whose current pin
19619
+ * is that message. When nothing matches the refresh is skipped — the next
19620
+ * channel read picks up the fresh permissions anyway.
19621
+ */
19622
+ const refreshChannelPermissionsForMessage = (messageId) => {
19623
+ var _a, _b, _c;
19624
+ const cachedMessage = (_a = getMessage$1.locally(messageId)) === null || _a === void 0 ? void 0 : _a.data;
19625
+ let channelId = cachedMessage === null || cachedMessage === void 0 ? void 0 : cachedMessage.channelId;
19626
+ if (!channelId) {
19627
+ channelId = (_c = (_b = queryCache(['channel', 'get'])) === null || _b === void 0 ? void 0 : _b.find(({ data }) => { var _a; return ((_a = data.pinnedMessage) === null || _a === void 0 ? void 0 : _a.messageId) === messageId; })) === null || _c === void 0 ? void 0 : _c.data.channelId;
19628
+ }
19629
+ if (!channelId)
19630
+ return;
19631
+ getChannel$1(channelId).catch(() => {
19632
+ /* silent per spec; UIKit renders no error toast */
19633
+ });
19634
+ };
19635
+
19636
+ /* begin_public_function
19637
+ id: message.pin
19638
+ */
19639
+ /**
19640
+ * ```js
19641
+ * import { MessageRepository } from '@amityco/ts-sdk'
19642
+ * const channel = await MessageRepository.pinMessage(messageId)
19643
+ * console.log(channel.pinnedMessage)
19644
+ * ```
19645
+ *
19646
+ * Pins a {@link Amity.Message} in its livestream (`live`) channel. A channel holds at
19647
+ * most one pinned message: pinning a different message replaces the current pin in a
19648
+ * single step — no unpin is needed first. Pinning the message that is already pinned is
19649
+ * rejected by the server (400).
19650
+ *
19651
+ * The caller must hold the `PIN_MESSAGE` permission in the channel (or be an admin with
19652
+ * `ModerateChannel`). The server also rejects pinning a deleted message, a message whose
19653
+ * author is banned from the channel or globally banned, and pinning in any channel type
19654
+ * other than `live`. On any error the pin state is left unchanged.
19655
+ *
19656
+ * The response is the affected channel; it is ingested into cache and fires
19657
+ * `local.channel.updated`, so `ChannelRepository.getChannel` observers update without
19658
+ * waiting for the `channel.messagePinned` realtime event.
19659
+ *
19660
+ * @param messageId the **internal** ID of the {@link Amity.Message} to pin
19661
+ * @returns the updated {@link Amity.Channel}, with `pinnedMessage` set
19662
+ *
19663
+ * @category Message API
19664
+ * @async
19665
+ */
19666
+ const pinMessage = async (messageId) => {
19667
+ const client = getActiveClient();
19668
+ client.log('message/pinMessage', messageId);
19669
+ let payload;
19670
+ try {
19671
+ ({ data: payload } = await client.http.post(`/api/v5/messages/${encodeURIComponent(messageId)}/pin`));
19672
+ }
19673
+ catch (error) {
19674
+ if ((error === null || error === void 0 ? void 0 : error.code) === 400301 /* Amity.ServerError.PERMISSION_DENIED */) {
19675
+ refreshChannelPermissionsForMessage(messageId);
19676
+ }
19677
+ throw error;
19678
+ }
19679
+ const data = await prepareChannelPayload(payload);
19680
+ if (client.cache)
19681
+ ingestInCache(data);
19682
+ fireEvent('local.channel.updated', data);
19683
+ return constructChannelObject(data.channels[0]);
19684
+ };
19685
+ /* end_public_function */
19686
+
19687
+ /* begin_public_function
19688
+ id: message.unpin
19689
+ */
19690
+ /**
19691
+ * ```js
19692
+ * import { MessageRepository } from '@amityco/ts-sdk'
19693
+ * const channel = await MessageRepository.unpinMessage(messageId)
19694
+ * console.log(channel.pinnedMessage) // null
19695
+ * ```
19696
+ *
19697
+ * Removes the pin from a {@link Amity.Message}. `messageId` must be the message that is
19698
+ * **currently** pinned in its channel; otherwise the server answers 400 — this is
19699
+ * deliberate, so a stale client cannot clear a pin that someone else has since replaced.
19700
+ * On that error, re-read the channel (or rely on the `channel.messagePinned` event) to
19701
+ * get the current pin.
19702
+ *
19703
+ * The caller must hold the `PIN_MESSAGE` permission in the channel (or be an admin with
19704
+ * `ModerateChannel`). Unpinning is allowed regardless of channel type and stream state.
19705
+ *
19706
+ * The response is the affected channel; it is ingested into cache and fires
19707
+ * `local.channel.updated`, so `ChannelRepository.getChannel` observers update without
19708
+ * waiting for the `channel.messageUnpinned` realtime event.
19709
+ *
19710
+ * @param messageId the **internal** ID of the currently pinned {@link Amity.Message}
19711
+ * @returns the updated {@link Amity.Channel}, with `pinnedMessage` cleared
19712
+ *
19713
+ * @category Message API
19714
+ * @async
19715
+ */
19716
+ const unpinMessage = async (messageId) => {
19717
+ const client = getActiveClient();
19718
+ client.log('message/unpinMessage', messageId);
19719
+ let payload;
19720
+ try {
19721
+ ({ data: payload } = await client.http.delete(`/api/v5/messages/${encodeURIComponent(messageId)}/pin`));
19722
+ }
19723
+ catch (error) {
19724
+ if ((error === null || error === void 0 ? void 0 : error.code) === 400301 /* Amity.ServerError.PERMISSION_DENIED */) {
19725
+ refreshChannelPermissionsForMessage(messageId);
19726
+ }
19727
+ throw error;
19728
+ }
19729
+ const data = await prepareChannelPayload(payload);
19730
+ if (client.cache)
19731
+ ingestInCache(data);
19732
+ fireEvent('local.channel.updated', data);
19733
+ return constructChannelObject(data.channels[0]);
19734
+ };
19735
+ /* end_public_function */
19736
+
18914
19737
  /**
18915
19738
  * ```js
18916
19739
  * import { MessageRepository } from '@amityco/ts-sdk'
@@ -19807,13 +20630,15 @@ const searchMessage = (params, callback) => {
19807
20630
  };
19808
20631
  /* end_public_function */
19809
20632
 
19810
- var index$n = /*#__PURE__*/Object.freeze({
20633
+ var index$o = /*#__PURE__*/Object.freeze({
19811
20634
  __proto__: null,
19812
20635
  createMessage: createMessage,
19813
20636
  updateMessage: updateMessage,
19814
20637
  editMessage: editMessage,
19815
20638
  deleteMessage: deleteMessage,
19816
20639
  softDeleteMessage: softDeleteMessage,
20640
+ pinMessage: pinMessage,
20641
+ unpinMessage: unpinMessage,
19817
20642
  markAsDelivered: markAsDelivered,
19818
20643
  getReadUsers: getReadUsers,
19819
20644
  getDeliveredUsers: getDeliveredUsers,
@@ -20343,7 +21168,7 @@ const stopMessageReceiptSync = (subChannelId) => {
20343
21168
  };
20344
21169
  /* end_public_function */
20345
21170
 
20346
- var index$m = /*#__PURE__*/Object.freeze({
21171
+ var index$n = /*#__PURE__*/Object.freeze({
20347
21172
  __proto__: null,
20348
21173
  getSubChannelByIds: getSubChannels$1,
20349
21174
  createSubChannel: createSubChannel,
@@ -20451,6 +21276,9 @@ const getChannel = (channelId, callback) => {
20451
21276
  onChannelMemberBanned,
20452
21277
  onChannelMemberUnbanned,
20453
21278
  onChannelSetMuted,
21279
+ onChannelMessagePinned,
21280
+ onChannelMessageUnpinned,
21281
+ onChannelPinnedMessageDeleted,
20454
21282
  convertEventPayload(onChannelMarkerFetched, 'entityId', 'channel'),
20455
21283
  convertEventPayload(onChannelMarkerUpdated, 'entityId', 'channel'),
20456
21284
  convertEventPayload((callback) => {
@@ -21076,6 +21904,14 @@ class ChannelLiveCollectionController extends LiveCollectionController {
21076
21904
  return;
21077
21905
  if (messagePreviewSetting ===
21078
21906
  "message-preview-not-include-deleted" /* Amity.MessagePreviewSetting.MESSAGE_PREVIEW_NOT_INCLUDE_DELETED */) {
21907
+ // The replacement preview the server sends back is an OLDER
21908
+ // message than the one just deleted, and
21909
+ // updateChannelMessagePreviewCache drops any preview that is not
21910
+ // newer than what is cached — so without clearing the entry
21911
+ // first the refetch is a no-op and the channel keeps showing the
21912
+ // deleted message. getChannel's own live object already does
21913
+ // this; the live collections did not.
21914
+ dropFromCache(['messagePreviewChannel', 'get', message.channelId]);
21079
21915
  await getChannel$1(message.channelId);
21080
21916
  }
21081
21917
  callback(message);
@@ -21800,6 +22636,14 @@ class SearchChannelLiveCollectionController extends LiveCollectionController {
21800
22636
  return;
21801
22637
  if (messagePreviewSetting ===
21802
22638
  "message-preview-not-include-deleted" /* Amity.MessagePreviewSetting.MESSAGE_PREVIEW_NOT_INCLUDE_DELETED */) {
22639
+ // The replacement preview the server sends back is an OLDER
22640
+ // message than the one just deleted, and
22641
+ // updateChannelMessagePreviewCache drops any preview that is not
22642
+ // newer than what is cached — so without clearing the entry
22643
+ // first the refetch is a no-op and the channel keeps showing the
22644
+ // deleted message. getChannel's own live object already does
22645
+ // this; the live collections did not.
22646
+ dropFromCache(['messagePreviewChannel', 'get', message.channelId]);
21803
22647
  await getChannel$1(message.channelId);
21804
22648
  }
21805
22649
  callback(message);
@@ -22294,7 +23138,7 @@ const searchMembers$1 = (params, callback, config) => {
22294
23138
  };
22295
23139
  /* end_public_function */
22296
23140
 
22297
- var index$l = /*#__PURE__*/Object.freeze({
23141
+ var index$m = /*#__PURE__*/Object.freeze({
22298
23142
  __proto__: null,
22299
23143
  addMembers: addMembers$1,
22300
23144
  removeMembers: removeMembers$1,
@@ -22516,7 +23360,7 @@ const unmuteMembers = async (channelId, userIds) => {
22516
23360
  };
22517
23361
  /* end_public_function */
22518
23362
 
22519
- var index$k = /*#__PURE__*/Object.freeze({
23363
+ var index$l = /*#__PURE__*/Object.freeze({
22520
23364
  __proto__: null,
22521
23365
  addRole: addRole,
22522
23366
  removeRole: removeRole,
@@ -22526,10 +23370,10 @@ var index$k = /*#__PURE__*/Object.freeze({
22526
23370
  unmuteMembers: unmuteMembers
22527
23371
  });
22528
23372
 
22529
- var index$j = /*#__PURE__*/Object.freeze({
23373
+ var index$k = /*#__PURE__*/Object.freeze({
22530
23374
  __proto__: null,
22531
- Membership: index$l,
22532
- Moderation: index$k,
23375
+ Membership: index$m,
23376
+ Moderation: index$l,
22533
23377
  getChannelByIds: getChannelByIds$1,
22534
23378
  createChannel: createChannel,
22535
23379
  updateChannel: updateChannel,
@@ -22547,6 +23391,8 @@ var index$j = /*#__PURE__*/Object.freeze({
22547
23391
  onChannelJoined: onChannelJoined,
22548
23392
  onChannelLeft: onChannelLeft,
22549
23393
  onChannelSetMuted: onChannelSetMuted,
23394
+ onChannelMessagePinned: onChannelMessagePinned,
23395
+ onChannelMessageUnpinned: onChannelMessageUnpinned,
22550
23396
  onChannelMemberAdded: onChannelMemberAdded,
22551
23397
  onChannelMemberRemoved: onChannelMemberRemoved,
22552
23398
  onChannelMemberBanned: onChannelMemberBanned,
@@ -22564,7 +23410,11 @@ var index$j = /*#__PURE__*/Object.freeze({
22564
23410
  isUnreadCountSupport: isUnreadCountSupport,
22565
23411
  convertFromRaw: convertFromRaw,
22566
23412
  preUpdateChannelCache: preUpdateChannelCache,
22567
- prepareChannelPayload: prepareChannelPayload
23413
+ prepareChannelPayload: prepareChannelPayload,
23414
+ findCachedChannelByAnyId: findCachedChannelByAnyId,
23415
+ findCachedChannelForPin: findCachedChannelForPin,
23416
+ updatePinnedMessageCache: updatePinnedMessageCache,
23417
+ applyChannelRolePayload: applyChannelRolePayload
22568
23418
  });
22569
23419
 
22570
23420
  const saveCommunityUsers = (communities, communityUsers) => {
@@ -23958,7 +24808,7 @@ const searchMembers = (params, callback, config) => {
23958
24808
  };
23959
24809
  /* end_public_function */
23960
24810
 
23961
- var index$i = /*#__PURE__*/Object.freeze({
24811
+ var index$j = /*#__PURE__*/Object.freeze({
23962
24812
  __proto__: null,
23963
24813
  addMembers: addMembers,
23964
24814
  removeMembers: removeMembers,
@@ -25209,7 +26059,7 @@ const unbanMembers = async (communityId, userIds) => {
25209
26059
  };
25210
26060
  /* end_public_function */
25211
26061
 
25212
- var index$h = /*#__PURE__*/Object.freeze({
26062
+ var index$i = /*#__PURE__*/Object.freeze({
25213
26063
  __proto__: null,
25214
26064
  addRoles: addRoles,
25215
26065
  removeRoles: removeRoles,
@@ -25217,10 +26067,10 @@ var index$h = /*#__PURE__*/Object.freeze({
25217
26067
  unbanMembers: unbanMembers
25218
26068
  });
25219
26069
 
25220
- var index$g = /*#__PURE__*/Object.freeze({
26070
+ var index$h = /*#__PURE__*/Object.freeze({
25221
26071
  __proto__: null,
25222
- Moderation: index$h,
25223
- Membership: index$i,
26072
+ Moderation: index$i,
26073
+ Membership: index$j,
25224
26074
  getCommunityByIds: getCommunities$1,
25225
26075
  createCommunity: createCommunity,
25226
26076
  updateCommunity: updateCommunity,
@@ -25456,7 +26306,7 @@ const getCategories = (params, callback, config) => {
25456
26306
  };
25457
26307
  /* end_public_function */
25458
26308
 
25459
- var index$f = /*#__PURE__*/Object.freeze({
26309
+ var index$g = /*#__PURE__*/Object.freeze({
25460
26310
  __proto__: null,
25461
26311
  getCategory: getCategory,
25462
26312
  getCategories: getCategories
@@ -25755,6 +26605,11 @@ const createCommentOptimistic = (bundle) => {
25755
26605
  targetType: 'community',
25756
26606
  dataType: 'text',
25757
26607
  syncState: "syncing" /* Amity.SyncState.Syncing */,
26608
+ // In a gated community the optimistic object stays out of public collections until the
26609
+ // response settles the status; the author's own view still updates through the local event
26610
+ approvalStatus: isCommentApprovalRequired(bundle)
26611
+ ? "pending" /* Amity.CommentApprovalStatusEnum.Pending */
26612
+ : "approved" /* Amity.CommentApprovalStatusEnum.Approved */,
25758
26613
  };
25759
26614
  const cachedAt = UNSYNCED_OBJECT_CACHED_AT_VALUE;
25760
26615
  pushToCache(['comment', 'get', comment.commentId], comment, { cachedAt });
@@ -25771,32 +26626,57 @@ const createCommentOptimistic = (bundle) => {
25771
26626
  * ```js
25772
26627
  * import { CommentRepository } from '@amityco/ts-sdk'
25773
26628
  * const newComment = await CommentRepository.createComment(bundle)
26629
+ *
26630
+ * // Server-confirmed only: nothing is shown until the request succeeds
26631
+ * const confirmed = await CommentRepository.createComment(bundle, false)
25774
26632
  * ```
25775
26633
  *
25776
26634
  * Creates an {@link Amity.Comment}
25777
26635
  *
26636
+ * With `isOptimistic` (the default) a local comment appears at once with `syncState: 'syncing'`,
26637
+ * moves to `'synced'` when the server confirms it and to `'error'` if the request fails, so the
26638
+ * caller can offer a retry. With `isOptimistic: false` nothing is created locally until the
26639
+ * server answers: on success the comment arrives already `'synced'`; on failure no comment
26640
+ * entity exists anywhere and the promise rejects.
26641
+ *
25778
26642
  * @param bundle The data necessary to create a new {@link Amity.Comment}
26643
+ * @param isOptimistic Show a local comment before the server confirms it. Default `true`.
26644
+ * Ignored when the client cache is disabled, since there is nowhere to hold the local comment.
25779
26645
  * @returns The newly created {@link Amity.Comment}
25780
26646
  *
25781
26647
  * @category Comment API
25782
26648
  * @async
25783
26649
  */
25784
- const createComment = async (bundle) => {
26650
+ const createComment = async (bundle, isOptimistic = true) => {
25785
26651
  const client = getActiveClient();
25786
- client.log('comment/createComment', bundle);
25787
- const optimisticData = createCommentOptimistic(bundle);
26652
+ client.log('comment/createComment', Object.assign(Object.assign({}, bundle), { isOptimistic }));
26653
+ // Without a local comment the server assigns the id; with one, the same id travels through the
26654
+ // optimistic object, the response and every realtime event so each step recognises it.
26655
+ const optimisticData = isOptimistic ? createCommentOptimistic(bundle) : undefined;
25788
26656
  const commentId = optimisticData === null || optimisticData === void 0 ? void 0 : optimisticData.commentId;
25789
26657
  try {
25790
- const { data } = await client.http.post('/api/v3/comments', Object.assign(Object.assign({}, bundle), { commentId }));
25791
- const { comments } = data;
26658
+ const { data: payload } = await client.http.post('/api/v3/comments', Object.assign(Object.assign({}, bundle), { commentId }));
25792
26659
  // BE always returns an array of comments If it got record 0 from BE it might have a problem on creation logic
25793
- if (comments.length === 0)
26660
+ if (payload.comments.length === 0)
25794
26661
  throw new Error('Comment not created');
26662
+ // Absent `approvalStatus` means approved; the result is the only source of the status.
26663
+ // The confirmed comment is written as `synced` so the cache itself tells the two states apart
26664
+ // (a local-event subscriber is not guaranteed to be listening).
26665
+ const prepared = prepareCommentPayload(payload);
26666
+ const data = Object.assign(Object.assign({}, prepared), { comments: prepared.comments.map(comment => comment.commentId === (commentId !== null && commentId !== void 0 ? commentId : comment.commentId)
26667
+ ? Object.assign(Object.assign({}, comment), { syncState: "synced" /* Amity.SyncState.Synced */ }) : comment) });
26668
+ const { comments } = data;
25795
26669
  const cachedAt = client.cache && Date.now();
25796
26670
  if (client.cache)
25797
26671
  ingestInCache(data, { cachedAt });
25798
26672
  if (['post', 'content'].includes(bundle.referenceType)) {
25799
- PostCommentCountEngine$1.getInstance().queueCommentChangeTask(new CreateTask(bundle.referenceId, comments[0].commentId, data.comments[0].createdAt));
26673
+ if (isCommentPending(comments[0])) {
26674
+ // Not counted for anyone else until approved; the author sees their own +1
26675
+ recordPendingAuthoredComment(comments[0]);
26676
+ }
26677
+ else {
26678
+ PostCommentCountEngine$1.getInstance().queueCommentChangeTask(new CreateTask(bundle.referenceId, comments[0].commentId, comments[0].createdAt));
26679
+ }
25800
26680
  }
25801
26681
  else if (bundle.referenceType === 'story') {
25802
26682
  const storyIndex = pullFromCache([
@@ -25817,9 +26697,9 @@ const createComment = async (bundle) => {
25817
26697
  categories: [],
25818
26698
  comments,
25819
26699
  communities: [],
25820
- communityUsers: data.communityUsers,
25821
- files: data.files,
25822
- users: data.users,
26700
+ communityUsers: payload.communityUsers,
26701
+ files: payload.files,
26702
+ users: payload.users,
25823
26703
  });
25824
26704
  }
25825
26705
  }
@@ -25827,9 +26707,9 @@ const createComment = async (bundle) => {
25827
26707
  fireEvent('local.comment.created', {
25828
26708
  comments: [Object.assign(Object.assign({}, comments[0]), { syncState: "synced" /* Amity.SyncState.Synced */ })],
25829
26709
  commentChildren: data.commentChildren,
25830
- users: data.users,
25831
- files: data.files,
25832
- communityUsers: data.communityUsers,
26710
+ users: payload.users,
26711
+ files: payload.files,
26712
+ communityUsers: payload.communityUsers,
25833
26713
  });
25834
26714
  return {
25835
26715
  data: LinkedObject.comment(comments[0]),
@@ -25837,9 +26717,17 @@ const createComment = async (bundle) => {
25837
26717
  };
25838
26718
  }
25839
26719
  catch (e) {
26720
+ // Only a local comment has anything to mark as failed; a server-confirmed-only create leaves
26721
+ // no entity behind
25840
26722
  if (optimisticData) {
26723
+ const failed = Object.assign(Object.assign({}, optimisticData), { syncState: "error" /* Amity.SyncState.Error */ });
26724
+ // Write the failure to the cache directly: the local event below only reaches pages that
26725
+ // observe comments, and the caller's retry UI reads the cached state
26726
+ pushToCache(['comment', 'get', failed.commentId], failed, {
26727
+ cachedAt: UNSYNCED_OBJECT_CACHED_AT_VALUE,
26728
+ });
25841
26729
  fireEvent('local.comment.created', {
25842
- comments: [Object.assign(Object.assign({}, optimisticData), { syncState: "error" /* Amity.SyncState.Error */ })],
26730
+ comments: [failed],
25843
26731
  commentChildren: [],
25844
26732
  users: [],
25845
26733
  files: [],
@@ -25872,16 +26760,26 @@ const createComment = async (bundle) => {
25872
26760
  * @async
25873
26761
  */
25874
26762
  const updateComment = async (commentId, patch) => {
26763
+ var _a;
25875
26764
  const client = getActiveClient();
25876
26765
  client.log('user/updateComment', patch);
25877
- const { data } = await client.http.put(`/api/v3/comments/${encodeURIComponent(commentId)}`, patch);
26766
+ const previous = (_a = pullFromCache(['comment', 'get', commentId])) === null || _a === void 0 ? void 0 : _a.data;
26767
+ const { data: payload } = await client.http.put(`/api/v3/comments/${encodeURIComponent(commentId)}`, patch);
26768
+ const data = prepareCommentPayload(payload);
26769
+ const updated = data.comments.find(comment => comment.commentId === commentId);
25878
26770
  const cachedAt = client.cache && Date.now();
25879
26771
  if (client.cache)
25880
26772
  ingestInCache(data, { cachedAt });
25881
- fireEvent('comment.updated', data);
25882
- const { comments } = data;
26773
+ // An effective edit in a gated community sends the comment back for review: it leaves the
26774
+ // public reply list and the public count once. The `comment.updated` event below removes it
26775
+ // from the comment collections; the realtime echo finds it already pending and does nothing.
26776
+ if (isCommentPending(updated) && previous && isCommentApproved(previous)) {
26777
+ unlinkCommentFromParent(updated);
26778
+ applyCommentReReviewCount(updated);
26779
+ }
26780
+ fireEvent('comment.updated', payload);
25883
26781
  return {
25884
- data: LinkedObject.comment(comments.find(comment => comment.commentId === commentId)),
26782
+ data: LinkedObject.comment(updated),
25885
26783
  cachedAt,
25886
26784
  };
25887
26785
  };
@@ -26021,7 +26919,13 @@ const deleteComment = async (commentId, permanent = false) => {
26021
26919
  const post = (_b = pullFromCache(['post', 'get', comment.data.referenceId])) === null || _b === void 0 ? void 0 : _b.data;
26022
26920
  if (post) {
26023
26921
  const engine = PostCommentCountEngine$1.getInstance();
26024
- engine.queueCommentChangeTask(new DeleteTask(post.postId, commentId));
26922
+ if (isCommentPending(comment.data)) {
26923
+ // Never counted for anyone else; only the author's own +1 goes away
26924
+ engine.removePendingAuthoredComment(post.postId, commentId);
26925
+ }
26926
+ else {
26927
+ engine.queueCommentChangeTask(new DeleteTask(post.postId, commentId));
26928
+ }
26025
26929
  if (!deleted.parentId && ((_c = deleted.children /* c8 ignore next */) === null || _c === void 0 ? void 0 : _c.length) > 0) {
26026
26930
  // NOTE: delete the parent comment will also remove all children comments
26027
26931
  deleted.children.forEach((childCommentId) => {
@@ -26091,6 +26995,109 @@ const hardDeleteComment = async (commentId) => {
26091
26995
  };
26092
26996
  /* end_public_function */
26093
26997
 
26998
+ /* begin_public_function
26999
+ id: comment.approve
27000
+ */
27001
+ /**
27002
+ * ```js
27003
+ * import { CommentRepository } from '@amityco/ts-sdk'
27004
+ *
27005
+ * const { data: comment } = await CommentRepository.approveComment('commentId')
27006
+ * ```
27007
+ *
27008
+ * Approves a {@link Amity.Comment} held for review, making it visible to everyone
27009
+ *
27010
+ * @param commentId The {@link Amity.Comment} ID to be approved
27011
+ * @return The {@link Amity.Comment} that was approved
27012
+ *
27013
+ * @category Comment API
27014
+ * @async
27015
+ */
27016
+ const approveComment = async (commentId) => {
27017
+ const client = getActiveClient();
27018
+ client.log('comment/approveComment', commentId);
27019
+ // No request body, and nothing is revealed before the server confirms
27020
+ const { data: payload } = await client.http.post(`/api/v3/comments/${encodeURIComponent(commentId)}/approve`);
27021
+ const data = prepareCommentPayload(prepareMembershipPayload(payload, 'communityUsers'));
27022
+ const approved = data.comments.find(comment => comment.commentId === commentId);
27023
+ const cachedAt = client.cache && Date.now();
27024
+ if (client.cache)
27025
+ ingestInCache(data, { cachedAt });
27026
+ // The reveal path: parent link and exactly one count task. Applied here so it does not depend
27027
+ // on an observer being open; the event below and the realtime `comment.approved` that follows
27028
+ // find the work done and reconcile by comment id (collection insert, queue removal).
27029
+ linkCommentToParent(approved);
27030
+ applyCommentApprovedCount(approved);
27031
+ fireEvent('comment.approved', payload);
27032
+ return {
27033
+ data: LinkedObject.comment(approved),
27034
+ cachedAt,
27035
+ };
27036
+ };
27037
+ /* end_public_function */
27038
+
27039
+ // Rejected is terminal and implies deleted
27040
+ const markRejected = (comment) => (Object.assign(Object.assign({}, comment), { approvalStatus: "rejected" /* Amity.CommentApprovalStatusEnum.Rejected */, isDeleted: true }));
27041
+ /* begin_public_function
27042
+ id: comment.decline
27043
+ */
27044
+ /**
27045
+ * ```js
27046
+ * import { CommentRepository } from '@amityco/ts-sdk'
27047
+ *
27048
+ * const { data: comment } = await CommentRepository.declineComment('commentId', 'Off-topic')
27049
+ * ```
27050
+ *
27051
+ * Declines a {@link Amity.Comment} held for review. A declined comment is also a deleted comment
27052
+ *
27053
+ * @param commentId The {@link Amity.Comment} ID to be declined
27054
+ * @param reason Optional reviewer's note, at most 250 characters, kept for the moderation record only
27055
+ * @return The {@link Amity.Comment} that was declined
27056
+ *
27057
+ * @category Comment API
27058
+ * @async
27059
+ */
27060
+ const declineComment = async (commentId, reason) => {
27061
+ var _a, _b, _c, _d, _e, _f;
27062
+ const client = getActiveClient();
27063
+ client.log('comment/declineComment', commentId, reason);
27064
+ // Read before the request: the fallback when the response carries no comment
27065
+ const cached = (_a = pullFromCache(['comment', 'get', commentId])) === null || _a === void 0 ? void 0 : _a.data;
27066
+ // `reason` is surfaced to nobody; the key is absent from the body when the caller gives none
27067
+ const { data: payload } = await client.http.post(`/api/v3/comments/${encodeURIComponent(commentId)}/decline`, reason ? { reason } : undefined);
27068
+ // The BE contract (PDT-5135 §3) states the response body for approve only; for decline the
27069
+ // standard comment payload is assumed. Ingest whatever came back, and when the body carries no
27070
+ // comment fall back to the cached one so the delete path below still runs.
27071
+ const returned = (_b = payload === null || payload === void 0 ? void 0 : payload.comments) === null || _b === void 0 ? void 0 : _b.some(comment => comment.commentId === commentId);
27072
+ if (!returned && !cached)
27073
+ throw new Error('Comment not found after decline');
27074
+ const comments = returned
27075
+ ? payload.comments.map(comment => comment.commentId === commentId ? markRejected(comment) : comment)
27076
+ : [markRejected(cached)];
27077
+ const declined = {
27078
+ comments,
27079
+ commentChildren: (_c = payload === null || payload === void 0 ? void 0 : payload.commentChildren) !== null && _c !== void 0 ? _c : [],
27080
+ users: (_d = payload === null || payload === void 0 ? void 0 : payload.users) !== null && _d !== void 0 ? _d : [],
27081
+ files: (_e = payload === null || payload === void 0 ? void 0 : payload.files) !== null && _e !== void 0 ? _e : [],
27082
+ communityUsers: (_f = payload === null || payload === void 0 ? void 0 : payload.communityUsers) !== null && _f !== void 0 ? _f : [],
27083
+ };
27084
+ // There is no `comment.declined` event: the existing delete path drops the comment from every
27085
+ // collection and the review queue, and moves no count because a pending comment was never counted.
27086
+ const data = prepareCommentPayload(prepareMembershipPayload(declined, 'communityUsers'));
27087
+ const rejected = data.comments.find(comment => comment.commentId === commentId);
27088
+ const cachedAt = client.cache && Date.now();
27089
+ if (client.cache)
27090
+ ingestInCache(data, { cachedAt });
27091
+ unlinkCommentFromParent(rejected);
27092
+ releasePendingAuthoredComment(rejected);
27093
+ fireEvent('comment.deleted', declined);
27094
+ return {
27095
+ data: LinkedObject.comment(rejected),
27096
+ cachedAt,
27097
+ };
27098
+ };
27099
+ /* end_public_function */
27100
+
26094
27101
  /* begin_public_function
26095
27102
  id: comment.flag
26096
27103
  */
@@ -26176,6 +27183,7 @@ const isCommentFlaggedByMe = async (commentId) => {
26176
27183
  /* end_public_function */
26177
27184
 
26178
27185
  const getComment$1 = async (commentId) => {
27186
+ var _a;
26179
27187
  const client = getActiveClient();
26180
27188
  client.log('comment/getComment', commentId);
26181
27189
  isInTombstone('comment', commentId);
@@ -26186,7 +27194,11 @@ const getComment$1 = async (commentId) => {
26186
27194
  data = response.data;
26187
27195
  }
26188
27196
  catch (error) {
26189
- if (checkIfShouldGoesToTombstone(error === null || error === void 0 ? void 0 : error.code)) {
27197
+ // A pending comment answers 404 to anyone but its author and reviewers. That is a
27198
+ // visibility answer, not a deletion — never tombstone a comment known to be pending.
27199
+ const cached = (_a = pullFromCache(['comment', 'get', commentId])) === null || _a === void 0 ? void 0 : _a.data;
27200
+ const isKnownPending = !!cached && isCommentPending(cached);
27201
+ if (!isKnownPending && checkIfShouldGoesToTombstone(error === null || error === void 0 ? void 0 : error.code)) {
26190
27202
  pushToTombstone('comment', commentId);
26191
27203
  }
26192
27204
  throw error;
@@ -26313,6 +27325,7 @@ const onLocalCommentReactionRemoved = (callback) => {
26313
27325
  */
26314
27326
  const getComment = (commentId, callback) => {
26315
27327
  return liveObject(commentId, callback, 'commentId', getComment$1, [
27328
+ onCommentApproved,
26316
27329
  onCommentDeleteLocal,
26317
27330
  onCommentDeleted,
26318
27331
  onCommentFlagged,
@@ -26326,28 +27339,31 @@ const getComment = (commentId, callback) => {
26326
27339
  };
26327
27340
  /* end_public_function */
26328
27341
 
26329
- class CommentPaginationController extends PaginationController {
26330
- async getRequest(queryParams, token) {
26331
- var _a;
26332
- const { pageSize, limit, includeDeleted } = queryParams, params = __rest(queryParams, ["pageSize", "limit", "includeDeleted"]);
26333
- const effectiveLimit = (_a = pageSize !== null && pageSize !== void 0 ? pageSize : limit) !== null && _a !== void 0 ? _a : COLLECTION_DEFAULT_PAGINATION_LIMIT;
26334
- const baseOptions = {
26335
- type: params.sortBy || queryParams.limit || queryParams.pageSize ? 'pagination' : undefined,
26336
- };
26337
- const options = token
26338
- ? Object.assign(Object.assign({}, baseOptions), { token }) : Object.assign(Object.assign({}, baseOptions), { pageSize: effectiveLimit, limit: effectiveLimit });
26339
- const { data: queryResponse } = await this.http.get(`/api/v3/comments`, {
26340
- params: Object.assign(Object.assign({}, params), { isDeleted: inferIsDeleted(includeDeleted), options }),
26341
- });
26342
- return queryResponse;
26343
- }
26344
- }
27342
+ /**
27343
+ * ```js
27344
+ * import { onCommentCreated } from '@amityco/ts-sdk'
27345
+ * const dispose = onCommentCreated(comment => {
27346
+ * // ...
27347
+ * })
27348
+ * ```
27349
+ *
27350
+ * Fired when a {@link Amity.InternalComment} has been created
27351
+ *
27352
+ * @param callback The function to call when the event was fired
27353
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
27354
+ *
27355
+ * @category Comment Events
27356
+ */
27357
+ const onCommentCreatedLocal = (callback) => {
27358
+ return createLocalCommentEventSubscriber('local.comment.created', callback);
27359
+ };
26345
27360
 
26346
27361
  var EnumCommentActions;
26347
27362
  (function (EnumCommentActions) {
26348
27363
  EnumCommentActions["OnCommentCreated"] = "onCommentCreated";
26349
27364
  EnumCommentActions["OnCommentUpdated"] = "onCommentUpdated";
26350
27365
  EnumCommentActions["OnCommentDeleted"] = "onCommentDeleted";
27366
+ EnumCommentActions["OnCommentApproved"] = "onCommentApproved";
26351
27367
  EnumCommentActions["OnCommentFlagged"] = "onCommentFlagged";
26352
27368
  EnumCommentActions["OnCommentUnflagged"] = "onCommentUnflagged";
26353
27369
  EnumCommentActions["OnCommentReactionAdded"] = "onCommentReactionAdded";
@@ -26370,15 +27386,17 @@ class CommentQueryStreamController extends QueryStreamController {
26370
27386
  }
26371
27387
  appendToQueryStream(response, direction, refresh = false) {
26372
27388
  var _a, _b;
27389
+ // The public list is approved-only even when the server widens the read for a reviewer
27390
+ const commentIds = response.comments.filter(isCommentApproved).map(getResolver('comment'));
26373
27391
  if (refresh) {
26374
27392
  pushToCache(this.cacheKey, {
26375
- data: response.comments.map(getResolver('comment')),
27393
+ data: commentIds,
26376
27394
  });
26377
27395
  }
26378
27396
  else {
26379
27397
  const collection = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
26380
27398
  const comments = (_b = collection === null || collection === void 0 ? void 0 : collection.data) !== null && _b !== void 0 ? _b : [];
26381
- pushToCache(this.cacheKey, Object.assign(Object.assign({}, collection), { data: [...new Set([...comments, ...response.comments.map(getResolver('comment'))])] }));
27399
+ pushToCache(this.cacheKey, Object.assign(Object.assign({}, collection), { data: [...new Set([...comments, ...commentIds])] }));
26382
27400
  }
26383
27401
  }
26384
27402
  reactor(action) {
@@ -26397,8 +27415,19 @@ class CommentQueryStreamController extends QueryStreamController {
26397
27415
  return;
26398
27416
  }
26399
27417
  if (action === EnumCommentActions.OnCommentCreated) {
27418
+ // A pending comment reaches clients over realtime before anyone may see it: persist
27419
+ // only, no insert and no emit. `comment.approved` is the reveal signal.
27420
+ if (!isCommentApproved(comment))
27421
+ return;
27422
+ collection.data = [...new Set([comment.commentId, ...collection.data])];
27423
+ }
27424
+ if (action === EnumCommentActions.OnCommentApproved) {
26400
27425
  collection.data = [...new Set([comment.commentId, ...collection.data])];
26401
27426
  }
27427
+ // An effective edit in a gated community returns the comment to review: it leaves the list
27428
+ if (action === EnumCommentActions.OnCommentUpdated && isCommentPending(comment)) {
27429
+ collection.data = collection.data.filter(commentId => commentId !== comment.commentId);
27430
+ }
26402
27431
  pushToCache(this.cacheKey, collection);
26403
27432
  this.notifyChange({ origin: "event" /* Amity.LiveDataOrigin.EVENT */, loading: false });
26404
27433
  };
@@ -26408,24 +27437,22 @@ class CommentQueryStreamController extends QueryStreamController {
26408
27437
  }
26409
27438
  }
26410
27439
 
26411
- /**
26412
- * ```js
26413
- * import { onCommentCreated } from '@amityco/ts-sdk'
26414
- * const dispose = onCommentCreated(comment => {
26415
- * // ...
26416
- * })
26417
- * ```
26418
- *
26419
- * Fired when a {@link Amity.InternalComment} has been created
26420
- *
26421
- * @param callback The function to call when the event was fired
26422
- * @returns an {@link Amity.Unsubscriber} function to stop listening
26423
- *
26424
- * @category Comment Events
26425
- */
26426
- const onCommentCreatedLocal = (callback) => {
26427
- return createLocalCommentEventSubscriber('local.comment.created', callback);
26428
- };
27440
+ class CommentPaginationController extends PaginationController {
27441
+ async getRequest(queryParams, token) {
27442
+ var _a;
27443
+ const { pageSize, limit, includeDeleted } = queryParams, params = __rest(queryParams, ["pageSize", "limit", "includeDeleted"]);
27444
+ const effectiveLimit = (_a = pageSize !== null && pageSize !== void 0 ? pageSize : limit) !== null && _a !== void 0 ? _a : COLLECTION_DEFAULT_PAGINATION_LIMIT;
27445
+ const baseOptions = {
27446
+ type: params.sortBy || queryParams.limit || queryParams.pageSize ? 'pagination' : undefined,
27447
+ };
27448
+ const options = token
27449
+ ? Object.assign(Object.assign({}, baseOptions), { token }) : Object.assign(Object.assign({}, baseOptions), { pageSize: effectiveLimit, limit: effectiveLimit });
27450
+ const { data: queryResponse } = await this.http.get(`/api/v3/comments`, {
27451
+ params: Object.assign(Object.assign({}, params), { isDeleted: inferIsDeleted(includeDeleted), options }),
27452
+ });
27453
+ return queryResponse;
27454
+ }
27455
+ }
26429
27456
 
26430
27457
  class CommentLiveCollectionController extends LiveCollectionController {
26431
27458
  constructor(query, callback) {
@@ -26461,6 +27488,7 @@ class CommentLiveCollectionController extends LiveCollectionController {
26461
27488
  { fn: onCommentCreated, action: EnumCommentActions.OnCommentCreated },
26462
27489
  { fn: onCommentUpdated, action: EnumCommentActions.OnCommentUpdated },
26463
27490
  { fn: onCommentDeleted, action: EnumCommentActions.OnCommentDeleted },
27491
+ { fn: onCommentApproved, action: EnumCommentActions.OnCommentApproved },
26464
27492
  { fn: onCommentFlagged, action: EnumCommentActions.OnCommentFlagged },
26465
27493
  { fn: onCommentUnflagged, action: EnumCommentActions.OnCommentUnflagged },
26466
27494
  { fn: onCommentReactionAdded, action: EnumCommentActions.OnCommentReactionAdded },
@@ -26494,7 +27522,8 @@ class CommentLiveCollectionController extends LiveCollectionController {
26494
27522
  });
26495
27523
  }
26496
27524
  applyFilter(data) {
26497
- let comments = data;
27525
+ // Approved-only, for every caller including the author of a pending comment
27526
+ let comments = data.filter(isCommentApproved);
26498
27527
  if (!this.query.includeDeleted) {
26499
27528
  comments = filterByPropEquality(comments, 'isDeleted', false);
26500
27529
  }
@@ -26582,7 +27611,202 @@ const getComments = (params, callback, config) => {
26582
27611
  };
26583
27612
  /* end_public_function */
26584
27613
 
26585
- var index$e = /*#__PURE__*/Object.freeze({
27614
+ class CommentReviewQueuePaginationController extends PaginationController {
27615
+ async getRequest(queryParams, token) {
27616
+ const { communityId, limit } = queryParams, params = __rest(queryParams, ["communityId", "limit"]);
27617
+ // The route defaults to 10, not the SDK-wide 20, and rejects anything above 100
27618
+ const effectiveLimit = Math.min(limit !== null && limit !== void 0 ? limit : COMMENT_REVIEW_QUEUE_DEFAULT_PAGINATION_SIZE, COMMENT_REVIEW_QUEUE_MAX_PAGINATION_SIZE);
27619
+ // Token paging only. The route also accepts options[skip], but offset paging over a queue that
27620
+ // drains from the front skips items, so the SDK never sends it.
27621
+ const options = token ? { token } : { limit: effectiveLimit };
27622
+ const { data: queryResponse } = await this.http.get(API_ENDPOINTS.commentReviewQueue(communityId), {
27623
+ params: Object.assign(Object.assign({}, params), { options }),
27624
+ });
27625
+ return queryResponse;
27626
+ }
27627
+ }
27628
+
27629
+ class CommentReviewQueueQueryStreamController extends QueryStreamController {
27630
+ constructor(query, cacheKey, notifyChange) {
27631
+ super(query, cacheKey);
27632
+ this.notifyChange = notifyChange;
27633
+ }
27634
+ async saveToMainDB(response) {
27635
+ const processedPayload = prepareCommentPayload(response);
27636
+ const client = getActiveClient();
27637
+ const cachedAt = client.cache && Date.now();
27638
+ if (client.cache) {
27639
+ ingestInCache(processedPayload, { cachedAt });
27640
+ }
27641
+ }
27642
+ appendToQueryStream(response, direction, refresh = false) {
27643
+ var _a, _b;
27644
+ // Server order is queue-entry order, oldest first. Keep it: later pages append, nothing
27645
+ // re-sorts by createdAt (the two diverge on re-review).
27646
+ const commentIds = response.comments.map(getResolver('comment'));
27647
+ if (refresh) {
27648
+ pushToCache(this.cacheKey, {
27649
+ data: commentIds,
27650
+ });
27651
+ }
27652
+ else {
27653
+ const collection = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
27654
+ const comments = (_b = collection === null || collection === void 0 ? void 0 : collection.data) !== null && _b !== void 0 ? _b : [];
27655
+ pushToCache(this.cacheKey, Object.assign(Object.assign({}, collection), { data: [...new Set([...comments, ...commentIds])] }));
27656
+ }
27657
+ }
27658
+ matchesQuery(comment) {
27659
+ return (comment.targetType === 'community' &&
27660
+ comment.targetId === this.query.communityId &&
27661
+ (!this.query.postId || comment.referenceId === this.query.postId));
27662
+ }
27663
+ reactor(action) {
27664
+ return (comment) => {
27665
+ var _a;
27666
+ const collection = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
27667
+ if (!collection || !this.matchesQuery(comment))
27668
+ return;
27669
+ const isQueued = collection.data.includes(comment.commentId);
27670
+ // A date-filtered queue is the server's to shape; live events only remove from it
27671
+ const acceptsLiveInserts = !this.query.startDate && !this.query.endDate;
27672
+ switch (action) {
27673
+ case EnumCommentActions.OnCommentCreated:
27674
+ case EnumCommentActions.OnCommentUpdated:
27675
+ if (isCommentPending(comment) && !comment.isDeleted) {
27676
+ if (isQueued || !acceptsLiveInserts)
27677
+ return;
27678
+ // Enters (or, after an edit, re-enters) the queue at the end
27679
+ collection.data = [...collection.data, comment.commentId];
27680
+ }
27681
+ else {
27682
+ if (!isQueued)
27683
+ return;
27684
+ collection.data = collection.data.filter(commentId => commentId !== comment.commentId);
27685
+ }
27686
+ break;
27687
+ case EnumCommentActions.OnCommentApproved:
27688
+ case EnumCommentActions.OnCommentDeleted:
27689
+ if (!isQueued)
27690
+ return;
27691
+ collection.data = collection.data.filter(commentId => commentId !== comment.commentId);
27692
+ break;
27693
+ default:
27694
+ return;
27695
+ }
27696
+ pushToCache(this.cacheKey, collection);
27697
+ this.notifyChange({ origin: "event" /* Amity.LiveDataOrigin.EVENT */, loading: false });
27698
+ };
27699
+ }
27700
+ subscribeRTE(createSubscriber) {
27701
+ return createSubscriber.map(subscriber => subscriber.fn(this.reactor(subscriber.action)));
27702
+ }
27703
+ }
27704
+
27705
+ class CommentReviewQueueLiveCollectionController extends LiveCollectionController {
27706
+ constructor(query, callback) {
27707
+ const queryStreamId = hash__default["default"](query);
27708
+ const cacheKey = ['commentReviewQueue', 'collection', queryStreamId];
27709
+ const paginationController = new CommentReviewQueuePaginationController(query);
27710
+ super(paginationController, queryStreamId, cacheKey, callback);
27711
+ this.query = query;
27712
+ this.queryStreamController = new CommentReviewQueueQueryStreamController(this.query, this.cacheKey, this.notifyChange.bind(this));
27713
+ this.callback = callback.bind(this);
27714
+ this.loadPage({ initial: true });
27715
+ }
27716
+ setup() {
27717
+ var _a;
27718
+ const collection = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
27719
+ if (!collection) {
27720
+ pushToCache(this.cacheKey, {
27721
+ data: [],
27722
+ params: {},
27723
+ });
27724
+ }
27725
+ }
27726
+ async persistModel(queryPayload) {
27727
+ await this.queryStreamController.saveToMainDB(queryPayload);
27728
+ }
27729
+ persistQueryStream({ response, direction, refresh, }) {
27730
+ this.queryStreamController.appendToQueryStream(response, direction, refresh);
27731
+ }
27732
+ startSubscription() {
27733
+ // No `comment.declined` exists: a decline arrives as `comment.deleted`
27734
+ return this.queryStreamController.subscribeRTE([
27735
+ { fn: onCommentCreated, action: EnumCommentActions.OnCommentCreated },
27736
+ { fn: onCommentUpdated, action: EnumCommentActions.OnCommentUpdated },
27737
+ { fn: onCommentApproved, action: EnumCommentActions.OnCommentApproved },
27738
+ { fn: onCommentDeleted, action: EnumCommentActions.OnCommentDeleted },
27739
+ { fn: onCommentDeleteLocal, action: EnumCommentActions.OnCommentDeleted },
27740
+ ]);
27741
+ }
27742
+ notifyChange({ origin, loading, error }) {
27743
+ var _a;
27744
+ // A caller who lost REVIEW_COMMUNITY_COMMENT must not keep reading stale pending content
27745
+ if ((error === null || error === void 0 ? void 0 : error.code) === 400301 /* Amity.ServerError.PERMISSION_DENIED */) {
27746
+ pushToCache(this.cacheKey, { data: [], params: {} });
27747
+ }
27748
+ const collection = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
27749
+ if (!collection)
27750
+ return;
27751
+ const data = collection.data
27752
+ .map(id => pullFromCache(['comment', 'get', id]))
27753
+ .filter(isNonNullable)
27754
+ .map(({ data }) => data)
27755
+ .filter(comment => isCommentPending(comment) && !comment.isDeleted)
27756
+ .map(LinkedObject.comment);
27757
+ if (!this.shouldNotify(data) && origin === 'event')
27758
+ return;
27759
+ this.callback({
27760
+ onNextPage: () => this.loadPage({ direction: "next" /* Amity.LiveCollectionPageDirection.NEXT */ }),
27761
+ data,
27762
+ hasNextPage: !!this.paginationController.getNextToken(),
27763
+ loading,
27764
+ error,
27765
+ });
27766
+ }
27767
+ }
27768
+
27769
+ /* begin_public_function
27770
+ id: comment.review_queue
27771
+ */
27772
+ /**
27773
+ * ```js
27774
+ * import { CommentRepository } from '@amityco/ts-sdk'
27775
+ *
27776
+ * const unsub = CommentRepository.getCommentReviewQueue(
27777
+ * { communityId: 'communityId' },
27778
+ * ({ data: comments, hasNextPage, onNextPage }) => {
27779
+ * // every comment is awaiting review
27780
+ * },
27781
+ * )
27782
+ * ```
27783
+ *
27784
+ * Observe the {@link Amity.Comment} list awaiting moderator review in a community, oldest first
27785
+ *
27786
+ * @param params the community to read the queue of, optionally narrowed to one post and a date range
27787
+ * @param callback the function to call when new data are available
27788
+ * @returns An {@link Amity.Unsubscriber} function to run when willing to stop observing the queue
27789
+ *
27790
+ * @category Comments Live Collection
27791
+ */
27792
+ const getCommentReviewQueue = (params, callback) => {
27793
+ const { log, cache } = getActiveClient();
27794
+ if (!cache)
27795
+ console.log(ENABLE_CACHE_MESSAGE);
27796
+ const timestamp = Date.now();
27797
+ log(`getCommentReviewQueue(tmpid: ${timestamp}) > listen`);
27798
+ const commentReviewQueueLiveCollection = new CommentReviewQueueLiveCollectionController(params, callback);
27799
+ const disposers = commentReviewQueueLiveCollection.startSubscription();
27800
+ const cacheKey = commentReviewQueueLiveCollection.getCacheKey();
27801
+ disposers.push(() => dropFromCache(cacheKey));
27802
+ return () => {
27803
+ log(`getCommentReviewQueue(tmpid: ${timestamp}) > dispose`);
27804
+ disposers.forEach(fn => fn());
27805
+ };
27806
+ };
27807
+ /* end_public_function */
27808
+
27809
+ var index$f = /*#__PURE__*/Object.freeze({
26586
27810
  __proto__: null,
26587
27811
  getCommentByIds: getCommentByIds,
26588
27812
  createComment: createComment,
@@ -26590,18 +27814,22 @@ var index$e = /*#__PURE__*/Object.freeze({
26590
27814
  deleteComment: deleteComment,
26591
27815
  softDeleteComment: softDeleteComment,
26592
27816
  hardDeleteComment: hardDeleteComment,
27817
+ approveComment: approveComment,
27818
+ declineComment: declineComment,
26593
27819
  flagComment: flagComment,
26594
27820
  unflagComment: unflagComment,
26595
27821
  isCommentFlaggedByMe: isCommentFlaggedByMe,
26596
27822
  onCommentCreated: onCommentCreated,
26597
27823
  onCommentUpdated: onCommentUpdated,
26598
27824
  onCommentDeleted: onCommentDeleted,
27825
+ onCommentApproved: onCommentApproved,
26599
27826
  onCommentFlagged: onCommentFlagged,
26600
27827
  onCommentUnflagged: onCommentUnflagged,
26601
27828
  onCommentReactionAdded: onCommentReactionAdded,
26602
27829
  onCommentReactionRemoved: onCommentReactionRemoved,
26603
27830
  getComment: getComment,
26604
- getComments: getComments
27831
+ getComments: getComments,
27832
+ getCommentReviewQueue: getCommentReviewQueue
26605
27833
  });
26606
27834
 
26607
27835
  /**
@@ -27851,7 +29079,7 @@ const getForYouFeed = (callback) => {
27851
29079
  };
27852
29080
  /* end_public_function */
27853
29081
 
27854
- var index$d = /*#__PURE__*/Object.freeze({
29082
+ var index$e = /*#__PURE__*/Object.freeze({
27855
29083
  __proto__: null,
27856
29084
  queryGlobalFeed: queryGlobalFeed,
27857
29085
  getCustomRankingGlobalFeed: getCustomRankingGlobalFeed,
@@ -30996,7 +32224,7 @@ const getRooms = (params, callback, config) => {
30996
32224
  };
30997
32225
  /* end_public_function */
30998
32226
 
30999
- var index$c = /*#__PURE__*/Object.freeze({
32227
+ var index$d = /*#__PURE__*/Object.freeze({
31000
32228
  __proto__: null,
31001
32229
  createRoom: createRoom,
31002
32230
  updateRoom: updateRoom,
@@ -31295,7 +32523,7 @@ const getCommunityLiveRoomPosts = (params, callback, config) => {
31295
32523
  };
31296
32524
  /* end_public_function */
31297
32525
 
31298
- var index$b = /*#__PURE__*/Object.freeze({
32526
+ var index$c = /*#__PURE__*/Object.freeze({
31299
32527
  __proto__: null,
31300
32528
  getPostByIds: getPostByIds,
31301
32529
  createPost: createPost,
@@ -31910,7 +33138,7 @@ const getStreams = (params, callback, config) => {
31910
33138
  };
31911
33139
  };
31912
33140
 
31913
- var index$a = /*#__PURE__*/Object.freeze({
33141
+ var index$b = /*#__PURE__*/Object.freeze({
31914
33142
  __proto__: null,
31915
33143
  createStream: createStream,
31916
33144
  updateStream: updateStream,
@@ -32213,7 +33441,7 @@ const stopHeartbeat = (roomId) => {
32213
33441
  };
32214
33442
  /* end_public_function */
32215
33443
 
32216
- var index$9 = /*#__PURE__*/Object.freeze({
33444
+ var index$a = /*#__PURE__*/Object.freeze({
32217
33445
  __proto__: null,
32218
33446
  getRoomOnlineUsers: getRoomOnlineUsers,
32219
33447
  getRoomUserCount: getRoomUserCount,
@@ -32524,7 +33752,7 @@ const getPoll = (pollId, callback) => {
32524
33752
  };
32525
33753
  /* end_public_function */
32526
33754
 
32527
- var index$8 = /*#__PURE__*/Object.freeze({
33755
+ var index$9 = /*#__PURE__*/Object.freeze({
32528
33756
  __proto__: null,
32529
33757
  createPoll: createPoll,
32530
33758
  closePoll: closePoll,
@@ -32828,7 +34056,7 @@ const getPlayer = async (parameters) => {
32828
34056
  return video;
32829
34057
  };
32830
34058
 
32831
- var index$7 = /*#__PURE__*/Object.freeze({
34059
+ var index$8 = /*#__PURE__*/Object.freeze({
32832
34060
  __proto__: null,
32833
34061
  getPlayer: getPlayer
32834
34062
  });
@@ -34001,7 +35229,7 @@ const getGlobalStoryTargets = (params, callback, config) => {
34001
35229
  };
34002
35230
  };
34003
35231
 
34004
- var index$6 = /*#__PURE__*/Object.freeze({
35232
+ var index$7 = /*#__PURE__*/Object.freeze({
34005
35233
  __proto__: null,
34006
35234
  createImageStory: createImageStory,
34007
35235
  createVideoStory: createVideoStory,
@@ -34038,7 +35266,7 @@ const getNetworkAds = async () => {
34038
35266
  };
34039
35267
  };
34040
35268
 
34041
- var index$5 = /*#__PURE__*/Object.freeze({
35269
+ var index$6 = /*#__PURE__*/Object.freeze({
34042
35270
  __proto__: null,
34043
35271
  getNetworkAds: getNetworkAds
34044
35272
  });
@@ -34429,7 +35657,7 @@ const markTraySeen = async (lastSeenAt) => {
34429
35657
  };
34430
35658
  /* end_public_function */
34431
35659
 
34432
- var index$4 = /*#__PURE__*/Object.freeze({
35660
+ var index$5 = /*#__PURE__*/Object.freeze({
34433
35661
  __proto__: null,
34434
35662
  getNotificationTraySeen: getNotificationTraySeen,
34435
35663
  getNotificationTrayItems: getNotificationTrayItems,
@@ -34731,7 +35959,7 @@ const getInvitations = (params, callback) => {
34731
35959
  };
34732
35960
  };
34733
35961
 
34734
- var index$3 = /*#__PURE__*/Object.freeze({
35962
+ var index$4 = /*#__PURE__*/Object.freeze({
34735
35963
  __proto__: null,
34736
35964
  cancelInvitation: cancelInvitation,
34737
35965
  onLocalInvitationCreated: onLocalInvitationCreated,
@@ -34880,7 +36108,7 @@ const getReactions = (postId, callback) => {
34880
36108
  };
34881
36109
  };
34882
36110
 
34883
- var index$2 = /*#__PURE__*/Object.freeze({
36111
+ var index$3 = /*#__PURE__*/Object.freeze({
34884
36112
  __proto__: null,
34885
36113
  createReaction: createReaction,
34886
36114
  onLiveReactionCreated: onLiveReactionCreated,
@@ -35363,7 +36591,7 @@ const getMyEvents = (params, callback, config) => {
35363
36591
  };
35364
36592
  };
35365
36593
 
35366
- var index$1 = /*#__PURE__*/Object.freeze({
36594
+ var index$2 = /*#__PURE__*/Object.freeze({
35367
36595
  __proto__: null,
35368
36596
  createEvent: createEvent,
35369
36597
  updateEvent: updateEvent,
@@ -35590,12 +36818,36 @@ const searchProducts = (params, callback, config) => {
35590
36818
  };
35591
36819
  /* end_public_function */
35592
36820
 
35593
- var index = /*#__PURE__*/Object.freeze({
36821
+ var index$1 = /*#__PURE__*/Object.freeze({
35594
36822
  __proto__: null,
35595
36823
  getProduct: getProduct,
35596
36824
  searchProducts: searchProducts
35597
36825
  });
35598
36826
 
36827
+ const getPool = async (topicId, limit) => {
36828
+ const client = getActiveClient();
36829
+ if (!topicId)
36830
+ throw new ASCError('topicId must not be empty', 800110 /* Amity.ClientError.INVALID_PARAMETERS */, "error" /* Amity.ErrorLevel.ERROR */);
36831
+ if (limit !== undefined && (limit < 1 || limit > 100))
36832
+ throw new ASCError('limit must be between 1 and 100', 800110 /* Amity.ClientError.INVALID_PARAMETERS */, "error" /* Amity.ErrorLevel.ERROR */);
36833
+ const { data } = await client.http.get('/api/v5/curated-content/pool', { params: { topicId, limit } });
36834
+ const { topic } = data, payload = __rest(data, ["topic"]);
36835
+ const processedPayload = preparePostPayload(payload);
36836
+ const cachedAt = client.cache && Date.now();
36837
+ if (client.cache) {
36838
+ ingestInCache(processedPayload, { cachedAt });
36839
+ }
36840
+ return {
36841
+ topic: LinkedObject.curatedContentTopic(topic),
36842
+ posts: processedPayload.posts.map(LinkedObject.post),
36843
+ };
36844
+ };
36845
+
36846
+ var index = /*#__PURE__*/Object.freeze({
36847
+ __proto__: null,
36848
+ getPool: getPool
36849
+ });
36850
+
35599
36851
  /**
35600
36852
  * Helper class for managing product tags per media attachment.
35601
36853
  *
@@ -35644,39 +36896,40 @@ class AmityAttachmentProductTags {
35644
36896
  }
35645
36897
 
35646
36898
  exports.API_REGIONS = API_REGIONS;
35647
- exports.AdRepository = index$5;
36899
+ exports.AdRepository = index$6;
35648
36900
  exports.AmityAttachmentProductTags = AmityAttachmentProductTags;
35649
- exports.CategoryRepository = index$f;
35650
- exports.ChannelRepository = index$j;
35651
- exports.Client = index$s;
35652
- exports.CommentRepository = index$e;
36901
+ exports.CategoryRepository = index$g;
36902
+ exports.ChannelRepository = index$k;
36903
+ exports.Client = index$t;
36904
+ exports.CommentRepository = index$f;
35653
36905
  exports.CommunityPostSettingMaps = CommunityPostSettingMaps;
35654
36906
  exports.CommunityPostSettings = CommunityPostSettings;
35655
- exports.CommunityRepository = index$g;
36907
+ exports.CommunityRepository = index$h;
35656
36908
  exports.ContentFeedType = ContentFeedType;
36909
+ exports.CuratedContentRepository = index;
35657
36910
  exports.DefaultCommunityPostSetting = DefaultCommunityPostSetting;
35658
- exports.EventRepository = index$1;
35659
- exports.FeedRepository = index$d;
35660
- exports.FileRepository = index$p;
36911
+ exports.EventRepository = index$2;
36912
+ exports.FeedRepository = index$e;
36913
+ exports.FileRepository = index$q;
35661
36914
  exports.FileType = FileType;
35662
36915
  exports.GET_WATCHER_URLS = GET_WATCHER_URLS;
35663
- exports.InvitationRepository = index$3;
35664
- exports.LiveReactionRepository = index$2;
35665
- exports.LiveStreamPlayer = index$7;
36916
+ exports.InvitationRepository = index$4;
36917
+ exports.LiveReactionRepository = index$3;
36918
+ exports.LiveStreamPlayer = index$8;
35666
36919
  exports.MessageContentType = MessageContentType;
35667
- exports.MessageRepository = index$n;
35668
- exports.PollRepository = index$8;
36920
+ exports.MessageRepository = index$o;
36921
+ exports.PollRepository = index$9;
35669
36922
  exports.PostContentType = PostContentType;
35670
- exports.PostRepository = index$b;
36923
+ exports.PostRepository = index$c;
35671
36924
  exports.PostStructureType = PostStructureType;
35672
- exports.ProductRepository = index;
35673
- exports.ReactionRepository = index$o;
35674
- exports.RoomPresenceRepository = index$9;
35675
- exports.RoomRepository = index$c;
35676
- exports.StoryRepository = index$6;
35677
- exports.StreamRepository = index$a;
35678
- exports.SubChannelRepository = index$m;
35679
- exports.UserRepository = index$q;
36925
+ exports.ProductRepository = index$1;
36926
+ exports.ReactionRepository = index$p;
36927
+ exports.RoomPresenceRepository = index$a;
36928
+ exports.RoomRepository = index$d;
36929
+ exports.StoryRepository = index$7;
36930
+ exports.StreamRepository = index$b;
36931
+ exports.SubChannelRepository = index$n;
36932
+ exports.UserRepository = index$r;
35680
36933
  exports.VERSION = VERSION;
35681
36934
  exports.VideoResolution = VideoResolution;
35682
36935
  exports.VideoSize = VideoSize;
@@ -35738,7 +36991,7 @@ exports.isPaged = isPaged;
35738
36991
  exports.isReportedByMe = isReportedByMe;
35739
36992
  exports.isSkip = isSkip;
35740
36993
  exports.mergeInCache = mergeInCache;
35741
- exports.notificationTray = index$4;
36994
+ exports.notificationTray = index$5;
35742
36995
  exports.onChannelMarkerFetched = onChannelMarkerFetched;
35743
36996
  exports.onFeedMarkerFetched = onFeedMarkerFetched;
35744
36997
  exports.onFeedMarkerUpdated = onFeedMarkerUpdated;