@amityco/ts-sdk 7.29.0 → 7.29.1-32c8dde1.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 (133) hide show
  1. package/dist/@types/core/errors.d.ts +4 -1
  2. package/dist/@types/core/errors.d.ts.map +1 -1
  3. package/dist/@types/domains/channel.d.ts +13 -1
  4. package/dist/@types/domains/channel.d.ts.map +1 -1
  5. package/dist/@types/domains/content.d.ts +11 -0
  6. package/dist/@types/domains/content.d.ts.map +1 -1
  7. package/dist/@types/domains/user.d.ts +89 -0
  8. package/dist/@types/domains/user.d.ts.map +1 -1
  9. package/dist/channelRepository/observers/getChannels/ChannelLiveCollectionController.d.ts.map +1 -1
  10. package/dist/channelRepository/observers/searchChannels/SearchChannelLiveCollectionController.d.ts.map +1 -1
  11. package/dist/channelRepository/observers/tests/integrations/getChannelsSortByLastMessage.integration.test.d.ts +2 -0
  12. package/dist/channelRepository/observers/tests/integrations/getChannelsSortByLastMessage.integration.test.d.ts.map +1 -0
  13. package/dist/channelRepository/utils/channelActivityOnLoginHandler.d.ts +16 -0
  14. package/dist/channelRepository/utils/channelActivityOnLoginHandler.d.ts.map +1 -0
  15. package/dist/channelRepository/utils/tests/integrations/channelActivity.integration.test.d.ts +2 -0
  16. package/dist/channelRepository/utils/tests/integrations/channelActivity.integration.test.d.ts.map +1 -0
  17. package/dist/channelRepository/utils/tests/updateChannelActivityTimestamps.test.d.ts +2 -0
  18. package/dist/channelRepository/utils/tests/updateChannelActivityTimestamps.test.d.ts.map +1 -0
  19. package/dist/channelRepository/utils/updateChannelActivityTimestamps.d.ts +42 -0
  20. package/dist/channelRepository/utils/updateChannelActivityTimestamps.d.ts.map +1 -0
  21. package/dist/client/api/resumeSession.d.ts.map +1 -1
  22. package/dist/client/api/setupLoginSubscriptions.d.ts.map +1 -1
  23. package/dist/commentRepository/api/flagComment.d.ts +72 -5
  24. package/dist/commentRepository/api/flagComment.d.ts.map +1 -1
  25. package/dist/commentRepository/internalApi/flagCommentWithOptions.d.ts +13 -0
  26. package/dist/commentRepository/internalApi/flagCommentWithOptions.d.ts.map +1 -0
  27. package/dist/commentRepository/internalApi/flagCommentWithReasonString.d.ts +17 -0
  28. package/dist/commentRepository/internalApi/flagCommentWithReasonString.d.ts.map +1 -0
  29. package/dist/commentRepository/internalApi/index.d.ts +2 -0
  30. package/dist/commentRepository/internalApi/index.d.ts.map +1 -1
  31. package/dist/commentRepository/internalApi/tests/integration/flagCommentWithOptions.integration.test.d.ts +2 -0
  32. package/dist/commentRepository/internalApi/tests/integration/flagCommentWithOptions.integration.test.d.ts.map +1 -0
  33. package/dist/commentRepository/internalApi/tests/integration/flagCommentWithReasonString.integration.test.d.ts +2 -0
  34. package/dist/commentRepository/internalApi/tests/integration/flagCommentWithReasonString.integration.test.d.ts.map +1 -0
  35. package/dist/core/microtasks.d.ts.map +1 -1
  36. package/dist/core/query/sorting.d.ts +10 -0
  37. package/dist/core/query/sorting.d.ts.map +1 -1
  38. package/dist/core/query/tests/sorting.test.d.ts +2 -0
  39. package/dist/core/query/tests/sorting.test.d.ts.map +1 -0
  40. package/dist/core/tests/microtasks.test.d.ts +2 -0
  41. package/dist/core/tests/microtasks.test.d.ts.map +1 -0
  42. package/dist/core/transports/tests/integration/serverErrorCodes.integration.test.d.ts +2 -0
  43. package/dist/core/transports/tests/integration/serverErrorCodes.integration.test.d.ts.map +1 -0
  44. package/dist/index.cjs.js +940 -140
  45. package/dist/index.esm.js +941 -142
  46. package/dist/index.umd.js +2 -2
  47. package/dist/messagePreview/utils/getChannelMessagePreviewWithUser.d.ts +1 -0
  48. package/dist/messagePreview/utils/getChannelMessagePreviewWithUser.d.ts.map +1 -1
  49. package/dist/messagePreview/utils/getSubChannelMessagePreviewWithUser.d.ts +1 -1
  50. package/dist/messagePreview/utils/getSubChannelMessagePreviewWithUser.d.ts.map +1 -1
  51. package/dist/messageRepository/api/createMessage.d.ts.map +1 -1
  52. package/dist/messageRepository/api/flagMessage.d.ts +73 -6
  53. package/dist/messageRepository/api/flagMessage.d.ts.map +1 -1
  54. package/dist/messageRepository/api/tests/integration/flagMessage.overloads.integration.test.d.ts +2 -0
  55. package/dist/messageRepository/api/tests/integration/flagMessage.overloads.integration.test.d.ts.map +1 -0
  56. package/dist/messageRepository/internalApi/flagMessageWithOptions.d.ts +13 -0
  57. package/dist/messageRepository/internalApi/flagMessageWithOptions.d.ts.map +1 -0
  58. package/dist/messageRepository/internalApi/flagMessageWithReasonString.d.ts +18 -0
  59. package/dist/messageRepository/internalApi/flagMessageWithReasonString.d.ts.map +1 -0
  60. package/dist/messageRepository/internalApi/tests/integration/flagMessageWithOptions.integration.test.d.ts +2 -0
  61. package/dist/messageRepository/internalApi/tests/integration/flagMessageWithOptions.integration.test.d.ts.map +1 -0
  62. package/dist/messageRepository/internalApi/tests/integration/flagMessageWithReasonString.integration.test.d.ts +2 -0
  63. package/dist/messageRepository/internalApi/tests/integration/flagMessageWithReasonString.integration.test.d.ts.map +1 -0
  64. package/dist/postRepository/api/flagPost.d.ts +73 -6
  65. package/dist/postRepository/api/flagPost.d.ts.map +1 -1
  66. package/dist/postRepository/api/tests/integration/flagPost.overloads.integration.test.d.ts +2 -0
  67. package/dist/postRepository/api/tests/integration/flagPost.overloads.integration.test.d.ts.map +1 -0
  68. package/dist/postRepository/api/tests/integration/setup.d.ts +3 -3
  69. package/dist/postRepository/internalApi/flagPostWithOptions.d.ts +13 -0
  70. package/dist/postRepository/internalApi/flagPostWithOptions.d.ts.map +1 -0
  71. package/dist/postRepository/internalApi/flagPostWithReasonString.d.ts +17 -0
  72. package/dist/postRepository/internalApi/flagPostWithReasonString.d.ts.map +1 -0
  73. package/dist/postRepository/internalApi/index.d.ts +2 -0
  74. package/dist/postRepository/internalApi/index.d.ts.map +1 -1
  75. package/dist/postRepository/internalApi/tests/integration/flagPostWithOptions.integration.test.d.ts +2 -0
  76. package/dist/postRepository/internalApi/tests/integration/flagPostWithOptions.integration.test.d.ts.map +1 -0
  77. package/dist/postRepository/internalApi/tests/integration/flagPostWithReasonString.integration.test.d.ts +2 -0
  78. package/dist/postRepository/internalApi/tests/integration/flagPostWithReasonString.integration.test.d.ts.map +1 -0
  79. package/dist/report/api/isReportedByMe.d.ts.map +1 -1
  80. package/dist/userRepository/api/flagUser.d.ts +50 -9
  81. package/dist/userRepository/api/flagUser.d.ts.map +1 -1
  82. package/dist/userRepository/api/flagUserBehavior.d.ts +29 -0
  83. package/dist/userRepository/api/flagUserBehavior.d.ts.map +1 -0
  84. package/dist/userRepository/api/flagUserProfile.d.ts +29 -0
  85. package/dist/userRepository/api/flagUserProfile.d.ts.map +1 -0
  86. package/dist/userRepository/api/index.d.ts +2 -0
  87. package/dist/userRepository/api/index.d.ts.map +1 -1
  88. package/dist/userRepository/api/isUserFlaggedByMe.d.ts +26 -4
  89. package/dist/userRepository/api/isUserFlaggedByMe.d.ts.map +1 -1
  90. package/dist/userRepository/api/queryBlockedUsers.d.ts.map +1 -1
  91. package/dist/userRepository/api/tests/integration/flagUser.overloads.integration.test.d.ts +2 -0
  92. package/dist/userRepository/api/tests/integration/flagUser.overloads.integration.test.d.ts.map +1 -0
  93. package/dist/userRepository/api/tests/integration/flagUserBehavior.integration.test.d.ts +2 -0
  94. package/dist/userRepository/api/tests/integration/flagUserBehavior.integration.test.d.ts.map +1 -0
  95. package/dist/userRepository/api/tests/integration/flagUserProfile.integration.test.d.ts +2 -0
  96. package/dist/userRepository/api/tests/integration/flagUserProfile.integration.test.d.ts.map +1 -0
  97. package/dist/userRepository/api/unflagUser.d.ts +26 -4
  98. package/dist/userRepository/api/unflagUser.d.ts.map +1 -1
  99. package/dist/userRepository/api/updateUser.d.ts.map +1 -1
  100. package/dist/userRepository/internalApi/getFlagsByMe.d.ts +21 -0
  101. package/dist/userRepository/internalApi/getFlagsByMe.d.ts.map +1 -0
  102. package/dist/userRepository/observers/getBlockedUsers/BlockedUserLiveCollectionController.d.ts +2 -2
  103. package/dist/userRepository/observers/getBlockedUsers/BlockedUserLiveCollectionController.d.ts.map +1 -1
  104. package/dist/userRepository/observers/getBlockingUsers/LiveCollectionController.d.ts +1 -1
  105. package/dist/userRepository/observers/getBlockingUsers/LiveCollectionController.d.ts.map +1 -1
  106. package/dist/userRepository/observers/getUsers/UserLiveCollectionController.d.ts +2 -2
  107. package/dist/userRepository/observers/getUsers/UserLiveCollectionController.d.ts.map +1 -1
  108. package/dist/userRepository/observers/searchUserByDisplayName/SearchUserLiveCollectionController.d.ts +2 -2
  109. package/dist/userRepository/observers/searchUserByDisplayName/SearchUserLiveCollectionController.d.ts.map +1 -1
  110. package/dist/userRepository/utils/submitUserReport.d.ts +10 -0
  111. package/dist/userRepository/utils/submitUserReport.d.ts.map +1 -0
  112. package/dist/userRepository/utils/userReport.d.ts +50 -0
  113. package/dist/userRepository/utils/userReport.d.ts.map +1 -0
  114. package/dist/utils/bloomFilter.d.ts +9 -0
  115. package/dist/utils/bloomFilter.d.ts.map +1 -0
  116. package/dist/utils/contentFlag.d.ts +28 -0
  117. package/dist/utils/contentFlag.d.ts.map +1 -0
  118. package/dist/utils/linkedObject/tests/userLinkedObject.test.d.ts +2 -0
  119. package/dist/utils/linkedObject/tests/userLinkedObject.test.d.ts.map +1 -0
  120. package/dist/utils/linkedObject/userLinkedObject.d.ts.map +1 -1
  121. package/dist/utils/tests/bloomFilter.test.d.ts +2 -0
  122. package/dist/utils/tests/bloomFilter.test.d.ts.map +1 -0
  123. package/dist/utils/tests/client.d.ts +1 -1
  124. package/dist/utils/tests/client.d.ts.map +1 -1
  125. package/dist/utils/tests/dummy/channel.d.ts +13 -9
  126. package/dist/utils/tests/dummy/channel.d.ts.map +1 -1
  127. package/dist/utils/tests/dummy/comment.d.ts +1 -1
  128. package/dist/utils/tests/dummy/community.d.ts +3 -3
  129. package/dist/utils/tests/dummy/message.d.ts +1 -1
  130. package/dist/utils/tests/dummy/token.d.ts +1 -1
  131. package/dist/utils/tests/dummy/user.d.ts +10 -12
  132. package/dist/utils/tests/dummy/user.d.ts.map +1 -1
  133. package/package.json +1 -1
package/dist/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import { encode, decode, btoa, atob } from 'js-base64';
1
+ import { encode, decode, toUint8Array, btoa, atob } from 'js-base64';
2
2
  import mitt from 'mitt';
3
3
  import debug from 'debug';
4
4
  import axios from 'axios';
@@ -42,6 +42,33 @@ var UserReportTypeEnum;
42
42
  UserReportTypeEnum["AVATAR"] = "avatarFileId";
43
43
  UserReportTypeEnum["BEHAVIOR"] = "behavior";
44
44
  })(UserReportTypeEnum || (UserReportTypeEnum = {}));
45
+ /**
46
+ * The profile field a user is reported for in `UserRepository.flagUserProfile`.
47
+ * Each member is the backend `reportType` wire value. There is deliberately no
48
+ * behavior member: behavior reports need a reason and go through
49
+ * `UserRepository.flagUserBehavior`.
50
+ */
51
+ var UserProfileFieldEnum;
52
+ (function (UserProfileFieldEnum) {
53
+ UserProfileFieldEnum["DISPLAY_NAME"] = "displayName";
54
+ UserProfileFieldEnum["DESCRIPTION"] = "description";
55
+ UserProfileFieldEnum["AVATAR"] = "avatarFileId";
56
+ })(UserProfileFieldEnum || (UserProfileFieldEnum = {}));
57
+ /**
58
+ * Selects which of the caller's reports `UserRepository.unflagUser` withdraws, sent as the
59
+ * `reportType` query parameter. The four typed members are the backend `reportType` wire
60
+ * values and match `UserReportTypeEnum`. `UNTYPED` selects the typeless (legacy) report,
61
+ * which `isUserFlaggedByMe` returns as `reportType: null`; it is a selector only, never
62
+ * stored, and the flag endpoint refuses it.
63
+ */
64
+ var UserReportTypeFilterEnum;
65
+ (function (UserReportTypeFilterEnum) {
66
+ UserReportTypeFilterEnum["DISPLAY_NAME"] = "displayName";
67
+ UserReportTypeFilterEnum["DESCRIPTION"] = "description";
68
+ UserReportTypeFilterEnum["AVATAR"] = "avatarFileId";
69
+ UserReportTypeFilterEnum["BEHAVIOR"] = "behavior";
70
+ UserReportTypeFilterEnum["UNTYPED"] = "untyped";
71
+ })(UserReportTypeFilterEnum || (UserReportTypeFilterEnum = {}));
45
72
 
46
73
  const FileType = Object.freeze({
47
74
  FILE: 'file',
@@ -818,6 +845,18 @@ class AmityModuleNotEntitledError extends ASCApiError {
818
845
  this.type = 'AmityModuleNotEntitledError';
819
846
  }
820
847
  }
848
+ /**
849
+ * Input sanitization related error
850
+ * @category Errors
851
+ */
852
+ class ASCInvalidParameterError extends ASCError {
853
+ /**
854
+ * @param message A custom error message
855
+ */
856
+ constructor(message) {
857
+ super(message, 800110 /* Amity.ClientError.INVALID_PARAMETERS */, "error" /* Amity.ErrorLevel.ERROR */);
858
+ }
859
+ }
821
860
 
822
861
  let activeClient = null;
823
862
  /**
@@ -1591,6 +1630,24 @@ const sortByLastUpdated = ({ updatedAt: a = 0 }, { updatedAt: b = 0 }) => new Da
1591
1630
  * Sorting a collection by the items with most recent activity
1592
1631
  */
1593
1632
  const sortByLastActivity = ({ lastActivity: a }, { lastActivity: b }) => new Date(b).valueOf() - new Date(a).valueOf();
1633
+ /**
1634
+ * Sorting a channel collection by the time of its newest message (newest first).
1635
+ * Channels whose `lastMessageAt` is `null` sort last, ordered by `createdAt`
1636
+ * (newest first) among themselves, matching the server's `sortBy=lastMessage`
1637
+ * order so the local sort agrees with the page order.
1638
+ */
1639
+ const sortByLastMessage = (a, b) => {
1640
+ var _a, _b;
1641
+ const aAt = (_a = a.lastMessageAt) !== null && _a !== void 0 ? _a : null;
1642
+ const bAt = (_b = b.lastMessageAt) !== null && _b !== void 0 ? _b : null;
1643
+ if (aAt !== null && bAt !== null)
1644
+ return new Date(bAt).valueOf() - new Date(aAt).valueOf();
1645
+ if (aAt !== null)
1646
+ return -1;
1647
+ if (bAt !== null)
1648
+ return 1;
1649
+ return new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf();
1650
+ };
1594
1651
  /**
1595
1652
  * Filter items by untilAt timestamp based on sort direction
1596
1653
  * - For 'lastCreated' (newest first): untilAt acts as lower bound, excludes items older than boundary
@@ -1675,8 +1732,13 @@ const scheduleTask = (task) => {
1675
1732
  clearTimeout(timer);
1676
1733
  tasks.push(task);
1677
1734
  timer = setTimeout(() => {
1678
- tasks.forEach(fn => fn());
1735
+ // Swap the queue out before running it. A task that schedules another task
1736
+ // while the batch runs (an event handler that fires an event) must land in
1737
+ // a fresh queue and its own timer, not in the array being drained and then
1738
+ // discarded.
1739
+ const batch = tasks;
1679
1740
  tasks = [];
1741
+ batch.forEach(fn => fn());
1680
1742
  }, 0);
1681
1743
  };
1682
1744
 
@@ -1774,18 +1836,163 @@ async function modifyMqttConnection() {
1774
1836
  }
1775
1837
  }
1776
1838
 
1839
+ /**
1840
+ * ```js
1841
+ * import { getFlagsByMe } from '~/userRepository/internalApi/getFlagsByMe'
1842
+ * const { isFlagByMe, flags } = await getFlagsByMe('userId')
1843
+ * ```
1844
+ *
1845
+ * The v4 read behind `user.getUserFlagsByMe()` and the deprecated
1846
+ * `UserRepository.isUserFlaggedByMe`: `GET /api/v4/users/:userId/isFlagByMe`, returned as
1847
+ * received. Nothing is filtered, cached or persisted, and `hashFlag` is not consulted here.
1848
+ * This is the Web counterpart of iOS `ASCUserService.getFlagsByMe` and Android
1849
+ * `UserRepository.getFlagsByMe`, kept apart from the deprecated public function so the
1850
+ * user-object read does not change if that function ever does.
1851
+ *
1852
+ * @param userId The ID of the user whose reports by the caller are read
1853
+ * @returns the caller's {@link Amity.UserFlagByMe} on that user
1854
+ *
1855
+ * @category Private
1856
+ * @async
1857
+ */
1858
+ const getFlagsByMe = async (userId) => {
1859
+ const client = getActiveClient();
1860
+ client.log('user/getFlagsByMe', userId);
1861
+ const { data } = await client.http.get(`/api/v4/users/${encodeURIComponent(userId)}/isFlagByMe`);
1862
+ return data;
1863
+ };
1864
+
1865
+ /* eslint-disable no-bitwise */
1866
+ /**
1867
+ * Client-side test of the `hashFlag` Bloom filter the backend attaches to flaggable objects.
1868
+ *
1869
+ * The filter holds the ids of every user who flagged the object: `bits` is its size, `hashes`
1870
+ * the number of hash functions per id and `hash` the bit array, base64-encoded. This is a port
1871
+ * of the test iOS (`_BloomFilter`) and Android (`EkoBloomFilter`) already run: FNV-1a 32-bit
1872
+ * over the UTF-8 bytes of the id with seeds `0` and `1576284489`, combined by double hashing
1873
+ * (`(a + i·b) mod bits`). A Bloom filter has no false negatives, so `false` is exact; `true`
1874
+ * means "probably", because another id's hashes may have set the same bits. The filter knows
1875
+ * nothing about report types.
1876
+ */
1877
+ const FNV_OFFSET_BASIS = 0x811c9dc5;
1878
+ const FNV_PRIME = 16777619;
1879
+ const SECOND_SEED = 1576284489;
1880
+ /** FNV-1a with the same mixing step and signed-byte handling as the native SDKs. */
1881
+ const fnv1a = (bytes, seed) => {
1882
+ let a = (FNV_OFFSET_BASIS ^ seed) >>> 0;
1883
+ for (let i = 0; i < bytes.length; i += 1) {
1884
+ // Bytes are `char` on iOS and `byte` on Android: both signed, so 0x80..0xff sign-extend.
1885
+ const byte = bytes[i] > 127 ? bytes[i] - 256 : bytes[i];
1886
+ a = Math.imul((a ^ byte) >>> 0, FNV_PRIME) >>> 0;
1887
+ }
1888
+ a = (a + (a << 13)) >>> 0;
1889
+ a = (a ^ (a >>> 7)) >>> 0;
1890
+ a = (a + (a << 3)) >>> 0;
1891
+ a = (a ^ (a >>> 17)) >>> 0;
1892
+ a = (a + (a << 5)) >>> 0;
1893
+ return a;
1894
+ };
1895
+ /** Bit positions for `value`, reproducing the native SDKs' signed 32-bit arithmetic. */
1896
+ const locationsFor = (value, bits, hashes) => {
1897
+ const bytes = toUint8Array(encode(value)); // UTF-8 bytes of the id
1898
+ const a = fnv1a(bytes, 0) | 0;
1899
+ const b = fnv1a(bytes, SECOND_SEED) | 0;
1900
+ const locations = [];
1901
+ let x = a % bits;
1902
+ for (let i = 0; i < hashes; i += 1) {
1903
+ locations.push(x < 0 ? x + bits : x);
1904
+ x = ((x + b) | 0) % bits;
1905
+ }
1906
+ return locations;
1907
+ };
1908
+ /**
1909
+ * Whether `value` is probably in the filter. `false` is exact; `true` may be a false positive.
1910
+ * A missing filter (`null`, the server's value when nobody has flagged the object), a missing
1911
+ * value or a malformed filter all answer `false`.
1912
+ */
1913
+ const testBloomFilter = (hashFlag, value) => {
1914
+ if (!hashFlag || !value)
1915
+ return false;
1916
+ const { bits, hashes, hash } = hashFlag;
1917
+ if (!(bits > 0) || !(hashes > 0) || typeof hash !== 'string')
1918
+ return false;
1919
+ let buckets;
1920
+ try {
1921
+ buckets = toUint8Array(hash);
1922
+ }
1923
+ catch (_a) {
1924
+ return false;
1925
+ }
1926
+ const locations = locationsFor(value, bits, hashes);
1927
+ for (let i = 0; i < locations.length; i += 1) {
1928
+ const index = Math.floor(locations[i] / 8);
1929
+ const bit = 1 << locations[i] % 8;
1930
+ if (index >= buckets.length || (buckets[index] & bit) === 0)
1931
+ return false;
1932
+ }
1933
+ return true;
1934
+ };
1935
+
1936
+ /** The current user's id, or `undefined` when there is no active client (the getter never throws). */
1937
+ const currentUserId = () => {
1938
+ try {
1939
+ return getActiveClient().userId;
1940
+ }
1941
+ catch (_a) {
1942
+ return undefined;
1943
+ }
1944
+ };
1945
+ /**
1946
+ * One `getUserFlagsByMe` closure per userId, remembering the newest user object linked for
1947
+ * that id. Live objects and collections compare snapshots with `isEqual`, which compares
1948
+ * functions by reference: a fresh closure on every linked object would make two reads of the
1949
+ * same user unequal and re-dispatch snapshots that did not change.
1950
+ */
1951
+ const flagsByMeEntries = new Map();
1952
+ /**
1953
+ * A Bloom filter has no false negatives, so `isFlaggedByMe === false` is exact and the request
1954
+ * can be skipped (REQ-012). The newest user object linked for that id is tested, as of its last
1955
+ * update. Without a filter to test, or without a current user, the server is asked.
1956
+ */
1957
+ const knownNotFlaggedByMe = (userId) => {
1958
+ var _a;
1959
+ const me = currentUserId();
1960
+ const user = (_a = flagsByMeEntries.get(userId)) === null || _a === void 0 ? void 0 : _a.latest;
1961
+ if (!me || !user || user.hashFlag === undefined)
1962
+ return false;
1963
+ return !testBloomFilter(user.hashFlag, me);
1964
+ };
1965
+ const getUserFlagsByMeFor = (user) => {
1966
+ let entry = flagsByMeEntries.get(user.userId);
1967
+ if (!entry) {
1968
+ entry = {
1969
+ latest: user,
1970
+ getUserFlagsByMe: async () => {
1971
+ if (knownNotFlaggedByMe(user.userId))
1972
+ return { isFlagByMe: false, flags: [] };
1973
+ return getFlagsByMe(user.userId);
1974
+ },
1975
+ };
1976
+ flagsByMeEntries.set(user.userId, entry);
1977
+ }
1978
+ entry.latest = user;
1979
+ return entry.getUserFlagsByMe;
1980
+ };
1777
1981
  const userLinkedObject = (user) => {
1778
1982
  return Object.assign(Object.assign({}, user), { get avatar() {
1779
1983
  var _a;
1780
1984
  if (!user.avatarFileId)
1781
- return undefined;
1782
- const avatar = (_a = pullFromCache([
1783
- 'file',
1784
- 'get',
1785
- `${user.avatarFileId}`,
1786
- ])) === null || _a === void 0 ? void 0 : _a.data;
1985
+ return null;
1986
+ const avatar = (_a = pullFromCache(['file', 'get', user.avatarFileId])) === null || _a === void 0 ? void 0 : _a.data;
1987
+ if (!avatar)
1988
+ return null;
1787
1989
  return avatar;
1788
- } });
1990
+ },
1991
+ get isFlaggedByMe() {
1992
+ // Local Bloom-filter test on this object's own hashFlag: exact when false, "probably"
1993
+ // when true, type-blind, no request. getUserFlagsByMe() gives the exact, per-type answer.
1994
+ return testBloomFilter(user.hashFlag, currentUserId());
1995
+ }, getUserFlagsByMe: getUserFlagsByMeFor(user) });
1789
1996
  };
1790
1997
 
1791
1998
  /* begin_public_function
@@ -3050,12 +3257,19 @@ const getSubChannelMessagePreview = (subChannelId) => {
3050
3257
  };
3051
3258
 
3052
3259
  const getSubChannelMessagePreviewWithUser = (subChannel) => {
3053
- var _a;
3054
3260
  const messagePreview = subChannel.messagePreviewId
3055
3261
  ? getSubChannelMessagePreview(subChannel.subChannelId)
3056
3262
  : null;
3057
3263
  const messagePreviewWithUser = messagePreview
3058
- ? Object.assign(Object.assign({}, messagePreview), { user: (_a = pullFromCache(['user', 'get', messagePreview === null || messagePreview === void 0 ? void 0 : messagePreview.creatorId])) === null || _a === void 0 ? void 0 : _a.data }) : null;
3264
+ ? Object.assign(Object.assign({}, messagePreview), { user: (() => {
3265
+ var _a;
3266
+ const internalUser = (_a = pullFromCache([
3267
+ 'user',
3268
+ 'get',
3269
+ messagePreview === null || messagePreview === void 0 ? void 0 : messagePreview.creatorId,
3270
+ ])) === null || _a === void 0 ? void 0 : _a.data;
3271
+ return internalUser ? userLinkedObject(internalUser) : undefined;
3272
+ })() }) : null;
3059
3273
  return Object.assign(Object.assign({}, subChannel), { messagePreview: messagePreviewWithUser });
3060
3274
  };
3061
3275
 
@@ -8042,6 +8256,140 @@ const postCommentCountEngineOnLoginHandler = () => onSessionStateChange(state =>
8042
8256
  }
8043
8257
  });
8044
8258
 
8259
+ const BOTH_FIELDS = { lastActivity: true, lastMessageAt: true };
8260
+ /**
8261
+ * The channel cache is keyed by `channelInternalId`, but a message names its
8262
+ * channel by whichever id the producer had: the raw MQTT payload carries the
8263
+ * internal id, a converted `Amity.InternalMessage` carries the public id, and
8264
+ * the optimistic message carries the sub-channel's `channelId`. Try the direct
8265
+ * key first, then match any of the three ids.
8266
+ */
8267
+ const findCachedChannel = (channelId) => {
8268
+ var _a, _b, _c;
8269
+ const direct = (_a = pullFromCache(['channel', 'get', channelId])) === null || _a === void 0 ? void 0 : _a.data;
8270
+ if (direct)
8271
+ return direct;
8272
+ return (_c = (_b = queryCache(['channel', 'get'])) === null || _b === void 0 ? void 0 : _b.find(({ data }) => data.channelInternalId === channelId ||
8273
+ data.channelId === channelId ||
8274
+ data.channelPublicId === channelId)) === null || _c === void 0 ? void 0 : _c.data;
8275
+ };
8276
+ const isNewer = (stored, at) => !stored || convertDateStringToTimestamp(stored) < convertDateStringToTimestamp(at);
8277
+ const isSameInstant = (stored, at) => !!stored && convertDateStringToTimestamp(stored) === convertDateStringToTimestamp(at);
8278
+ /** Pushes the changed fields in one write and emits one `local.channel.updated`. */
8279
+ const commit = (channel, changes) => {
8280
+ if (Object.keys(changes).length === 0)
8281
+ return false;
8282
+ const updated = shallowClone(channel, changes);
8283
+ // Write back under the key the cache actually uses (the resolver key), not
8284
+ // under `channel.channelId`, which can differ from `channelInternalId`.
8285
+ pushToCache(['channel', 'get', getResolver('channel')(updated)], updated);
8286
+ fireEvent('local.channel.updated', {
8287
+ channels: [updated],
8288
+ channelUsers: [],
8289
+ users: [],
8290
+ files: [],
8291
+ });
8292
+ return true;
8293
+ };
8294
+ /**
8295
+ * Single owner of every local write to a channel's ordering timestamps,
8296
+ * `lastActivity` and `lastMessageAt` (ChannelActivityService v1).
8297
+ *
8298
+ * - Monotonic per field: a field moves only when `at` is newer than the stored
8299
+ * value (or the stored value is `null`), so a late or replayed event never
8300
+ * moves a channel down the list.
8301
+ * - One write: both fields are read, compared and pushed in a single cache
8302
+ * update, followed by one `local.channel.updated` event so every open channel
8303
+ * collection re-sorts once.
8304
+ * - Server channel payloads never go through here; they overwrite unguarded.
8305
+ *
8306
+ * @returns `true` when at least one field moved
8307
+ * @category private
8308
+ */
8309
+ const updateChannelActivityTimestamps = (channelId, at, fields = BOTH_FIELDS) => {
8310
+ const client = getActiveClient();
8311
+ if (!client.cache || !channelId || !at)
8312
+ return false;
8313
+ const channel = findCachedChannel(channelId);
8314
+ if (!channel)
8315
+ return false;
8316
+ const changes = {};
8317
+ if (fields.lastActivity && isNewer(channel.lastActivity, at))
8318
+ changes.lastActivity = at;
8319
+ if (fields.lastMessageAt && isNewer(channel.lastMessageAt, at))
8320
+ changes.lastMessageAt = at;
8321
+ return commit(channel, changes);
8322
+ };
8323
+ /**
8324
+ * Local correction for the current user's own send (ChannelActivityService v1
8325
+ * REQ-009, Trigger Matrix row 1b). Once the server confirms the message, each
8326
+ * field that still holds the device-clock `localAt` written at send time is
8327
+ * replaced by the server's `serverAt`, even when that is older. A field a newer
8328
+ * message has already moved is left alone. One write, one change notification,
8329
+ * nothing when neither field still holds `localAt`.
8330
+ *
8331
+ * @returns `true` when at least one field was replaced
8332
+ * @category private
8333
+ */
8334
+ const confirmOwnMessage = (channelId, localAt, serverAt) => {
8335
+ const client = getActiveClient();
8336
+ if (!client.cache || !channelId || !localAt || !serverAt)
8337
+ return false;
8338
+ if (isSameInstant(localAt, serverAt))
8339
+ return false;
8340
+ const channel = findCachedChannel(channelId);
8341
+ if (!channel)
8342
+ return false;
8343
+ const changes = {};
8344
+ if (isSameInstant(channel.lastActivity, localAt))
8345
+ changes.lastActivity = serverAt;
8346
+ if (isSameInstant(channel.lastMessageAt, localAt))
8347
+ changes.lastMessageAt = serverAt;
8348
+ return commit(channel, changes);
8349
+ };
8350
+
8351
+ /**
8352
+ * Session-wide owner of the local `lastActivity` / `lastMessageAt` updates
8353
+ * (ChannelActivityService v1, Web migration rows W1 – W5).
8354
+ *
8355
+ * Registered once per session from `setupLoginSubscriptions` and
8356
+ * `resumeSession`, so the update runs whether or not a channel collection is
8357
+ * open, and exactly once per message however many collections are open.
8358
+ *
8359
+ * It listens to the raw events rather than `onMessageCreatedMqtt`, so it does
8360
+ * not duplicate that subscriber's side effects (mark-read, unread recount).
8361
+ *
8362
+ * @category private
8363
+ */
8364
+ const channelActivityOnLoginHandler = () => {
8365
+ const client = getActiveClient();
8366
+ const disposers = [
8367
+ // Any user's new message, in any message feed of the channel. The raw
8368
+ // payload's `channelId` is the channel's internal id.
8369
+ createEventSubscriber(client, 'channelActivity', 'message.created', payload => {
8370
+ payload.messages.forEach(message => {
8371
+ updateChannelActivityTimestamps(message.channelId, message.createdAt);
8372
+ });
8373
+ }),
8374
+ // The current user's own send fires this three times: with the optimistic
8375
+ // message at send time (Syncing), on the create response (Synced), and on
8376
+ // failure (Error). The device-clock time of the Syncing / Error fires was
8377
+ // already applied synchronously in createMessageOptimistic, and replaying it
8378
+ // here could undo the server correction made on the response
8379
+ // (confirmOwnMessage). Only the server-confirmed fire goes through the guard.
8380
+ createEventSubscriber(client, 'channelActivity', 'local.message.created', payload => {
8381
+ payload.messages.forEach(message => {
8382
+ if (message.syncState !== "synced" /* Amity.SyncState.Synced */)
8383
+ return;
8384
+ updateChannelActivityTimestamps(message.channelId, message.createdAt);
8385
+ });
8386
+ }),
8387
+ ];
8388
+ return () => {
8389
+ disposers.forEach(fn => fn());
8390
+ };
8391
+ };
8392
+
8045
8393
  const EVENTS = [
8046
8394
  'disconnected',
8047
8395
  'error',
@@ -8231,7 +8579,7 @@ const setupLoginSubscriptions = (unsubWatcher) => {
8231
8579
  // NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
8232
8580
  // the channel because currently backend can't handle this, so every time a user is banned from
8233
8581
  // a channel or the channel is deleted the channel's unread count will not be reset to zero
8234
- onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler(), reactionSyncEngineOnLoginHandler(), blockedUserSyncEngineOnLoginHandler(), blockingUserSyncEngineOnLoginHandler(), blockRealtimeOnLoginHandler(), resetAutoSubscriptionsOnLogout(), CursorController.clearOnLogout(), postCommentCountEngineOnLoginHandler());
8582
+ onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler(), reactionSyncEngineOnLoginHandler(), blockedUserSyncEngineOnLoginHandler(), blockingUserSyncEngineOnLoginHandler(), blockRealtimeOnLoginHandler(), resetAutoSubscriptionsOnLogout(), CursorController.clearOnLogout(), postCommentCountEngineOnLoginHandler(), channelActivityOnLoginHandler());
8235
8583
  if (client.useLegacyUnreadCount) {
8236
8584
  subscriptions.push(readReceiptSyncEngineOnLoginHandler());
8237
8585
  }
@@ -9492,7 +9840,7 @@ const resumeSession = async (params, sessionHandler, config) => {
9492
9840
  // NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
9493
9841
  // the channel because currently backend can't handle this, so every time a user is banned from
9494
9842
  // a channel or the channel is deleted the channel's unread count will not be reset to zero
9495
- onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler());
9843
+ onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler(), channelActivityOnLoginHandler());
9496
9844
  if (client.useLegacyUnreadCount) {
9497
9845
  subscriptions$1.push(readReceiptSyncEngineOnLoginHandler());
9498
9846
  }
@@ -13451,7 +13799,7 @@ const getWatchSessionStorage = () => {
13451
13799
  return storageInstance;
13452
13800
  };
13453
13801
 
13454
- 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-----";
13802
+ 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=";
13455
13803
  /*
13456
13804
  * The crypto algorithm used for importing key and signing string
13457
13805
  */
@@ -15226,7 +15574,7 @@ const updateUser = async (userId, patch) => {
15226
15574
  ingestInCache(payload, { cachedAt });
15227
15575
  fireEvent('user.updated', data);
15228
15576
  return {
15229
- data: payload.users.find(user => user.userId === userId),
15577
+ data: userLinkedObject(payload.users.find(user => user.userId === userId)),
15230
15578
  cachedAt,
15231
15579
  };
15232
15580
  };
@@ -15250,18 +15598,18 @@ const buildFlagUserBody = (options) => {
15250
15598
  /**
15251
15599
  * ```js
15252
15600
  * import { UserRepository } from '@amityco/ts-sdk'
15601
+ *
15602
+ * // Typeless report (supported).
15253
15603
  * const flagged = await UserRepository.flagUser('userId')
15254
- * const flaggedBehavior = await UserRepository.flagUser('userId', {
15255
- * reportType: 'behavior',
15256
- * reason: 'Harassment or bullying',
15257
- * comment: 'optional free-text',
15258
- * })
15259
15604
  * ```
15260
15605
  *
15606
+ * Flags a user. Two call shapes are accepted (see the overloads): id-only, which
15607
+ * sends no body and stays supported, and the deprecated structured options object,
15608
+ * whose request mapping is unchanged. New code reporting a profile field or behavior
15609
+ * should use `flagUserProfile` or `flagUserBehavior`.
15610
+ *
15261
15611
  * @param userId The ID of the user to be flagged
15262
- * @param options Optional report metadata forwarded to the server as the request
15263
- * body: `reportType`, `reason`, `comment`. Only non-null fields are included.
15264
- * All validation is server-side; a bodyless call produces a typeless report.
15612
+ * @param options Deprecated report metadata; omit for a typeless report
15265
15613
  * @returns the flagged {@link Amity.User}
15266
15614
  *
15267
15615
  * @category User API
@@ -15278,29 +15626,228 @@ const flagUser = async (userId, options) => {
15278
15626
  ingestInCache(payload);
15279
15627
  }
15280
15628
  fireEvent('user.flagged', data);
15281
- return (_a = payload.users.find(user => user.userId === userId)) !== null && _a !== void 0 ? _a : payload.users[0];
15629
+ return userLinkedObject((_a = payload.users.find(user => user.userId === userId)) !== null && _a !== void 0 ? _a : payload.users[0]);
15282
15630
  };
15283
15631
  /* end_public_function */
15284
15632
 
15633
+ /**
15634
+ * Sends a validated split user report (`flagUserProfile` / `flagUserBehavior`) to the
15635
+ * same endpoint as `flagUser`, then persists the returned user and fires `user.flagged`
15636
+ * exactly as `flagUser` does, so all three calls share return, cache and event behaviour.
15637
+ *
15638
+ * Not exported from the public API.
15639
+ */
15640
+ const submitUserReport = async (apiName, userId, body) => {
15641
+ var _a;
15642
+ const client = getActiveClient();
15643
+ client.log(`user/${apiName}`, { userId, body });
15644
+ const { data } = await client.http.post(`/api/v4/me/flags/${encodeURIComponent(userId)}`, body);
15645
+ const payload = prepareUserPayload(data);
15646
+ if (client.cache) {
15647
+ ingestInCache(payload);
15648
+ }
15649
+ fireEvent('user.flagged', data);
15650
+ return userLinkedObject((_a = payload.users.find(user => user.userId === userId)) !== null && _a !== void 0 ? _a : payload.users[0]);
15651
+ };
15652
+
15653
+ /** Maximum `comment` length, in string length units, after trimming. */
15654
+ const USER_REPORT_COMMENT_MAX_LENGTH = 300;
15655
+ const PROFILE_FIELDS = Object.values(UserProfileFieldEnum);
15656
+ const BEHAVIOR_REASONS = Object.values(ContentFlagReasonEnum);
15657
+ const REPORT_TYPE_FILTERS = Object.values(UserReportTypeFilterEnum);
15658
+ const assertOptionsObject = (apiName, options, shape) => {
15659
+ if (typeof options !== 'object' || options === null || Array.isArray(options)) {
15660
+ throw new ASCInvalidParameterError(`${apiName}: expected an options object \`${shape}\``);
15661
+ }
15662
+ return options;
15663
+ };
15664
+ /**
15665
+ * Normalizes an optional reporter `comment`: it must be a string when supplied (`null`
15666
+ * is not a valid supplied comment); it is trimmed; a blank result is omitted (the
15667
+ * backend rejects `""`); a result longer than 300 characters is rejected before any
15668
+ * request. Otherwise the trimmed text is returned unchanged.
15669
+ *
15670
+ * @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` for a non-string or over-length comment
15671
+ */
15672
+ const normalizeUserReportComment = (apiName, comment) => {
15673
+ if (comment === undefined)
15674
+ return undefined;
15675
+ if (typeof comment !== 'string') {
15676
+ throw new ASCInvalidParameterError(`${apiName}: \`comment\` must be a string when supplied`);
15677
+ }
15678
+ const trimmed = comment.trim();
15679
+ if (trimmed.length === 0)
15680
+ return undefined;
15681
+ if (trimmed.length > USER_REPORT_COMMENT_MAX_LENGTH) {
15682
+ throw new ASCInvalidParameterError(`${apiName}: \`comment\` must be at most ${USER_REPORT_COMMENT_MAX_LENGTH} characters after trimming`);
15683
+ }
15684
+ return trimmed;
15685
+ };
15686
+ const withComment = (body, comment) => comment === undefined ? body : Object.assign(Object.assign({}, body), { comment });
15687
+ /**
15688
+ * Validates `flagUserProfile` options and builds its request body. Untyped callers can
15689
+ * pass anything at runtime, so nothing is trusted from the declared type. Any `reason`
15690
+ * key is ignored and never sent.
15691
+ *
15692
+ * @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when the options are malformed
15693
+ */
15694
+ const toUserProfileReportBody = (options) => {
15695
+ const apiName = 'flagUserProfile';
15696
+ const { field, comment } = assertOptionsObject(apiName, options, '{ field, comment? }');
15697
+ if (typeof field !== 'string' || !PROFILE_FIELDS.includes(field)) {
15698
+ throw new ASCInvalidParameterError(`${apiName}: \`field\` must be a \`UserProfileFieldEnum\` value`);
15699
+ }
15700
+ return withComment({ reportType: field }, normalizeUserReportComment(apiName, comment));
15701
+ };
15702
+ /**
15703
+ * Validates `flagUserBehavior` options and builds its request body. `reason` must be one
15704
+ * of the nine `ContentFlagReasonEnum` values and is sent as that canonical label;
15705
+ * `reportType` is always `behavior`.
15706
+ *
15707
+ * @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when the options are malformed
15708
+ */
15709
+ const toUserBehaviorReportBody = (options) => {
15710
+ const apiName = 'flagUserBehavior';
15711
+ const { reason, comment } = assertOptionsObject(apiName, options, '{ reason, comment? }');
15712
+ if (typeof reason !== 'string' || !BEHAVIOR_REASONS.includes(reason)) {
15713
+ throw new ASCInvalidParameterError(`${apiName}: \`reason\` must be a \`ContentFlagReasonEnum\` value`);
15714
+ }
15715
+ return withComment({ reportType: 'behavior', reason }, normalizeUserReportComment(apiName, comment));
15716
+ };
15717
+ /**
15718
+ * Validates the optional `{ reportType? }` of `unflagUser` and builds its query parameters.
15719
+ * Returns `undefined` when no `reportType` is given, so the request carries no query string
15720
+ * and withdraws every report the caller filed on the user. The server validates the value
15721
+ * too (422); this local check only saves the round-trip for the string alias, which admits
15722
+ * raw strings. Untyped callers can pass anything at runtime, so nothing is trusted from the
15723
+ * declared type.
15724
+ *
15725
+ * @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when the options are malformed or `reportType` is unknown
15726
+ */
15727
+ const toUserReportTypeFilter = (options) => {
15728
+ if (options === undefined)
15729
+ return undefined;
15730
+ const apiName = 'unflagUser';
15731
+ const { reportType } = assertOptionsObject(apiName, options, '{ reportType? }');
15732
+ if (reportType === undefined)
15733
+ return undefined;
15734
+ if (typeof reportType !== 'string' || !REPORT_TYPE_FILTERS.includes(reportType)) {
15735
+ throw new ASCInvalidParameterError(`${apiName}: \`reportType\` must be a \`UserReportTypeFilterEnum\` value`);
15736
+ }
15737
+ return { reportType: reportType };
15738
+ };
15739
+
15740
+ /* begin_public_function
15741
+ id: user.flag_profile
15742
+ */
15743
+ /**
15744
+ * ```js
15745
+ * import { UserRepository, UserProfileFieldEnum } from '@amityco/ts-sdk'
15746
+ * const flagged = await UserRepository.flagUserProfile('userId', {
15747
+ * field: UserProfileFieldEnum.AVATAR,
15748
+ * comment: 'Nudity in profile photo',
15749
+ * })
15750
+ * ```
15751
+ *
15752
+ * Reports one of a user's profile fields (display name, description or avatar). The
15753
+ * call has no reason: profile reports never carry one, so the backend's reason rules
15754
+ * cannot be violated from here. A `reason` key passed at runtime is ignored and never sent.
15755
+ *
15756
+ * `comment` is trimmed; a blank comment is omitted from the request; a comment longer
15757
+ * than 300 characters after trimming is rejected before any request. Otherwise the
15758
+ * trimmed text is sent unchanged.
15759
+ *
15760
+ * Sends `POST /api/v4/me/flags/:userId` with `{ reportType: <field>, comment? }`.
15761
+ *
15762
+ * @param userId The ID of the user to be reported
15763
+ * @param options `field` is a `UserProfileFieldEnum` value; `comment` is optional free text
15764
+ * @returns the flagged {@link Amity.User}
15765
+ * @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when `options` is missing or malformed, or `comment` is over 300 characters, before any request is made
15766
+ *
15767
+ * @category User API
15768
+ * @async
15769
+ * */
15770
+ const flagUserProfile = async (userId, options) => submitUserReport('flagUserProfile', userId, toUserProfileReportBody(options));
15771
+ /* end_public_function */
15772
+
15773
+ /* begin_public_function
15774
+ id: user.flag_behavior
15775
+ */
15776
+ /**
15777
+ * ```js
15778
+ * import { UserRepository, ContentFlagReasonEnum } from '@amityco/ts-sdk'
15779
+ * const flagged = await UserRepository.flagUserBehavior('userId', {
15780
+ * reason: ContentFlagReasonEnum.Others,
15781
+ * comment: 'Impersonating our brand',
15782
+ * })
15783
+ * ```
15784
+ *
15785
+ * Reports a user's behavior. `reason` is required and is one of the nine
15786
+ * `ContentFlagReasonEnum` values, sent as its canonical label. Reporter text, including
15787
+ * the explanation for `Others`, always travels in `comment`; the reason never carries text.
15788
+ *
15789
+ * `comment` is trimmed; a blank comment is omitted from the request; a comment longer
15790
+ * than 300 characters after trimming is rejected before any request. Otherwise the
15791
+ * trimmed text is sent unchanged.
15792
+ *
15793
+ * Sends `POST /api/v4/me/flags/:userId` with `{ reportType: 'behavior', reason, comment? }`.
15794
+ *
15795
+ * @param userId The ID of the user to be reported
15796
+ * @param options `reason` is a `ContentFlagReasonEnum` value; `comment` is optional free text
15797
+ * @returns the flagged {@link Amity.User}
15798
+ * @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when `options` is missing or malformed, `reason` is missing or unknown, or `comment` is over 300 characters, before any request is made
15799
+ *
15800
+ * @category User API
15801
+ * @async
15802
+ * */
15803
+ const flagUserBehavior = async (userId, options) => submitUserReport('flagUserBehavior', userId, toUserBehaviorReportBody(options));
15804
+ /* end_public_function */
15805
+
15285
15806
  /* begin_public_function
15286
15807
  id: user.unflag
15287
15808
  */
15288
15809
  /**
15289
15810
  * ```js
15290
- * import { UserRepository } from '@amityco/ts-sdk'
15291
- * const unflagged = await UserRepository.unflagUser('post', postId)
15811
+ * import { UserRepository, UserReportTypeFilterEnum } from '@amityco/ts-sdk'
15812
+ *
15813
+ * // Withdraw every report I filed on the user.
15814
+ * const unflagged = await UserRepository.unflagUser('userId')
15815
+ *
15816
+ * // Withdraw only my behavior report; my other reports on the user stay.
15817
+ * await UserRepository.unflagUser('userId', { reportType: UserReportTypeFilterEnum.BEHAVIOR })
15818
+ *
15819
+ * // Withdraw only my typeless (legacy) report.
15820
+ * await UserRepository.unflagUser('userId', { reportType: UserReportTypeFilterEnum.UNTYPED })
15292
15821
  * ```
15293
15822
  *
15823
+ * Withdraws the caller's reports on a user. Without `reportType` every report the caller
15824
+ * filed on that user is withdrawn, as before. With `reportType` only the caller's report
15825
+ * of that type is withdrawn; the caller's other reports and other users' reports are
15826
+ * untouched. Withdrawing a report that does not exist succeeds and changes nothing.
15827
+ *
15828
+ * The returned user is persisted to the cache, so its `hashFlag` says whether the caller
15829
+ * still holds any report on the user. Do not flip a local "flagged by me" flag to `false`
15830
+ * after a per-type withdrawal; read the fresh user, or call `isUserFlaggedByMe` for the
15831
+ * remaining per-type state (`flags`).
15832
+ *
15833
+ * Sends `DELETE /api/v4/me/flags/:userId`, with `?reportType=<type>` when one is given.
15834
+ *
15294
15835
  * @param userId The ID of the user to unflag
15295
- * @returns the deleted report result
15836
+ * @param options Optional; `reportType` selects a single report to withdraw. For a report that `isUserFlaggedByMe` lists with `reportType: null`, pass `UserReportTypeFilterEnum.UNTYPED`
15837
+ * @returns `true` once the server has processed the withdrawal
15838
+ * @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when `reportType` is not a `UserReportTypeFilterEnum` value, before any request is made
15296
15839
  *
15297
15840
  * @category User API
15298
15841
  * @async
15299
15842
  * */
15300
- const unflagUser = async (userId) => {
15301
- const client = getActiveClient();
15302
- client.log('user/unflag', userId);
15303
- const { data } = await client.http.delete(`/api/v4/me/flags/${encodeURIComponent(userId)}`);
15843
+ const unflagUser = async (userId, options) => {
15844
+ const client = getActiveClient();
15845
+ client.log('user/unflag', { userId, options });
15846
+ const params = toUserReportTypeFilter(options);
15847
+ const url = `/api/v4/me/flags/${encodeURIComponent(userId)}`;
15848
+ const { data } = params
15849
+ ? await client.http.delete(url, { params })
15850
+ : await client.http.delete(url);
15304
15851
  const payload = prepareUserPayload(data);
15305
15852
  if (client.cache) {
15306
15853
  ingestInCache(payload);
@@ -15316,11 +15863,33 @@ const unflagUser = async (userId) => {
15316
15863
  /**
15317
15864
  * ```js
15318
15865
  * import { UserRepository } from '@amityco/ts-sdk'
15319
- * const isFlagged = await UserRepository.isUserFlaggedByMe(postId)
15866
+ *
15867
+ * const { isFlagByMe, flags } = await UserRepository.isUserFlaggedByMe('userId')
15868
+ *
15869
+ * // Grey out the report options already used; null marks the typeless report.
15870
+ * const used = new Set(flags.map(flag => flag.reportType))
15320
15871
  * ```
15321
15872
  *
15322
- * @param userId The ID of the thing to check a report to.
15323
- * @returns `true` if the report is created by me, `false` if doesn't.
15873
+ * Returns whether the caller has reported a user, with each of the caller's own reports
15874
+ * on that user, exactly as `GET /api/v4/users/:userId/isFlagByMe` answers. Each report
15875
+ * carries `reportType`, `reason`, `comment` and `createdAt`; a field the report does not
15876
+ * have is `null`. `reportType: null` is the typeless (legacy) report, which `unflagUser`
15877
+ * withdraws with `UserReportTypeFilterEnum.UNTYPED`. Only the caller's reports come back.
15878
+ *
15879
+ * This is the only source of per-type state. Nothing is filtered, cached or persisted:
15880
+ * the call reads the server every time and does not consult `hashFlag`, which is a
15881
+ * type-blind bloom filter. Call it again after a flag or unflag when the UI needs the
15882
+ * updated per-type state.
15883
+ *
15884
+ * Breaking change: earlier versions returned a boolean from the v3 endpoint, which the
15885
+ * backend has deprecated. Read `isFlagByMe` on the result for the same answer.
15886
+ *
15887
+ * @deprecated On an {@link Amity.User} you already hold, read `user.isFlaggedByMe` for a
15888
+ * request-free badge and call `user.getUserFlagsByMe()` for the reports; it skips the request
15889
+ * when the Bloom test says the user is not flagged. This function remains the underlying request.
15890
+ *
15891
+ * @param userId The ID of the user to check
15892
+ * @returns the caller's {@link Amity.UserFlagByMe} on that user
15324
15893
  *
15325
15894
  * @category Report API
15326
15895
  * @async
@@ -15328,8 +15897,7 @@ const unflagUser = async (userId) => {
15328
15897
  const isUserFlaggedByMe = async (userId) => {
15329
15898
  const client = getActiveClient();
15330
15899
  client.log('user/isUserFlaggedByMe', userId);
15331
- const { data: { isFlagByMe }, } = await client.http.get(`/api/v3/users/${userId}/isFlagByMe`);
15332
- return isFlagByMe;
15900
+ return getFlagsByMe(userId);
15333
15901
  };
15334
15902
  /* end_public_function */
15335
15903
 
@@ -15579,16 +16147,14 @@ const getUser = (userId, callback) => {
15579
16147
  };
15580
16148
  /* end_public_function */
15581
16149
 
15582
- class UserPaginationController extends PaginationController {
15583
- async getRequest(queryParams, token) {
15584
- const { limit = COLLECTION_DEFAULT_PAGINATION_LIMIT } = queryParams, params = __rest(queryParams, ["limit"]);
15585
- const options = token ? { token } : { limit };
15586
- const { data: queryResponse } = await this.http.get(`/api/v3/users`, {
15587
- params: Object.assign(Object.assign({}, params), { options, isDeleted: false }),
15588
- });
15589
- return queryResponse;
15590
- }
15591
- }
16150
+ var EnumUserActions;
16151
+ (function (EnumUserActions) {
16152
+ EnumUserActions["OnUserDeleted"] = "onUserDeleted";
16153
+ EnumUserActions["OnUserUpdated"] = "onUserUpdated";
16154
+ EnumUserActions["OnUserFlagged"] = "onUserFlagged";
16155
+ EnumUserActions["OnUserUnflagged"] = "onUserUnflagged";
16156
+ EnumUserActions["OnUserFlagCleared"] = "onUserFlagCleared";
16157
+ })(EnumUserActions || (EnumUserActions = {}));
15592
16158
 
15593
16159
  class UserQueryStreamController extends QueryStreamController {
15594
16160
  constructor(query, cacheKey, notifyChange, preparePayload) {
@@ -15636,14 +16202,16 @@ class UserQueryStreamController extends QueryStreamController {
15636
16202
  }
15637
16203
  }
15638
16204
 
15639
- var EnumUserActions;
15640
- (function (EnumUserActions) {
15641
- EnumUserActions["OnUserDeleted"] = "onUserDeleted";
15642
- EnumUserActions["OnUserUpdated"] = "onUserUpdated";
15643
- EnumUserActions["OnUserFlagged"] = "onUserFlagged";
15644
- EnumUserActions["OnUserUnflagged"] = "onUserUnflagged";
15645
- EnumUserActions["OnUserFlagCleared"] = "onUserFlagCleared";
15646
- })(EnumUserActions || (EnumUserActions = {}));
16205
+ class UserPaginationController extends PaginationController {
16206
+ async getRequest(queryParams, token) {
16207
+ const { limit = COLLECTION_DEFAULT_PAGINATION_LIMIT } = queryParams, params = __rest(queryParams, ["limit"]);
16208
+ const options = token ? { token } : { limit };
16209
+ const { data: queryResponse } = await this.http.get(`/api/v3/users`, {
16210
+ params: Object.assign(Object.assign({}, params), { options, isDeleted: false }),
16211
+ });
16212
+ return queryResponse;
16213
+ }
16214
+ }
15647
16215
 
15648
16216
  class UserLiveCollectionController extends LiveCollectionController {
15649
16217
  constructor(query, callback) {
@@ -15760,16 +16328,17 @@ const getUsers = (params, callback, config) => {
15760
16328
  };
15761
16329
  /* end_public_function */
15762
16330
 
15763
- class BlockedUserPaginationController extends PaginationController {
15764
- async getRequest(queryParams, token) {
15765
- const { limit = COLLECTION_DEFAULT_PAGINATION_LIMIT } = queryParams, params = __rest(queryParams, ["limit"]);
15766
- const options = token ? { token } : { limit };
15767
- const { data: queryResponse } = await this.http.get('/api/v4/me/user-blocks', {
15768
- params: Object.assign(Object.assign({}, params), { options, isDeleted: false }),
15769
- });
15770
- return queryResponse;
16331
+ const convertEventPayload = (eventHandler, sourceModelProp, destinationDomain) => (callback) => eventHandler(sourceModel => {
16332
+ var _a;
16333
+ if (!sourceModel) {
16334
+ return sourceModel;
15771
16335
  }
15772
- }
16336
+ const cacheKey = [destinationDomain, 'get', `${sourceModel[sourceModelProp]}`];
16337
+ const model = (_a = pullFromCache(cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
16338
+ if (!model)
16339
+ return;
16340
+ return callback(model);
16341
+ });
15773
16342
 
15774
16343
  var EnumBlockActions;
15775
16344
  (function (EnumBlockActions) {
@@ -15825,17 +16394,16 @@ class BlockedUserQueryStreamController extends QueryStreamController {
15825
16394
  }
15826
16395
  }
15827
16396
 
15828
- const convertEventPayload = (eventHandler, sourceModelProp, destinationDomain) => (callback) => eventHandler(sourceModel => {
15829
- var _a;
15830
- if (!sourceModel) {
15831
- return sourceModel;
16397
+ class BlockedUserPaginationController extends PaginationController {
16398
+ async getRequest(queryParams, token) {
16399
+ const { limit = COLLECTION_DEFAULT_PAGINATION_LIMIT } = queryParams, params = __rest(queryParams, ["limit"]);
16400
+ const options = token ? { token } : { limit };
16401
+ const { data: queryResponse } = await this.http.get('/api/v4/me/user-blocks', {
16402
+ params: Object.assign(Object.assign({}, params), { options, isDeleted: false }),
16403
+ });
16404
+ return queryResponse;
15832
16405
  }
15833
- const cacheKey = [destinationDomain, 'get', `${sourceModel[sourceModelProp]}`];
15834
- const model = (_a = pullFromCache(cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
15835
- if (!model)
15836
- return;
15837
- return callback(model);
15838
- });
16406
+ }
15839
16407
 
15840
16408
  class BlockedUserLiveCollectionController extends LiveCollectionController {
15841
16409
  constructor(query, callback) {
@@ -16145,17 +16713,6 @@ const getBlockingUsers = (callback) => {
16145
16713
  };
16146
16714
  /* end_public_function */
16147
16715
 
16148
- class SearchUserPaginationController extends PaginationController {
16149
- async getRequest(queryParams, token) {
16150
- const { limit = COLLECTION_DEFAULT_PAGINATION_LIMIT, displayName } = queryParams, params = __rest(queryParams, ["limit", "displayName"]);
16151
- const options = token ? { token } : { limit };
16152
- const { data: queryResponse } = await this.http.get(`/api/v3/users`, {
16153
- params: Object.assign(Object.assign({}, params), { keyword: displayName, options, isDeleted: false }),
16154
- });
16155
- return queryResponse;
16156
- }
16157
- }
16158
-
16159
16716
  class SearchUserQueryStreamController extends QueryStreamController {
16160
16717
  constructor(query, cacheKey, notifyChange, preparePayload) {
16161
16718
  super(query, cacheKey);
@@ -16202,6 +16759,17 @@ class SearchUserQueryStreamController extends QueryStreamController {
16202
16759
  }
16203
16760
  }
16204
16761
 
16762
+ class SearchUserPaginationController extends PaginationController {
16763
+ async getRequest(queryParams, token) {
16764
+ const { limit = COLLECTION_DEFAULT_PAGINATION_LIMIT, displayName } = queryParams, params = __rest(queryParams, ["limit", "displayName"]);
16765
+ const options = token ? { token } : { limit };
16766
+ const { data: queryResponse } = await this.http.get(`/api/v3/users`, {
16767
+ params: Object.assign(Object.assign({}, params), { keyword: displayName, options, isDeleted: false }),
16768
+ });
16769
+ return queryResponse;
16770
+ }
16771
+ }
16772
+
16205
16773
  class SearchUserLiveCollectionController extends LiveCollectionController {
16206
16774
  constructor(query, callback) {
16207
16775
  var _a;
@@ -16456,6 +17024,8 @@ var index$r = /*#__PURE__*/Object.freeze({
16456
17024
  getUserByIds: getUserByIds,
16457
17025
  updateUser: updateUser,
16458
17026
  flagUser: flagUser,
17027
+ flagUserProfile: flagUserProfile,
17028
+ flagUserBehavior: flagUserBehavior,
16459
17029
  unflagUser: unflagUser,
16460
17030
  isUserFlaggedByMe: isUserFlaggedByMe,
16461
17031
  getAllBlockedUsers: getAllBlockedUsers,
@@ -18877,7 +19447,8 @@ const getPostReport = async ({ client, referenceId, }) => {
18877
19447
  };
18878
19448
  const getUserReport = async ({ client, referenceId, }) => {
18879
19449
  var _a;
18880
- const { data } = await client.http.get(`/api/v3/users/${referenceId}/isflagbyme`);
19450
+ // v3 isFlagByMe is deprecated by the backend; v4 answers the same boolean plus the caller's flags.
19451
+ const { data } = await client.http.get(`/api/v4/users/${encodeURIComponent(referenceId)}/isFlagByMe`);
18881
19452
  const { result, isFlagByMe } = data !== null && data !== void 0 ? data : {};
18882
19453
  return (_a = result !== null && result !== void 0 ? result : isFlagByMe) !== null && _a !== void 0 ? _a : false;
18883
19454
  };
@@ -19494,7 +20065,7 @@ const getLocalId = () => `LOCAL_${uuid()}`;
19494
20065
  // FIXME: temp solution
19495
20066
  let uniqueId;
19496
20067
  const createMessageOptimistic = (bundle) => {
19497
- var _a, _b;
20068
+ var _a, _b, _c, _d;
19498
20069
  const client = getActiveClient();
19499
20070
  if (!client.cache)
19500
20071
  return;
@@ -19525,9 +20096,20 @@ const createMessageOptimistic = (bundle) => {
19525
20096
  }
19526
20097
  // as reused to update created and updated time, which should be the same
19527
20098
  const createdTime = new Date().toISOString();
19528
- const message = Object.assign({ creatorId: client.userId, creatorPrivateId: getActiveUser()._id, channelSegment: ((_b = subChannel === null || subChannel === void 0 ? void 0 : subChannel.data.messageCount) !== null && _b !== void 0 ? _b : 0) + 1, childrenNumber: 0, createdAt: createdTime, updatedAt: createdTime, syncState: "syncing" /* Amity.SyncState.Syncing */, isDeleted: false }, bundleWithMessageId);
20099
+ const message = Object.assign({
20100
+ // The optimistic message must name its channel (public id) so the own-send
20101
+ // update can move lastActivity / lastMessageAt at send time. Take it from the
20102
+ // cached sub-channel; when that is not cached, resolve the sub-channel id
20103
+ // through the channel cache, which for a channel's default sub-channel is
20104
+ // the channel itself (ChannelActivityService migration row I2). A thread or
20105
+ // unknown channel resolves to undefined, as before.
20106
+ channelId: (_b = subChannel === null || subChannel === void 0 ? void 0 : subChannel.data.channelId) !== null && _b !== void 0 ? _b : (_c = findCachedChannel(bundle.subChannelId)) === null || _c === void 0 ? void 0 : _c.channelId, creatorId: client.userId, creatorPrivateId: getActiveUser()._id, channelSegment: ((_d = subChannel === null || subChannel === void 0 ? void 0 : subChannel.data.messageCount) !== null && _d !== void 0 ? _d : 0) + 1, childrenNumber: 0, createdAt: createdTime, updatedAt: createdTime, syncState: "syncing" /* Amity.SyncState.Syncing */, isDeleted: false }, bundleWithMessageId);
19529
20107
  const cachedAt = UNSYNCED_OBJECT_CACHED_AT_VALUE;
19530
20108
  pushToCache(['message', 'get', message.messageId], message, { cachedAt });
20109
+ // Own-send update (ChannelActivityService row 1): move the channel at send
20110
+ // time, synchronously, so the confirmation on the send response always sees
20111
+ // this value rather than racing the scheduled local event below.
20112
+ updateChannelActivityTimestamps(message.channelId, message.createdAt);
19531
20113
  fireEvent('local.message.created', { messages: [message] });
19532
20114
  return message;
19533
20115
  };
@@ -19567,6 +20149,9 @@ const createMessage = async (bundle) => {
19567
20149
  if (client.cache) {
19568
20150
  ingestInCache(data, { cachedAt });
19569
20151
  }
20152
+ // The own-send update wrote the device-clock createdAt; replace it with the
20153
+ // server's now that the message is confirmed (ChannelActivityService REQ-009).
20154
+ confirmOwnMessage(optimisticData === null || optimisticData === void 0 ? void 0 : optimisticData.channelId, optimisticData === null || optimisticData === void 0 ? void 0 : optimisticData.createdAt, messages[0].createdAt);
19570
20155
  fireEvent('local.message.created', {
19571
20156
  messages: [Object.assign(Object.assign({}, messages[0]), { syncState: "synced" /* Amity.SyncState.Synced */ })],
19572
20157
  });
@@ -20160,23 +20745,85 @@ getDeliveredUsers.locally = (query) => {
20160
20745
  return { data: users, cachedAt, prevPage, nextPage };
20161
20746
  };
20162
20747
 
20163
- /* begin_public_function
20164
- id: message.flag
20165
- */
20748
+ const CONTENT_FLAG_REASONS = Object.values(ContentFlagReasonEnum);
20166
20749
  /**
20167
- * ```js
20168
- * import { MessageRepository } from '@amityco/ts-sdk'
20169
- * const flagged = await MessageRepository.flagMessage(messageId, reason)
20170
- * ```
20750
+ * True when a public `flagX(id, input)` argument is the legacy shape (a reason string
20751
+ * or nothing). Anything else is routed to the `Amity.ContentFlagOptions` path, whose
20752
+ * validation rejects malformed values before any request is made.
20753
+ */
20754
+ const isLegacyContentFlagReason = (input) => input === undefined || typeof input === 'string';
20755
+ /**
20756
+ * Validates an `Amity.ContentFlagOptions` argument. Untyped (plain JS) callers can pass
20757
+ * anything at runtime, so the checks do not trust the declared type: the value must be a
20758
+ * plain object, `reason` must be one of the nine `ContentFlagReasonEnum` values and
20759
+ * `detail`, when supplied, must be a string (`null` is not a valid supplied detail).
20171
20760
  *
20172
- * @param messageId of the message to flag
20173
- * @param reason the reason to flag the message
20174
- * @returns the created report result
20761
+ * @param apiName used in the error message, e.g. `flagPost`
20762
+ * @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when the options are malformed
20763
+ */
20764
+ const validateContentFlagOptions = (apiName, options) => {
20765
+ if (typeof options !== 'object' || options === null || Array.isArray(options)) {
20766
+ throw new ASCInvalidParameterError(`${apiName}: expected an options object \`{ reason, detail? }\``);
20767
+ }
20768
+ const { reason, detail } = options;
20769
+ if (typeof reason !== 'string' || !CONTENT_FLAG_REASONS.includes(reason)) {
20770
+ throw new ASCInvalidParameterError(`${apiName}: \`reason\` must be a \`ContentFlagReasonEnum\` value`);
20771
+ }
20772
+ if (detail !== undefined && typeof detail !== 'string') {
20773
+ throw new ASCInvalidParameterError(`${apiName}: \`detail\` must be a string when supplied`);
20774
+ }
20775
+ return { reason: reason, detail };
20776
+ };
20777
+ /**
20778
+ * Builds the request body for validated options. `detail` is forwarded unchanged (empty
20779
+ * and whitespace-only strings included) and omitted from the body when `undefined`; the
20780
+ * backend enforces the 300-character limit.
20781
+ */
20782
+ const toContentFlagBody = ({ reason, detail }) => detail === undefined ? { reason } : { reason, detail };
20783
+
20784
+ /**
20785
+ * New `flagMessage(messageId, { reason, detail? })` implementation. Any category,
20786
+ * including `Others`, may carry a `detail`. `detail` is forwarded unchanged (empty and
20787
+ * whitespace-only strings included) and omitted from the request body when `undefined`;
20788
+ * the backend enforces the 300-character limit.
20175
20789
  *
20176
- * @category Message API
20177
- * @async
20178
- * */
20179
- const flagMessage = async (messageId, reason) => {
20790
+ * Not exported from the public API. It is reached through the
20791
+ * `MessageRepository.flagMessage(messageId, options)` overload; see `api/flagMessage.ts`.
20792
+ *
20793
+ * @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when `options` is malformed, before any request is made
20794
+ */
20795
+ const flagMessageWithOptions = async (messageId, options) => {
20796
+ // Validate first: an input error must surface as INVALID_PARAMETERS regardless of
20797
+ // whether a client is active, and validation needs nothing from the client.
20798
+ const body = toContentFlagBody(validateContentFlagOptions('flagMessage', options));
20799
+ const client = getActiveClient();
20800
+ client.log('message/flag', messageId);
20801
+ const { data: payload } = await client.http.post(`/api/v5/messages/${encodeURIComponent(messageId)}/flags`, body);
20802
+ if (client.cache) {
20803
+ const messagePayload = await prepareMessagePayload(payload);
20804
+ ingestInCache(messagePayload);
20805
+ }
20806
+ fireEvent('message.flagged', payload);
20807
+ return !!payload;
20808
+ };
20809
+
20810
+ /**
20811
+ * Legacy `flagMessage(messageId, reason?)` implementation, kept verbatim so existing
20812
+ * callers keep the exact request body they had before the options contract existed:
20813
+ *
20814
+ * - predefined `ContentFlagReasonEnum` value → `{ reason: <value>, detail: '' }`
20815
+ * - any other non-empty string (including the literal `'Others'`) → `{ reason: 'Others', detail: <string> }`
20816
+ * - `undefined` or `''` → `{ reason: 'Others', detail: '' }`
20817
+ * (for `undefined` the message body carries `detail: undefined`, which JSON serialization omits)
20818
+ *
20819
+ * Not exported from the public API. It is reached through the deprecated
20820
+ * `MessageRepository.flagMessage(messageId, reason)` overload; see `api/flagMessage.ts`.
20821
+ *
20822
+ * Not itself deprecated: it implements the deprecated legacy-string overload of the public
20823
+ * `flagMessage`, which carries the deprecation tag. New code should use `flagMessageWithOptions`
20824
+ * (the `{ reason, detail? }` contract). This path stays available throughout the current major version.
20825
+ */
20826
+ const flagMessageWithReasonString = async (messageId, reason) => {
20180
20827
  const client = getActiveClient();
20181
20828
  client.log('message/flag', messageId);
20182
20829
  const isPredefinedReason = reason &&
@@ -20193,6 +20840,42 @@ const flagMessage = async (messageId, reason) => {
20193
20840
  fireEvent('message.flagged', payload);
20194
20841
  return !!payload;
20195
20842
  };
20843
+
20844
+ /* begin_public_function
20845
+ id: message.flag
20846
+ */
20847
+ /**
20848
+ * ```js
20849
+ * import { MessageRepository, ContentFlagReasonEnum } from '@amityco/ts-sdk'
20850
+ *
20851
+ * // No reason.
20852
+ * await MessageRepository.flagMessage(messageId)
20853
+ *
20854
+ * // Category plus optional reporter context (any category, including Others).
20855
+ * await MessageRepository.flagMessage(messageId, {
20856
+ * reason: ContentFlagReasonEnum.SpamOrScams,
20857
+ * detail: 'Repeated promotional links',
20858
+ * })
20859
+ * ```
20860
+ *
20861
+ * Reports a message to moderation. Three call shapes are accepted (see the overloads):
20862
+ * id-only, an options object, and the deprecated legacy reason string. The public
20863
+ * name only dispatches on the input shape; the two implementations are kept separate
20864
+ * so the legacy path is untouched by the new contract:
20865
+ *
20866
+ * - `undefined` or a string → `flagMessageWithReasonString` (unchanged legacy mapping)
20867
+ * - anything else → `flagMessageWithOptions` (validated `{ reason, detail? }`)
20868
+ *
20869
+ * @param messageId The ID of the message to flag
20870
+ * @param input an options object `{ reason, detail? }`, a legacy reason string, or nothing
20871
+ * @returns `true` when the report was created
20872
+ *
20873
+ * @category Message API
20874
+ * @async
20875
+ * */
20876
+ const flagMessage = (messageId, input) => isLegacyContentFlagReason(input)
20877
+ ? flagMessageWithReasonString(messageId, input)
20878
+ : flagMessageWithOptions(messageId, input);
20196
20879
  /* end_public_function */
20197
20880
 
20198
20881
  /* begin_public_function
@@ -21912,6 +22595,9 @@ class ChannelLiveCollectionController extends LiveCollectionController {
21912
22595
  case 'lastCreated':
21913
22596
  channels = channels.sort(sortByLastCreated);
21914
22597
  break;
22598
+ case 'lastMessage':
22599
+ channels = channels.sort(sortByLastMessage);
22600
+ break;
21915
22601
  case 'displayName':
21916
22602
  /*
21917
22603
  * The server returns channels with empty | null displayName's first before
@@ -21972,9 +22658,8 @@ class ChannelLiveCollectionController extends LiveCollectionController {
21972
22658
  objectResolverEngine.resolve(message.channelId, "channel" /* Amity.ReferenceType.CHANNEL */);
21973
22659
  }
21974
22660
  else {
21975
- updateChannelCache(channelCache, {
21976
- lastActivity: message.createdAt,
21977
- });
22661
+ // lastActivity / lastMessageAt are moved by channelActivityOnLoginHandler
22662
+ // (session-wide, monotonic, one write). This listener only re-sorts.
21978
22663
  callback(message);
21979
22664
  }
21980
22665
  });
@@ -21992,9 +22677,7 @@ class ChannelLiveCollectionController extends LiveCollectionController {
21992
22677
  ])) === null || _a === void 0 ? void 0 : _a.data;
21993
22678
  if (!cacheData)
21994
22679
  return;
21995
- updateChannelCache(cacheData, {
21996
- lastActivity: message.createdAt,
21997
- });
22680
+ // Timestamps are moved by channelActivityOnLoginHandler; only re-sort here.
21998
22681
  callback(message);
21999
22682
  });
22000
22683
  }, 'channelId', 'channel'),
@@ -22732,9 +23415,7 @@ class SearchChannelLiveCollectionController extends LiveCollectionController {
22732
23415
  ])) === null || _a === void 0 ? void 0 : _a.data;
22733
23416
  if (!cacheData)
22734
23417
  return;
22735
- updateChannelCache(cacheData, {
22736
- lastActivity: message.createdAt,
22737
- });
23418
+ // Timestamps are moved by channelActivityOnLoginHandler; only re-sort here.
22738
23419
  callback(message);
22739
23420
  });
22740
23421
  }, 'channelId', 'channel'),
@@ -22751,9 +23432,7 @@ class SearchChannelLiveCollectionController extends LiveCollectionController {
22751
23432
  ])) === null || _a === void 0 ? void 0 : _a.data;
22752
23433
  if (!cacheData)
22753
23434
  return;
22754
- updateChannelCache(cacheData, {
22755
- lastActivity: message.createdAt,
22756
- });
23435
+ // Timestamps are moved by channelActivityOnLoginHandler; only re-sort here.
22757
23436
  callback(message);
22758
23437
  });
22759
23438
  }, 'channelId', 'channel'),
@@ -27338,23 +28017,22 @@ const declineComment = async (commentId, reason) => {
27338
28017
  };
27339
28018
  /* end_public_function */
27340
28019
 
27341
- /* begin_public_function
27342
- id: comment.flag
27343
- */
27344
28020
  /**
27345
- * ```js
27346
- * import { CommentRepository } from '@amityco/ts-sdk'
27347
- * const flagged = await CommentRepository.flagComment(commentId, reason)
27348
- * ```
28021
+ * Legacy `flagComment(commentId, reason?)` implementation, kept verbatim so existing
28022
+ * callers keep the exact request body they had before the options contract existed:
27349
28023
  *
27350
- * @param commentId The ID of the comment to flag
27351
- * @param reason the reason to flag the comment
27352
- * @returns the created report result
28024
+ * - predefined `ContentFlagReasonEnum` value → `{ reason: <value>, detail: '' }`
28025
+ * - any other non-empty string (including the literal `'Others'`) → `{ reason: 'Others', detail: <string> }`
28026
+ * - `undefined` or `''` → `{ reason: 'Others', detail: '' }`
27353
28027
  *
27354
- * @category Comment API
27355
- * @async
27356
- * */
27357
- const flagComment = async (commentId, reason) => {
28028
+ * Not exported from the public API. It is reached through the deprecated
28029
+ * `CommentRepository.flagComment(commentId, reason)` overload; see `api/flagComment.ts`.
28030
+ *
28031
+ * Not itself deprecated: it implements the deprecated legacy-string overload of the public
28032
+ * `flagComment`, which carries the deprecation tag. New code should use `flagCommentWithOptions`
28033
+ * (the `{ reason, detail? }` contract). This path stays available throughout the current major version.
28034
+ */
28035
+ const flagCommentWithReasonString = async (commentId, reason) => {
27358
28036
  const client = getActiveClient();
27359
28037
  client.log('comment/flagComment', commentId);
27360
28038
  const isPredefinedReason = reason &&
@@ -27370,6 +28048,67 @@ const flagComment = async (commentId, reason) => {
27370
28048
  fireEvent('comment.flagged', payload);
27371
28049
  return !!payload;
27372
28050
  };
28051
+
28052
+ /**
28053
+ * New `flagComment(commentId, { reason, detail? })` implementation. Any category,
28054
+ * including `Others`, may carry a `detail`. `detail` is forwarded unchanged (empty and
28055
+ * whitespace-only strings included) and omitted from the request body when `undefined`;
28056
+ * the backend enforces the 300-character limit.
28057
+ *
28058
+ * Not exported from the public API. It is reached through the
28059
+ * `CommentRepository.flagComment(commentId, options)` overload; see `api/flagComment.ts`.
28060
+ *
28061
+ * @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when `options` is malformed, before any request is made
28062
+ */
28063
+ const flagCommentWithOptions = async (commentId, options) => {
28064
+ // Validate first: an input error must surface as INVALID_PARAMETERS regardless of
28065
+ // whether a client is active, and validation needs nothing from the client.
28066
+ const body = toContentFlagBody(validateContentFlagOptions('flagComment', options));
28067
+ const client = getActiveClient();
28068
+ client.log('comment/flagComment', commentId);
28069
+ const { data: payload } = await client.http.post(`/api/v3/comments/${encodeURIComponent(commentId)}/flag`, body);
28070
+ if (client.cache) {
28071
+ ingestInCache(payload);
28072
+ }
28073
+ fireEvent('comment.flagged', payload);
28074
+ return !!payload;
28075
+ };
28076
+
28077
+ /* begin_public_function
28078
+ id: comment.flag
28079
+ */
28080
+ /**
28081
+ * ```js
28082
+ * import { CommentRepository, ContentFlagReasonEnum } from '@amityco/ts-sdk'
28083
+ *
28084
+ * // No reason: sent as Others with an empty detail.
28085
+ * await CommentRepository.flagComment(commentId)
28086
+ *
28087
+ * // Category plus optional reporter context (any category, including Others).
28088
+ * await CommentRepository.flagComment(commentId, {
28089
+ * reason: ContentFlagReasonEnum.SpamOrScams,
28090
+ * detail: 'Repeated promotional links',
28091
+ * })
28092
+ * ```
28093
+ *
28094
+ * Reports a comment to moderation. Three call shapes are accepted (see the overloads):
28095
+ * id-only, an options object, and the deprecated legacy reason string. The public
28096
+ * name only dispatches on the input shape; the two implementations are kept separate
28097
+ * so the legacy path is untouched by the new contract:
28098
+ *
28099
+ * - `undefined` or a string → `flagCommentWithReasonString` (unchanged legacy mapping)
28100
+ * - anything else → `flagCommentWithOptions` (validated `{ reason, detail? }`)
28101
+ *
28102
+ * @param commentId The ID of the comment to flag
28103
+ * @param input an options object `{ reason, detail? }`, a legacy reason string, or nothing
28104
+ * @returns `true` when the report was created
28105
+ *
28106
+ * @category Comment API
28107
+ * @async
28108
+ * */
28109
+ const flagComment = (commentId, input) => isLegacyContentFlagReason(input)
28110
+ ? flagCommentWithReasonString(commentId, input)
28111
+ : flagCommentWithOptions(commentId, input);
27373
28112
  /* end_public_function */
27374
28113
 
27375
28114
  /* begin_public_function
@@ -29723,23 +30462,47 @@ const declinePost = async (postId) => {
29723
30462
  };
29724
30463
  /* end_public_function */
29725
30464
 
29726
- /* begin_public_function
29727
- id: post.flag
29728
- */
29729
30465
  /**
29730
- * ```js
29731
- * import { PostRepository } from '@amityco/ts-sdk'
29732
- * const flagged = await PostRepository.flagPost(postId, reason)
29733
- * ```
30466
+ * New `flagPost(postId, { reason, detail? })` implementation. Any category, including
30467
+ * `Others`, may carry a `detail`. `detail` is forwarded unchanged (empty and
30468
+ * whitespace-only strings included) and omitted from the request body when `undefined`;
30469
+ * the backend enforces the 300-character limit.
29734
30470
  *
29735
- * @param postId of the post to flag
29736
- * @param reason the reason to flag the post
29737
- * @returns a boolean
30471
+ * Not exported from the public API. It is reached through the
30472
+ * `PostRepository.flagPost(postId, options)` overload; see `api/flagPost.ts`.
29738
30473
  *
29739
- * @category Post API
29740
- * @async
29741
- * */
29742
- const flagPost = async (postId, reason) => {
30474
+ * @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when `options` is malformed, before any request is made
30475
+ */
30476
+ const flagPostWithOptions = async (postId, options) => {
30477
+ // Validate first: an input error must surface as INVALID_PARAMETERS regardless of
30478
+ // whether a client is active, and validation needs nothing from the client.
30479
+ const body = toContentFlagBody(validateContentFlagOptions('flagPost', options));
30480
+ const client = getActiveClient();
30481
+ client.log('post/flagPost', postId);
30482
+ const { data: payload } = await client.http.post(`/api/v3/posts/${encodeURIComponent(postId)}/flag`, body);
30483
+ if (client.cache) {
30484
+ ingestInCache(prepareMembershipPayload(payload, 'communityUsers'));
30485
+ }
30486
+ fireEvent('post.flagged', payload);
30487
+ return !!payload;
30488
+ };
30489
+
30490
+ /**
30491
+ * Legacy `flagPost(postId, reason?)` implementation, kept verbatim so existing
30492
+ * callers keep the exact request body they had before the options contract existed:
30493
+ *
30494
+ * - predefined `ContentFlagReasonEnum` value → `{ reason: <value>, detail: '' }`
30495
+ * - any other non-empty string (including the literal `'Others'`) → `{ reason: 'Others', detail: <string> }`
30496
+ * - `undefined` or `''` → `{ reason: 'Others', detail: '' }`
30497
+ *
30498
+ * Not exported from the public API. It is reached through the deprecated
30499
+ * `PostRepository.flagPost(postId, reason)` overload; see `api/flagPost.ts`.
30500
+ *
30501
+ * Not itself deprecated: it implements the deprecated legacy-string overload of the public
30502
+ * `flagPost`, which carries the deprecation tag. New code should use `flagPostWithOptions`
30503
+ * (the `{ reason, detail? }` contract). This path stays available throughout the current major version.
30504
+ */
30505
+ const flagPostWithReasonString = async (postId, reason) => {
29743
30506
  const client = getActiveClient();
29744
30507
  client.log('post/flagPost', postId);
29745
30508
  const isPredefinedReason = reason &&
@@ -29755,6 +30518,42 @@ const flagPost = async (postId, reason) => {
29755
30518
  fireEvent('post.flagged', payload);
29756
30519
  return !!payload;
29757
30520
  };
30521
+
30522
+ /* begin_public_function
30523
+ id: post.flag
30524
+ */
30525
+ /**
30526
+ * ```js
30527
+ * import { PostRepository, ContentFlagReasonEnum } from '@amityco/ts-sdk'
30528
+ *
30529
+ * // No reason.
30530
+ * await PostRepository.flagPost(postId)
30531
+ *
30532
+ * // Category plus optional reporter context (any category, including Others).
30533
+ * await PostRepository.flagPost(postId, {
30534
+ * reason: ContentFlagReasonEnum.SpamOrScams,
30535
+ * detail: 'Repeated promotional links',
30536
+ * })
30537
+ * ```
30538
+ *
30539
+ * Reports a post to moderation. Three call shapes are accepted (see the overloads):
30540
+ * id-only, an options object, and the deprecated legacy reason string. The public
30541
+ * name only dispatches on the input shape; the two implementations are kept separate
30542
+ * so the legacy path is untouched by the new contract:
30543
+ *
30544
+ * - `undefined` or a string → `flagPostWithReasonString` (unchanged legacy mapping)
30545
+ * - anything else → `flagPostWithOptions` (validated `{ reason, detail? }`)
30546
+ *
30547
+ * @param postId The ID of the post to flag
30548
+ * @param input an options object `{ reason, detail? }`, a legacy reason string, or nothing
30549
+ * @returns `true` when the report was created
30550
+ *
30551
+ * @category Post API
30552
+ * @async
30553
+ * */
30554
+ const flagPost = (postId, input) => isLegacyContentFlagReason(input)
30555
+ ? flagPostWithReasonString(postId, input)
30556
+ : flagPostWithOptions(postId, input);
29758
30557
  /* end_public_function */
29759
30558
 
29760
30559
  /* begin_public_function
@@ -37135,4 +37934,4 @@ class AmityAttachmentProductTags {
37135
37934
  }
37136
37935
  }
37137
37936
 
37138
- export { API_REGIONS, index$6 as AdRepository, AmityAttachmentProductTags, AmityAutoSubscription, AmityChannelNotificationModeEnum, AmityCommunityType, AmityEventOrderOption, AmityEventOriginType, AmityEventResponseStatus, AmityEventSortOption, AmityEventStatus, AmityEventType, AmityLiveViewerCountMode, AmityModuleNotEntitledError, AmitySharableContentType, AnalyticsSourceTypeEnum, index$g as CategoryRepository, index$k as ChannelRepository, index$t as Client, index$f as CommentRepository, CommunityNotificationEventNameEnum, CommunityPostSettingMaps, CommunityPostSettings, index$h as CommunityRepository, ContentFeedType, ContentFlagReasonEnum, index as CuratedContentRepository, DefaultCommunityPostSetting, index$2 as EventRepository, FeedDataTypeEnum, index$e as FeedRepository, FeedSortByEnum, FeedSourceEnum, FeedTypeEnum, FileAccessTypeEnum, index$q as FileRepository, FileType, GET_WATCHER_URLS, index$4 as InvitationRepository, InvitationSortByEnum, InvitationStatusEnum, InvitationTargetTypeEnum, InvitationTypeEnum, JoinRequestStatusEnum, JoinResultStatusEnum, index$3 as LiveReactionRepository, index$8 as LiveStreamPlayer, MembershipAcceptanceTypeEnum, MessageContentType, index$o as MessageRepository, NotificationRolesFilterTypeEnum, NotificationSettingsLevelEnum, index$9 as PollRepository, PostContentType, index$c as PostRepository, PostStructureType, index$1 as ProductRepository, index$p as ReactionRepository, index$a as RoomPresenceRepository, index$d as RoomRepository, SearchUsersByEnum, index$7 as StoryRepository, index$b as StreamRepository, index$n as SubChannelRepository, SubscriptionLevels, UserNotificationModuleNameEnum, UserReportTypeEnum, index$r as UserRepository, UserTypeEnum, VERSION, VideoResolution, VideoSize, VideoTranscodingStatus, backupCache, createQuery, createReport, createUserToken, deleteReport, disableCache, dropFromCache, enableCache, exceedsUntilAtBoundary, filterByChannelMembership, filterByCommunityMembership, filterByFeedType, filterByPostDataTypes, filterByPropEquality, filterByPropInclusion, filterByPropIntersection, filterBySearchTerm, filterByStringComparePartially, filterByUntilAt, filterCommentsByBlockUsers, filterPostsByBlockUsers, getBlockUsersId, getChannelTopic, getCommentTopic, getCommunityStoriesTopic, getCommunityTopic, getLiveReactionTopic, getLiveStreamTopic, getMarkedMessageTopic, getMarkerUserFeedTopic, getMessageTopic, getMyFollowersTopic, getMyFollowingsTopic, getNetworkId, getNetworkTopic, getPostTopic, getRole, getRoomStreamerTopic, getRoomWatcherTopic, getSmartFeedChannelTopic, getSmartFeedMessageTopic, getSmartFeedSubChannelTopic, getStoryTopic, getSubChannelTopic, getUserTopic, isAfterBefore, isAfterBeforeRaw, isCachable, isFetcher, isFresh, isLocal, isMutator, isOffline, isPaged, isReportedByMe, isSkip, mergeInCache, index$5 as notificationTray, onChannelMarkerFetched, onFeedMarkerFetched, onFeedMarkerUpdated, onMessageMarked, onMessageMarkerFetched, onSubChannelMarkerFetched, onSubChannelMarkerUpdated, onUserMarkerFetched, onUserMarkerFetchedLegacy, pullFromCache, pushToCache, queryCache, queryOptions, queryRoles, resolveModuleAvailability, restoreCache, runQuery, sortByChannelSegment, sortByDisplayName, sortByFirstCreated, sortByFirstUpdated, sortByLastActivity, sortByLastCreated, sortByLastUpdated, sortByLocalSortingDate, sortByName, sortBySegmentNumber, subscribeTopic, toPage, toPageRaw, toToken, upsertInCache, validateUntilAt, wipeCache };
37937
+ export { API_REGIONS, index$6 as AdRepository, AmityAttachmentProductTags, AmityAutoSubscription, AmityChannelNotificationModeEnum, AmityCommunityType, AmityEventOrderOption, AmityEventOriginType, AmityEventResponseStatus, AmityEventSortOption, AmityEventStatus, AmityEventType, AmityLiveViewerCountMode, AmityModuleNotEntitledError, AmitySharableContentType, AnalyticsSourceTypeEnum, index$g as CategoryRepository, index$k as ChannelRepository, index$t as Client, index$f as CommentRepository, CommunityNotificationEventNameEnum, CommunityPostSettingMaps, CommunityPostSettings, index$h as CommunityRepository, ContentFeedType, ContentFlagReasonEnum, index as CuratedContentRepository, DefaultCommunityPostSetting, index$2 as EventRepository, FeedDataTypeEnum, index$e as FeedRepository, FeedSortByEnum, FeedSourceEnum, FeedTypeEnum, FileAccessTypeEnum, index$q as FileRepository, FileType, GET_WATCHER_URLS, index$4 as InvitationRepository, InvitationSortByEnum, InvitationStatusEnum, InvitationTargetTypeEnum, InvitationTypeEnum, JoinRequestStatusEnum, JoinResultStatusEnum, index$3 as LiveReactionRepository, index$8 as LiveStreamPlayer, MembershipAcceptanceTypeEnum, MessageContentType, index$o as MessageRepository, NotificationRolesFilterTypeEnum, NotificationSettingsLevelEnum, index$9 as PollRepository, PostContentType, index$c as PostRepository, PostStructureType, index$1 as ProductRepository, index$p as ReactionRepository, index$a as RoomPresenceRepository, index$d as RoomRepository, SearchUsersByEnum, index$7 as StoryRepository, index$b as StreamRepository, index$n as SubChannelRepository, SubscriptionLevels, UserNotificationModuleNameEnum, UserProfileFieldEnum, UserReportTypeEnum, UserReportTypeFilterEnum, index$r as UserRepository, UserTypeEnum, VERSION, VideoResolution, VideoSize, VideoTranscodingStatus, backupCache, createQuery, createReport, createUserToken, deleteReport, disableCache, dropFromCache, enableCache, exceedsUntilAtBoundary, filterByChannelMembership, filterByCommunityMembership, filterByFeedType, filterByPostDataTypes, filterByPropEquality, filterByPropInclusion, filterByPropIntersection, filterBySearchTerm, filterByStringComparePartially, filterByUntilAt, filterCommentsByBlockUsers, filterPostsByBlockUsers, getBlockUsersId, getChannelTopic, getCommentTopic, getCommunityStoriesTopic, getCommunityTopic, getLiveReactionTopic, getLiveStreamTopic, getMarkedMessageTopic, getMarkerUserFeedTopic, getMessageTopic, getMyFollowersTopic, getMyFollowingsTopic, getNetworkId, getNetworkTopic, getPostTopic, getRole, getRoomStreamerTopic, getRoomWatcherTopic, getSmartFeedChannelTopic, getSmartFeedMessageTopic, getSmartFeedSubChannelTopic, getStoryTopic, getSubChannelTopic, getUserTopic, isAfterBefore, isAfterBeforeRaw, isCachable, isFetcher, isFresh, isLocal, isMutator, isOffline, isPaged, isReportedByMe, isSkip, mergeInCache, index$5 as notificationTray, onChannelMarkerFetched, onFeedMarkerFetched, onFeedMarkerUpdated, onMessageMarked, onMessageMarkerFetched, onSubChannelMarkerFetched, onSubChannelMarkerUpdated, onUserMarkerFetched, onUserMarkerFetchedLegacy, pullFromCache, pushToCache, queryCache, queryOptions, queryRoles, resolveModuleAvailability, restoreCache, runQuery, sortByChannelSegment, sortByDisplayName, sortByFirstCreated, sortByFirstUpdated, sortByLastActivity, sortByLastCreated, sortByLastMessage, sortByLastUpdated, sortByLocalSortingDate, sortByName, sortBySegmentNumber, subscribeTopic, toPage, toPageRaw, toToken, upsertInCache, validateUntilAt, wipeCache };