@amityco/ts-sdk 7.29.0 → 7.29.1-0bbc9cd8.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.
- package/dist/@types/core/errors.d.ts +4 -1
- package/dist/@types/core/errors.d.ts.map +1 -1
- package/dist/@types/domains/channel.d.ts +13 -1
- package/dist/@types/domains/channel.d.ts.map +1 -1
- package/dist/@types/domains/content.d.ts +11 -0
- package/dist/@types/domains/content.d.ts.map +1 -1
- package/dist/@types/domains/event.d.ts +1 -1
- package/dist/@types/domains/event.d.ts.map +1 -1
- package/dist/@types/domains/group.d.ts +2 -2
- package/dist/@types/domains/group.d.ts.map +1 -1
- package/dist/@types/domains/user.d.ts +95 -0
- package/dist/@types/domains/user.d.ts.map +1 -1
- package/dist/channelRepository/events/onUserDeleted.d.ts.map +1 -1
- package/dist/channelRepository/observers/getChannels/ChannelLiveCollectionController.d.ts.map +1 -1
- package/dist/channelRepository/observers/searchChannels/SearchChannelLiveCollectionController.d.ts.map +1 -1
- package/dist/channelRepository/observers/tests/integrations/getChannelsSortByLastMessage.integration.test.d.ts +2 -0
- package/dist/channelRepository/observers/tests/integrations/getChannelsSortByLastMessage.integration.test.d.ts.map +1 -0
- package/dist/channelRepository/utils/channelActivityOnLoginHandler.d.ts +16 -0
- package/dist/channelRepository/utils/channelActivityOnLoginHandler.d.ts.map +1 -0
- package/dist/channelRepository/utils/tests/integrations/channelActivity.integration.test.d.ts +2 -0
- package/dist/channelRepository/utils/tests/integrations/channelActivity.integration.test.d.ts.map +1 -0
- package/dist/channelRepository/utils/tests/updateChannelActivityTimestamps.test.d.ts +2 -0
- package/dist/channelRepository/utils/tests/updateChannelActivityTimestamps.test.d.ts.map +1 -0
- package/dist/channelRepository/utils/updateChannelActivityTimestamps.d.ts +42 -0
- package/dist/channelRepository/utils/updateChannelActivityTimestamps.d.ts.map +1 -0
- package/dist/client/api/resumeSession.d.ts.map +1 -1
- package/dist/client/api/setupLoginSubscriptions.d.ts.map +1 -1
- package/dist/commentRepository/api/flagComment.d.ts +72 -5
- package/dist/commentRepository/api/flagComment.d.ts.map +1 -1
- package/dist/commentRepository/internalApi/flagCommentWithOptions.d.ts +13 -0
- package/dist/commentRepository/internalApi/flagCommentWithOptions.d.ts.map +1 -0
- package/dist/commentRepository/internalApi/flagCommentWithReasonString.d.ts +17 -0
- package/dist/commentRepository/internalApi/flagCommentWithReasonString.d.ts.map +1 -0
- package/dist/commentRepository/internalApi/index.d.ts +2 -0
- package/dist/commentRepository/internalApi/index.d.ts.map +1 -1
- package/dist/commentRepository/internalApi/tests/integration/flagCommentWithOptions.integration.test.d.ts +2 -0
- package/dist/commentRepository/internalApi/tests/integration/flagCommentWithOptions.integration.test.d.ts.map +1 -0
- package/dist/commentRepository/internalApi/tests/integration/flagCommentWithReasonString.integration.test.d.ts +2 -0
- package/dist/commentRepository/internalApi/tests/integration/flagCommentWithReasonString.integration.test.d.ts.map +1 -0
- package/dist/communityRepository/communityMembership/events/onUserDeleted.d.ts.map +1 -1
- package/dist/communityRepository/utils/payload.d.ts.map +1 -1
- package/dist/core/microtasks.d.ts.map +1 -1
- package/dist/core/query/sorting.d.ts +10 -0
- package/dist/core/query/sorting.d.ts.map +1 -1
- package/dist/core/query/tests/sorting.test.d.ts +2 -0
- package/dist/core/query/tests/sorting.test.d.ts.map +1 -0
- package/dist/core/tests/microtasks.test.d.ts +2 -0
- package/dist/core/tests/microtasks.test.d.ts.map +1 -0
- package/dist/core/transports/tests/integration/serverErrorCodes.integration.test.d.ts +2 -0
- package/dist/core/transports/tests/integration/serverErrorCodes.integration.test.d.ts.map +1 -0
- package/dist/group/utils/withUser.d.ts +3 -3
- package/dist/group/utils/withUser.d.ts.map +1 -1
- package/dist/index.cjs.js +953 -148
- package/dist/index.esm.js +954 -150
- package/dist/index.umd.js +2 -2
- package/dist/messagePreview/utils/getChannelMessagePreviewWithUser.d.ts +1 -0
- package/dist/messagePreview/utils/getChannelMessagePreviewWithUser.d.ts.map +1 -1
- package/dist/messagePreview/utils/getSubChannelMessagePreviewWithUser.d.ts +1 -1
- package/dist/messagePreview/utils/getSubChannelMessagePreviewWithUser.d.ts.map +1 -1
- package/dist/messageRepository/api/createMessage.d.ts.map +1 -1
- package/dist/messageRepository/api/flagMessage.d.ts +73 -6
- package/dist/messageRepository/api/flagMessage.d.ts.map +1 -1
- package/dist/messageRepository/api/tests/integration/flagMessage.overloads.integration.test.d.ts +2 -0
- package/dist/messageRepository/api/tests/integration/flagMessage.overloads.integration.test.d.ts.map +1 -0
- package/dist/messageRepository/internalApi/flagMessageWithOptions.d.ts +13 -0
- package/dist/messageRepository/internalApi/flagMessageWithOptions.d.ts.map +1 -0
- package/dist/messageRepository/internalApi/flagMessageWithReasonString.d.ts +18 -0
- package/dist/messageRepository/internalApi/flagMessageWithReasonString.d.ts.map +1 -0
- package/dist/messageRepository/internalApi/tests/integration/flagMessageWithOptions.integration.test.d.ts +2 -0
- package/dist/messageRepository/internalApi/tests/integration/flagMessageWithOptions.integration.test.d.ts.map +1 -0
- package/dist/messageRepository/internalApi/tests/integration/flagMessageWithReasonString.integration.test.d.ts +2 -0
- package/dist/messageRepository/internalApi/tests/integration/flagMessageWithReasonString.integration.test.d.ts.map +1 -0
- package/dist/postRepository/api/flagPost.d.ts +73 -6
- package/dist/postRepository/api/flagPost.d.ts.map +1 -1
- package/dist/postRepository/api/tests/integration/flagPost.overloads.integration.test.d.ts +2 -0
- package/dist/postRepository/api/tests/integration/flagPost.overloads.integration.test.d.ts.map +1 -0
- package/dist/postRepository/api/tests/integration/setup.d.ts +3 -3
- package/dist/postRepository/internalApi/flagPostWithOptions.d.ts +13 -0
- package/dist/postRepository/internalApi/flagPostWithOptions.d.ts.map +1 -0
- package/dist/postRepository/internalApi/flagPostWithReasonString.d.ts +17 -0
- package/dist/postRepository/internalApi/flagPostWithReasonString.d.ts.map +1 -0
- package/dist/postRepository/internalApi/index.d.ts +2 -0
- package/dist/postRepository/internalApi/index.d.ts.map +1 -1
- package/dist/postRepository/internalApi/tests/integration/flagPostWithOptions.integration.test.d.ts +2 -0
- package/dist/postRepository/internalApi/tests/integration/flagPostWithOptions.integration.test.d.ts.map +1 -0
- package/dist/postRepository/internalApi/tests/integration/flagPostWithReasonString.integration.test.d.ts +2 -0
- package/dist/postRepository/internalApi/tests/integration/flagPostWithReasonString.integration.test.d.ts.map +1 -0
- package/dist/postRepository/utils/payload.d.ts.map +1 -1
- package/dist/report/api/isReportedByMe.d.ts.map +1 -1
- package/dist/userRepository/api/flagUser.d.ts +50 -9
- package/dist/userRepository/api/flagUser.d.ts.map +1 -1
- package/dist/userRepository/api/flagUserBehavior.d.ts +29 -0
- package/dist/userRepository/api/flagUserBehavior.d.ts.map +1 -0
- package/dist/userRepository/api/flagUserProfile.d.ts +29 -0
- package/dist/userRepository/api/flagUserProfile.d.ts.map +1 -0
- package/dist/userRepository/api/index.d.ts +2 -0
- package/dist/userRepository/api/index.d.ts.map +1 -1
- package/dist/userRepository/api/isUserFlaggedByMe.d.ts +26 -4
- package/dist/userRepository/api/isUserFlaggedByMe.d.ts.map +1 -1
- package/dist/userRepository/api/queryBlockedUsers.d.ts.map +1 -1
- package/dist/userRepository/api/tests/integration/flagUser.overloads.integration.test.d.ts +2 -0
- package/dist/userRepository/api/tests/integration/flagUser.overloads.integration.test.d.ts.map +1 -0
- package/dist/userRepository/api/tests/integration/flagUserBehavior.integration.test.d.ts +2 -0
- package/dist/userRepository/api/tests/integration/flagUserBehavior.integration.test.d.ts.map +1 -0
- package/dist/userRepository/api/tests/integration/flagUserProfile.integration.test.d.ts +2 -0
- package/dist/userRepository/api/tests/integration/flagUserProfile.integration.test.d.ts.map +1 -0
- package/dist/userRepository/api/unflagUser.d.ts +26 -4
- package/dist/userRepository/api/unflagUser.d.ts.map +1 -1
- package/dist/userRepository/api/updateUser.d.ts.map +1 -1
- package/dist/userRepository/internalApi/getFlagsByMe.d.ts +21 -0
- package/dist/userRepository/internalApi/getFlagsByMe.d.ts.map +1 -0
- package/dist/userRepository/observers/getBlockedUsers/BlockedUserLiveCollectionController.d.ts +2 -2
- package/dist/userRepository/observers/getBlockedUsers/BlockedUserLiveCollectionController.d.ts.map +1 -1
- package/dist/userRepository/observers/getBlockingUsers/LiveCollectionController.d.ts +1 -1
- package/dist/userRepository/observers/getBlockingUsers/LiveCollectionController.d.ts.map +1 -1
- package/dist/userRepository/observers/getUsers/UserLiveCollectionController.d.ts +2 -2
- package/dist/userRepository/observers/getUsers/UserLiveCollectionController.d.ts.map +1 -1
- package/dist/userRepository/observers/searchUserByDisplayName/SearchUserLiveCollectionController.d.ts +2 -2
- package/dist/userRepository/observers/searchUserByDisplayName/SearchUserLiveCollectionController.d.ts.map +1 -1
- package/dist/userRepository/observers/searchUserByDisplayName.d.ts +1 -1
- package/dist/userRepository/observers/searchUserByDisplayName.d.ts.map +1 -1
- package/dist/userRepository/observers/searchUsers.d.ts +1 -1
- package/dist/userRepository/observers/searchUsers.d.ts.map +1 -1
- package/dist/userRepository/utils/submitUserReport.d.ts +10 -0
- package/dist/userRepository/utils/submitUserReport.d.ts.map +1 -0
- package/dist/userRepository/utils/userReport.d.ts +50 -0
- package/dist/userRepository/utils/userReport.d.ts.map +1 -0
- package/dist/utils/bloomFilter.d.ts +9 -0
- package/dist/utils/bloomFilter.d.ts.map +1 -0
- package/dist/utils/contentFlag.d.ts +28 -0
- package/dist/utils/contentFlag.d.ts.map +1 -0
- package/dist/utils/linkedObject/tests/userLinkedObject.test.d.ts +2 -0
- package/dist/utils/linkedObject/tests/userLinkedObject.test.d.ts.map +1 -0
- package/dist/utils/linkedObject/userLinkedObject.d.ts.map +1 -1
- package/dist/utils/tests/bloomFilter.test.d.ts +2 -0
- package/dist/utils/tests/bloomFilter.test.d.ts.map +1 -0
- package/dist/utils/tests/client.d.ts +1 -1
- package/dist/utils/tests/client.d.ts.map +1 -1
- package/dist/utils/tests/dummy/channel.d.ts +31 -10
- package/dist/utils/tests/dummy/channel.d.ts.map +1 -1
- package/dist/utils/tests/dummy/comment.d.ts +1 -1
- package/dist/utils/tests/dummy/community.d.ts +2 -2
- package/dist/utils/tests/dummy/community.d.ts.map +1 -1
- package/dist/utils/tests/dummy/message.d.ts +1 -1
- package/dist/utils/tests/dummy/token.d.ts +1 -1
- package/dist/utils/tests/dummy/user.d.ts +10 -12
- package/dist/utils/tests/dummy/user.d.ts.map +1 -1
- 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
|
-
|
|
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,167 @@ 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
|
|
1782
|
-
const avatar = (_a = pullFromCache([
|
|
1783
|
-
|
|
1784
|
-
|
|
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
|
+
},
|
|
1996
|
+
/* begin_public_function
|
|
1997
|
+
id: user.get_flags_by_me
|
|
1998
|
+
*/
|
|
1999
|
+
getUserFlagsByMe: getUserFlagsByMeFor(user) });
|
|
1789
2000
|
};
|
|
1790
2001
|
|
|
1791
2002
|
/* begin_public_function
|
|
@@ -2862,7 +3073,8 @@ const convertRawMembershipToMembership = (member) => {
|
|
|
2862
3073
|
if (!userCache) {
|
|
2863
3074
|
return undefined;
|
|
2864
3075
|
}
|
|
2865
|
-
|
|
3076
|
+
// Every user the SDK hands out is a linked object (isFlaggedByMe, getUserFlagsByMe, avatar).
|
|
3077
|
+
return userLinkedObject(userCache);
|
|
2866
3078
|
} });
|
|
2867
3079
|
};
|
|
2868
3080
|
/**
|
|
@@ -2896,7 +3108,7 @@ const withUser = (member, userIdProp = 'userId', userProp = 'user') => {
|
|
|
2896
3108
|
if (!userCache) {
|
|
2897
3109
|
return undefined;
|
|
2898
3110
|
}
|
|
2899
|
-
return userCache;
|
|
3111
|
+
return userLinkedObject(userCache);
|
|
2900
3112
|
} }, member);
|
|
2901
3113
|
};
|
|
2902
3114
|
/**
|
|
@@ -3050,12 +3262,19 @@ const getSubChannelMessagePreview = (subChannelId) => {
|
|
|
3050
3262
|
};
|
|
3051
3263
|
|
|
3052
3264
|
const getSubChannelMessagePreviewWithUser = (subChannel) => {
|
|
3053
|
-
var _a;
|
|
3054
3265
|
const messagePreview = subChannel.messagePreviewId
|
|
3055
3266
|
? getSubChannelMessagePreview(subChannel.subChannelId)
|
|
3056
3267
|
: null;
|
|
3057
3268
|
const messagePreviewWithUser = messagePreview
|
|
3058
|
-
? Object.assign(Object.assign({}, messagePreview), { user: (
|
|
3269
|
+
? Object.assign(Object.assign({}, messagePreview), { user: (() => {
|
|
3270
|
+
var _a;
|
|
3271
|
+
const internalUser = (_a = pullFromCache([
|
|
3272
|
+
'user',
|
|
3273
|
+
'get',
|
|
3274
|
+
messagePreview === null || messagePreview === void 0 ? void 0 : messagePreview.creatorId,
|
|
3275
|
+
])) === null || _a === void 0 ? void 0 : _a.data;
|
|
3276
|
+
return internalUser ? userLinkedObject(internalUser) : undefined;
|
|
3277
|
+
})() }) : null;
|
|
3059
3278
|
return Object.assign(Object.assign({}, subChannel), { messagePreview: messagePreviewWithUser });
|
|
3060
3279
|
};
|
|
3061
3280
|
|
|
@@ -8042,6 +8261,140 @@ const postCommentCountEngineOnLoginHandler = () => onSessionStateChange(state =>
|
|
|
8042
8261
|
}
|
|
8043
8262
|
});
|
|
8044
8263
|
|
|
8264
|
+
const BOTH_FIELDS = { lastActivity: true, lastMessageAt: true };
|
|
8265
|
+
/**
|
|
8266
|
+
* The channel cache is keyed by `channelInternalId`, but a message names its
|
|
8267
|
+
* channel by whichever id the producer had: the raw MQTT payload carries the
|
|
8268
|
+
* internal id, a converted `Amity.InternalMessage` carries the public id, and
|
|
8269
|
+
* the optimistic message carries the sub-channel's `channelId`. Try the direct
|
|
8270
|
+
* key first, then match any of the three ids.
|
|
8271
|
+
*/
|
|
8272
|
+
const findCachedChannel = (channelId) => {
|
|
8273
|
+
var _a, _b, _c;
|
|
8274
|
+
const direct = (_a = pullFromCache(['channel', 'get', channelId])) === null || _a === void 0 ? void 0 : _a.data;
|
|
8275
|
+
if (direct)
|
|
8276
|
+
return direct;
|
|
8277
|
+
return (_c = (_b = queryCache(['channel', 'get'])) === null || _b === void 0 ? void 0 : _b.find(({ data }) => data.channelInternalId === channelId ||
|
|
8278
|
+
data.channelId === channelId ||
|
|
8279
|
+
data.channelPublicId === channelId)) === null || _c === void 0 ? void 0 : _c.data;
|
|
8280
|
+
};
|
|
8281
|
+
const isNewer = (stored, at) => !stored || convertDateStringToTimestamp(stored) < convertDateStringToTimestamp(at);
|
|
8282
|
+
const isSameInstant = (stored, at) => !!stored && convertDateStringToTimestamp(stored) === convertDateStringToTimestamp(at);
|
|
8283
|
+
/** Pushes the changed fields in one write and emits one `local.channel.updated`. */
|
|
8284
|
+
const commit = (channel, changes) => {
|
|
8285
|
+
if (Object.keys(changes).length === 0)
|
|
8286
|
+
return false;
|
|
8287
|
+
const updated = shallowClone(channel, changes);
|
|
8288
|
+
// Write back under the key the cache actually uses (the resolver key), not
|
|
8289
|
+
// under `channel.channelId`, which can differ from `channelInternalId`.
|
|
8290
|
+
pushToCache(['channel', 'get', getResolver('channel')(updated)], updated);
|
|
8291
|
+
fireEvent('local.channel.updated', {
|
|
8292
|
+
channels: [updated],
|
|
8293
|
+
channelUsers: [],
|
|
8294
|
+
users: [],
|
|
8295
|
+
files: [],
|
|
8296
|
+
});
|
|
8297
|
+
return true;
|
|
8298
|
+
};
|
|
8299
|
+
/**
|
|
8300
|
+
* Single owner of every local write to a channel's ordering timestamps,
|
|
8301
|
+
* `lastActivity` and `lastMessageAt` (ChannelActivityService v1).
|
|
8302
|
+
*
|
|
8303
|
+
* - Monotonic per field: a field moves only when `at` is newer than the stored
|
|
8304
|
+
* value (or the stored value is `null`), so a late or replayed event never
|
|
8305
|
+
* moves a channel down the list.
|
|
8306
|
+
* - One write: both fields are read, compared and pushed in a single cache
|
|
8307
|
+
* update, followed by one `local.channel.updated` event so every open channel
|
|
8308
|
+
* collection re-sorts once.
|
|
8309
|
+
* - Server channel payloads never go through here; they overwrite unguarded.
|
|
8310
|
+
*
|
|
8311
|
+
* @returns `true` when at least one field moved
|
|
8312
|
+
* @category private
|
|
8313
|
+
*/
|
|
8314
|
+
const updateChannelActivityTimestamps = (channelId, at, fields = BOTH_FIELDS) => {
|
|
8315
|
+
const client = getActiveClient();
|
|
8316
|
+
if (!client.cache || !channelId || !at)
|
|
8317
|
+
return false;
|
|
8318
|
+
const channel = findCachedChannel(channelId);
|
|
8319
|
+
if (!channel)
|
|
8320
|
+
return false;
|
|
8321
|
+
const changes = {};
|
|
8322
|
+
if (fields.lastActivity && isNewer(channel.lastActivity, at))
|
|
8323
|
+
changes.lastActivity = at;
|
|
8324
|
+
if (fields.lastMessageAt && isNewer(channel.lastMessageAt, at))
|
|
8325
|
+
changes.lastMessageAt = at;
|
|
8326
|
+
return commit(channel, changes);
|
|
8327
|
+
};
|
|
8328
|
+
/**
|
|
8329
|
+
* Local correction for the current user's own send (ChannelActivityService v1
|
|
8330
|
+
* REQ-009, Trigger Matrix row 1b). Once the server confirms the message, each
|
|
8331
|
+
* field that still holds the device-clock `localAt` written at send time is
|
|
8332
|
+
* replaced by the server's `serverAt`, even when that is older. A field a newer
|
|
8333
|
+
* message has already moved is left alone. One write, one change notification,
|
|
8334
|
+
* nothing when neither field still holds `localAt`.
|
|
8335
|
+
*
|
|
8336
|
+
* @returns `true` when at least one field was replaced
|
|
8337
|
+
* @category private
|
|
8338
|
+
*/
|
|
8339
|
+
const confirmOwnMessage = (channelId, localAt, serverAt) => {
|
|
8340
|
+
const client = getActiveClient();
|
|
8341
|
+
if (!client.cache || !channelId || !localAt || !serverAt)
|
|
8342
|
+
return false;
|
|
8343
|
+
if (isSameInstant(localAt, serverAt))
|
|
8344
|
+
return false;
|
|
8345
|
+
const channel = findCachedChannel(channelId);
|
|
8346
|
+
if (!channel)
|
|
8347
|
+
return false;
|
|
8348
|
+
const changes = {};
|
|
8349
|
+
if (isSameInstant(channel.lastActivity, localAt))
|
|
8350
|
+
changes.lastActivity = serverAt;
|
|
8351
|
+
if (isSameInstant(channel.lastMessageAt, localAt))
|
|
8352
|
+
changes.lastMessageAt = serverAt;
|
|
8353
|
+
return commit(channel, changes);
|
|
8354
|
+
};
|
|
8355
|
+
|
|
8356
|
+
/**
|
|
8357
|
+
* Session-wide owner of the local `lastActivity` / `lastMessageAt` updates
|
|
8358
|
+
* (ChannelActivityService v1, Web migration rows W1 – W5).
|
|
8359
|
+
*
|
|
8360
|
+
* Registered once per session from `setupLoginSubscriptions` and
|
|
8361
|
+
* `resumeSession`, so the update runs whether or not a channel collection is
|
|
8362
|
+
* open, and exactly once per message however many collections are open.
|
|
8363
|
+
*
|
|
8364
|
+
* It listens to the raw events rather than `onMessageCreatedMqtt`, so it does
|
|
8365
|
+
* not duplicate that subscriber's side effects (mark-read, unread recount).
|
|
8366
|
+
*
|
|
8367
|
+
* @category private
|
|
8368
|
+
*/
|
|
8369
|
+
const channelActivityOnLoginHandler = () => {
|
|
8370
|
+
const client = getActiveClient();
|
|
8371
|
+
const disposers = [
|
|
8372
|
+
// Any user's new message, in any message feed of the channel. The raw
|
|
8373
|
+
// payload's `channelId` is the channel's internal id.
|
|
8374
|
+
createEventSubscriber(client, 'channelActivity', 'message.created', payload => {
|
|
8375
|
+
payload.messages.forEach(message => {
|
|
8376
|
+
updateChannelActivityTimestamps(message.channelId, message.createdAt);
|
|
8377
|
+
});
|
|
8378
|
+
}),
|
|
8379
|
+
// The current user's own send fires this three times: with the optimistic
|
|
8380
|
+
// message at send time (Syncing), on the create response (Synced), and on
|
|
8381
|
+
// failure (Error). The device-clock time of the Syncing / Error fires was
|
|
8382
|
+
// already applied synchronously in createMessageOptimistic, and replaying it
|
|
8383
|
+
// here could undo the server correction made on the response
|
|
8384
|
+
// (confirmOwnMessage). Only the server-confirmed fire goes through the guard.
|
|
8385
|
+
createEventSubscriber(client, 'channelActivity', 'local.message.created', payload => {
|
|
8386
|
+
payload.messages.forEach(message => {
|
|
8387
|
+
if (message.syncState !== "synced" /* Amity.SyncState.Synced */)
|
|
8388
|
+
return;
|
|
8389
|
+
updateChannelActivityTimestamps(message.channelId, message.createdAt);
|
|
8390
|
+
});
|
|
8391
|
+
}),
|
|
8392
|
+
];
|
|
8393
|
+
return () => {
|
|
8394
|
+
disposers.forEach(fn => fn());
|
|
8395
|
+
};
|
|
8396
|
+
};
|
|
8397
|
+
|
|
8045
8398
|
const EVENTS = [
|
|
8046
8399
|
'disconnected',
|
|
8047
8400
|
'error',
|
|
@@ -8231,7 +8584,7 @@ const setupLoginSubscriptions = (unsubWatcher) => {
|
|
|
8231
8584
|
// NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
|
|
8232
8585
|
// the channel because currently backend can't handle this, so every time a user is banned from
|
|
8233
8586
|
// 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());
|
|
8587
|
+
onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler(), reactionSyncEngineOnLoginHandler(), blockedUserSyncEngineOnLoginHandler(), blockingUserSyncEngineOnLoginHandler(), blockRealtimeOnLoginHandler(), resetAutoSubscriptionsOnLogout(), CursorController.clearOnLogout(), postCommentCountEngineOnLoginHandler(), channelActivityOnLoginHandler());
|
|
8235
8588
|
if (client.useLegacyUnreadCount) {
|
|
8236
8589
|
subscriptions.push(readReceiptSyncEngineOnLoginHandler());
|
|
8237
8590
|
}
|
|
@@ -9492,7 +9845,7 @@ const resumeSession = async (params, sessionHandler, config) => {
|
|
|
9492
9845
|
// NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
|
|
9493
9846
|
// the channel because currently backend can't handle this, so every time a user is banned from
|
|
9494
9847
|
// 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());
|
|
9848
|
+
onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler(), channelActivityOnLoginHandler());
|
|
9496
9849
|
if (client.useLegacyUnreadCount) {
|
|
9497
9850
|
subscriptions$1.push(readReceiptSyncEngineOnLoginHandler());
|
|
9498
9851
|
}
|
|
@@ -11784,7 +12137,7 @@ const prepareCommunityPayload = (rawPayload) => {
|
|
|
11784
12137
|
// map users with community
|
|
11785
12138
|
const mappedCommunityUsers = rawPayload.communityUsers.map(communityUser => {
|
|
11786
12139
|
const user = internalUsers.find(user => user.userId === communityUser.userId);
|
|
11787
|
-
return Object.assign(Object.assign({}, communityUser), { user });
|
|
12140
|
+
return Object.assign(Object.assign({}, communityUser), { user: user ? userLinkedObject(user) : undefined });
|
|
11788
12141
|
});
|
|
11789
12142
|
const communityWithMembershipStatus = updateMembershipStatus(communitiesWithPostSetting, mappedCommunityUsers);
|
|
11790
12143
|
return Object.assign(Object.assign({}, rawPayload), { users: rawPayload.users.map(convertRawUserToInternalUser), communities: communityWithMembershipStatus, communityUsers: mappedCommunityUsers });
|
|
@@ -11801,7 +12154,7 @@ const prepareCommunityMembershipPayload = (rawPayload) => {
|
|
|
11801
12154
|
// map users with community
|
|
11802
12155
|
const mappedCommunityUsers = rawPayload.communityUsers.map(communityUser => {
|
|
11803
12156
|
const user = rawPayload.users.find(user => user.userId === communityUser.userId);
|
|
11804
|
-
return Object.assign(Object.assign({}, communityUser), { user });
|
|
12157
|
+
return Object.assign(Object.assign({}, communityUser), { user: user ? userLinkedObject(convertRawUserToInternalUser(user)) : undefined });
|
|
11805
12158
|
});
|
|
11806
12159
|
const communityWithMembershipStatus = updateMembershipStatus(communitiesWithPostSetting, mappedCommunityUsers);
|
|
11807
12160
|
return Object.assign(Object.assign({}, rawPayload), { communities: communityWithMembershipStatus, communityUsers: mappedCommunityUsers });
|
|
@@ -13451,7 +13804,7 @@ const getWatchSessionStorage = () => {
|
|
|
13451
13804
|
return storageInstance;
|
|
13452
13805
|
};
|
|
13453
13806
|
|
|
13454
|
-
const privateKey = "
|
|
13807
|
+
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
13808
|
/*
|
|
13456
13809
|
* The crypto algorithm used for importing key and signing string
|
|
13457
13810
|
*/
|
|
@@ -15226,7 +15579,7 @@ const updateUser = async (userId, patch) => {
|
|
|
15226
15579
|
ingestInCache(payload, { cachedAt });
|
|
15227
15580
|
fireEvent('user.updated', data);
|
|
15228
15581
|
return {
|
|
15229
|
-
data: payload.users.find(user => user.userId === userId),
|
|
15582
|
+
data: userLinkedObject(payload.users.find(user => user.userId === userId)),
|
|
15230
15583
|
cachedAt,
|
|
15231
15584
|
};
|
|
15232
15585
|
};
|
|
@@ -15250,18 +15603,18 @@ const buildFlagUserBody = (options) => {
|
|
|
15250
15603
|
/**
|
|
15251
15604
|
* ```js
|
|
15252
15605
|
* import { UserRepository } from '@amityco/ts-sdk'
|
|
15606
|
+
*
|
|
15607
|
+
* // Typeless report (supported).
|
|
15253
15608
|
* 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
15609
|
* ```
|
|
15260
15610
|
*
|
|
15611
|
+
* Flags a user. Two call shapes are accepted (see the overloads): id-only, which
|
|
15612
|
+
* sends no body and stays supported, and the deprecated structured options object,
|
|
15613
|
+
* whose request mapping is unchanged. New code reporting a profile field or behavior
|
|
15614
|
+
* should use `flagUserProfile` or `flagUserBehavior`.
|
|
15615
|
+
*
|
|
15261
15616
|
* @param userId The ID of the user to be flagged
|
|
15262
|
-
* @param options
|
|
15263
|
-
* body: `reportType`, `reason`, `comment`. Only non-null fields are included.
|
|
15264
|
-
* All validation is server-side; a bodyless call produces a typeless report.
|
|
15617
|
+
* @param options Deprecated report metadata; omit for a typeless report
|
|
15265
15618
|
* @returns the flagged {@link Amity.User}
|
|
15266
15619
|
*
|
|
15267
15620
|
* @category User API
|
|
@@ -15278,29 +15631,228 @@ const flagUser = async (userId, options) => {
|
|
|
15278
15631
|
ingestInCache(payload);
|
|
15279
15632
|
}
|
|
15280
15633
|
fireEvent('user.flagged', data);
|
|
15281
|
-
return (_a = payload.users.find(user => user.userId === userId)) !== null && _a !== void 0 ? _a : payload.users[0];
|
|
15634
|
+
return userLinkedObject((_a = payload.users.find(user => user.userId === userId)) !== null && _a !== void 0 ? _a : payload.users[0]);
|
|
15282
15635
|
};
|
|
15283
15636
|
/* end_public_function */
|
|
15284
15637
|
|
|
15638
|
+
/**
|
|
15639
|
+
* Sends a validated split user report (`flagUserProfile` / `flagUserBehavior`) to the
|
|
15640
|
+
* same endpoint as `flagUser`, then persists the returned user and fires `user.flagged`
|
|
15641
|
+
* exactly as `flagUser` does, so all three calls share return, cache and event behaviour.
|
|
15642
|
+
*
|
|
15643
|
+
* Not exported from the public API.
|
|
15644
|
+
*/
|
|
15645
|
+
const submitUserReport = async (apiName, userId, body) => {
|
|
15646
|
+
var _a;
|
|
15647
|
+
const client = getActiveClient();
|
|
15648
|
+
client.log(`user/${apiName}`, { userId, body });
|
|
15649
|
+
const { data } = await client.http.post(`/api/v4/me/flags/${encodeURIComponent(userId)}`, body);
|
|
15650
|
+
const payload = prepareUserPayload(data);
|
|
15651
|
+
if (client.cache) {
|
|
15652
|
+
ingestInCache(payload);
|
|
15653
|
+
}
|
|
15654
|
+
fireEvent('user.flagged', data);
|
|
15655
|
+
return userLinkedObject((_a = payload.users.find(user => user.userId === userId)) !== null && _a !== void 0 ? _a : payload.users[0]);
|
|
15656
|
+
};
|
|
15657
|
+
|
|
15658
|
+
/** Maximum `comment` length, in string length units, after trimming. */
|
|
15659
|
+
const USER_REPORT_COMMENT_MAX_LENGTH = 300;
|
|
15660
|
+
const PROFILE_FIELDS = Object.values(UserProfileFieldEnum);
|
|
15661
|
+
const BEHAVIOR_REASONS = Object.values(ContentFlagReasonEnum);
|
|
15662
|
+
const REPORT_TYPE_FILTERS = Object.values(UserReportTypeFilterEnum);
|
|
15663
|
+
const assertOptionsObject = (apiName, options, shape) => {
|
|
15664
|
+
if (typeof options !== 'object' || options === null || Array.isArray(options)) {
|
|
15665
|
+
throw new ASCInvalidParameterError(`${apiName}: expected an options object \`${shape}\``);
|
|
15666
|
+
}
|
|
15667
|
+
return options;
|
|
15668
|
+
};
|
|
15669
|
+
/**
|
|
15670
|
+
* Normalizes an optional reporter `comment`: it must be a string when supplied (`null`
|
|
15671
|
+
* is not a valid supplied comment); it is trimmed; a blank result is omitted (the
|
|
15672
|
+
* backend rejects `""`); a result longer than 300 characters is rejected before any
|
|
15673
|
+
* request. Otherwise the trimmed text is returned unchanged.
|
|
15674
|
+
*
|
|
15675
|
+
* @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` for a non-string or over-length comment
|
|
15676
|
+
*/
|
|
15677
|
+
const normalizeUserReportComment = (apiName, comment) => {
|
|
15678
|
+
if (comment === undefined)
|
|
15679
|
+
return undefined;
|
|
15680
|
+
if (typeof comment !== 'string') {
|
|
15681
|
+
throw new ASCInvalidParameterError(`${apiName}: \`comment\` must be a string when supplied`);
|
|
15682
|
+
}
|
|
15683
|
+
const trimmed = comment.trim();
|
|
15684
|
+
if (trimmed.length === 0)
|
|
15685
|
+
return undefined;
|
|
15686
|
+
if (trimmed.length > USER_REPORT_COMMENT_MAX_LENGTH) {
|
|
15687
|
+
throw new ASCInvalidParameterError(`${apiName}: \`comment\` must be at most ${USER_REPORT_COMMENT_MAX_LENGTH} characters after trimming`);
|
|
15688
|
+
}
|
|
15689
|
+
return trimmed;
|
|
15690
|
+
};
|
|
15691
|
+
const withComment = (body, comment) => comment === undefined ? body : Object.assign(Object.assign({}, body), { comment });
|
|
15692
|
+
/**
|
|
15693
|
+
* Validates `flagUserProfile` options and builds its request body. Untyped callers can
|
|
15694
|
+
* pass anything at runtime, so nothing is trusted from the declared type. Any `reason`
|
|
15695
|
+
* key is ignored and never sent.
|
|
15696
|
+
*
|
|
15697
|
+
* @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when the options are malformed
|
|
15698
|
+
*/
|
|
15699
|
+
const toUserProfileReportBody = (options) => {
|
|
15700
|
+
const apiName = 'flagUserProfile';
|
|
15701
|
+
const { field, comment } = assertOptionsObject(apiName, options, '{ field, comment? }');
|
|
15702
|
+
if (typeof field !== 'string' || !PROFILE_FIELDS.includes(field)) {
|
|
15703
|
+
throw new ASCInvalidParameterError(`${apiName}: \`field\` must be a \`UserProfileFieldEnum\` value`);
|
|
15704
|
+
}
|
|
15705
|
+
return withComment({ reportType: field }, normalizeUserReportComment(apiName, comment));
|
|
15706
|
+
};
|
|
15707
|
+
/**
|
|
15708
|
+
* Validates `flagUserBehavior` options and builds its request body. `reason` must be one
|
|
15709
|
+
* of the nine `ContentFlagReasonEnum` values and is sent as that canonical label;
|
|
15710
|
+
* `reportType` is always `behavior`.
|
|
15711
|
+
*
|
|
15712
|
+
* @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when the options are malformed
|
|
15713
|
+
*/
|
|
15714
|
+
const toUserBehaviorReportBody = (options) => {
|
|
15715
|
+
const apiName = 'flagUserBehavior';
|
|
15716
|
+
const { reason, comment } = assertOptionsObject(apiName, options, '{ reason, comment? }');
|
|
15717
|
+
if (typeof reason !== 'string' || !BEHAVIOR_REASONS.includes(reason)) {
|
|
15718
|
+
throw new ASCInvalidParameterError(`${apiName}: \`reason\` must be a \`ContentFlagReasonEnum\` value`);
|
|
15719
|
+
}
|
|
15720
|
+
return withComment({ reportType: 'behavior', reason }, normalizeUserReportComment(apiName, comment));
|
|
15721
|
+
};
|
|
15722
|
+
/**
|
|
15723
|
+
* Validates the optional `{ reportType? }` of `unflagUser` and builds its query parameters.
|
|
15724
|
+
* Returns `undefined` when no `reportType` is given, so the request carries no query string
|
|
15725
|
+
* and withdraws every report the caller filed on the user. The server validates the value
|
|
15726
|
+
* too (422); this local check only saves the round-trip for the string alias, which admits
|
|
15727
|
+
* raw strings. Untyped callers can pass anything at runtime, so nothing is trusted from the
|
|
15728
|
+
* declared type.
|
|
15729
|
+
*
|
|
15730
|
+
* @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when the options are malformed or `reportType` is unknown
|
|
15731
|
+
*/
|
|
15732
|
+
const toUserReportTypeFilter = (options) => {
|
|
15733
|
+
if (options === undefined)
|
|
15734
|
+
return undefined;
|
|
15735
|
+
const apiName = 'unflagUser';
|
|
15736
|
+
const { reportType } = assertOptionsObject(apiName, options, '{ reportType? }');
|
|
15737
|
+
if (reportType === undefined)
|
|
15738
|
+
return undefined;
|
|
15739
|
+
if (typeof reportType !== 'string' || !REPORT_TYPE_FILTERS.includes(reportType)) {
|
|
15740
|
+
throw new ASCInvalidParameterError(`${apiName}: \`reportType\` must be a \`UserReportTypeFilterEnum\` value`);
|
|
15741
|
+
}
|
|
15742
|
+
return { reportType: reportType };
|
|
15743
|
+
};
|
|
15744
|
+
|
|
15745
|
+
/* begin_public_function
|
|
15746
|
+
id: user.flag_profile
|
|
15747
|
+
*/
|
|
15748
|
+
/**
|
|
15749
|
+
* ```js
|
|
15750
|
+
* import { UserRepository, UserProfileFieldEnum } from '@amityco/ts-sdk'
|
|
15751
|
+
* const flagged = await UserRepository.flagUserProfile('userId', {
|
|
15752
|
+
* field: UserProfileFieldEnum.AVATAR,
|
|
15753
|
+
* comment: 'Nudity in profile photo',
|
|
15754
|
+
* })
|
|
15755
|
+
* ```
|
|
15756
|
+
*
|
|
15757
|
+
* Reports one of a user's profile fields (display name, description or avatar). The
|
|
15758
|
+
* call has no reason: profile reports never carry one, so the backend's reason rules
|
|
15759
|
+
* cannot be violated from here. A `reason` key passed at runtime is ignored and never sent.
|
|
15760
|
+
*
|
|
15761
|
+
* `comment` is trimmed; a blank comment is omitted from the request; a comment longer
|
|
15762
|
+
* than 300 characters after trimming is rejected before any request. Otherwise the
|
|
15763
|
+
* trimmed text is sent unchanged.
|
|
15764
|
+
*
|
|
15765
|
+
* Sends `POST /api/v4/me/flags/:userId` with `{ reportType: <field>, comment? }`.
|
|
15766
|
+
*
|
|
15767
|
+
* @param userId The ID of the user to be reported
|
|
15768
|
+
* @param options `field` is a `UserProfileFieldEnum` value; `comment` is optional free text
|
|
15769
|
+
* @returns the flagged {@link Amity.User}
|
|
15770
|
+
* @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when `options` is missing or malformed, or `comment` is over 300 characters, before any request is made
|
|
15771
|
+
*
|
|
15772
|
+
* @category User API
|
|
15773
|
+
* @async
|
|
15774
|
+
* */
|
|
15775
|
+
const flagUserProfile = async (userId, options) => submitUserReport('flagUserProfile', userId, toUserProfileReportBody(options));
|
|
15776
|
+
/* end_public_function */
|
|
15777
|
+
|
|
15778
|
+
/* begin_public_function
|
|
15779
|
+
id: user.flag_behavior
|
|
15780
|
+
*/
|
|
15781
|
+
/**
|
|
15782
|
+
* ```js
|
|
15783
|
+
* import { UserRepository, ContentFlagReasonEnum } from '@amityco/ts-sdk'
|
|
15784
|
+
* const flagged = await UserRepository.flagUserBehavior('userId', {
|
|
15785
|
+
* reason: ContentFlagReasonEnum.Others,
|
|
15786
|
+
* comment: 'Impersonating our brand',
|
|
15787
|
+
* })
|
|
15788
|
+
* ```
|
|
15789
|
+
*
|
|
15790
|
+
* Reports a user's behavior. `reason` is required and is one of the nine
|
|
15791
|
+
* `ContentFlagReasonEnum` values, sent as its canonical label. Reporter text, including
|
|
15792
|
+
* the explanation for `Others`, always travels in `comment`; the reason never carries text.
|
|
15793
|
+
*
|
|
15794
|
+
* `comment` is trimmed; a blank comment is omitted from the request; a comment longer
|
|
15795
|
+
* than 300 characters after trimming is rejected before any request. Otherwise the
|
|
15796
|
+
* trimmed text is sent unchanged.
|
|
15797
|
+
*
|
|
15798
|
+
* Sends `POST /api/v4/me/flags/:userId` with `{ reportType: 'behavior', reason, comment? }`.
|
|
15799
|
+
*
|
|
15800
|
+
* @param userId The ID of the user to be reported
|
|
15801
|
+
* @param options `reason` is a `ContentFlagReasonEnum` value; `comment` is optional free text
|
|
15802
|
+
* @returns the flagged {@link Amity.User}
|
|
15803
|
+
* @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
|
|
15804
|
+
*
|
|
15805
|
+
* @category User API
|
|
15806
|
+
* @async
|
|
15807
|
+
* */
|
|
15808
|
+
const flagUserBehavior = async (userId, options) => submitUserReport('flagUserBehavior', userId, toUserBehaviorReportBody(options));
|
|
15809
|
+
/* end_public_function */
|
|
15810
|
+
|
|
15285
15811
|
/* begin_public_function
|
|
15286
15812
|
id: user.unflag
|
|
15287
15813
|
*/
|
|
15288
15814
|
/**
|
|
15289
15815
|
* ```js
|
|
15290
|
-
* import { UserRepository } from '@amityco/ts-sdk'
|
|
15291
|
-
*
|
|
15816
|
+
* import { UserRepository, UserReportTypeFilterEnum } from '@amityco/ts-sdk'
|
|
15817
|
+
*
|
|
15818
|
+
* // Withdraw every report I filed on the user.
|
|
15819
|
+
* const unflagged = await UserRepository.unflagUser('userId')
|
|
15820
|
+
*
|
|
15821
|
+
* // Withdraw only my behavior report; my other reports on the user stay.
|
|
15822
|
+
* await UserRepository.unflagUser('userId', { reportType: UserReportTypeFilterEnum.BEHAVIOR })
|
|
15823
|
+
*
|
|
15824
|
+
* // Withdraw only my typeless (legacy) report.
|
|
15825
|
+
* await UserRepository.unflagUser('userId', { reportType: UserReportTypeFilterEnum.UNTYPED })
|
|
15292
15826
|
* ```
|
|
15293
15827
|
*
|
|
15828
|
+
* Withdraws the caller's reports on a user. Without `reportType` every report the caller
|
|
15829
|
+
* filed on that user is withdrawn, as before. With `reportType` only the caller's report
|
|
15830
|
+
* of that type is withdrawn; the caller's other reports and other users' reports are
|
|
15831
|
+
* untouched. Withdrawing a report that does not exist succeeds and changes nothing.
|
|
15832
|
+
*
|
|
15833
|
+
* The returned user is persisted to the cache, so its `hashFlag` says whether the caller
|
|
15834
|
+
* still holds any report on the user. Do not flip a local "flagged by me" flag to `false`
|
|
15835
|
+
* after a per-type withdrawal; read the fresh user, or call `isUserFlaggedByMe` for the
|
|
15836
|
+
* remaining per-type state (`flags`).
|
|
15837
|
+
*
|
|
15838
|
+
* Sends `DELETE /api/v4/me/flags/:userId`, with `?reportType=<type>` when one is given.
|
|
15839
|
+
*
|
|
15294
15840
|
* @param userId The ID of the user to unflag
|
|
15295
|
-
* @
|
|
15841
|
+
* @param options Optional; `reportType` selects a single report to withdraw. For a report that `isUserFlaggedByMe` lists with `reportType: null`, pass `UserReportTypeFilterEnum.UNTYPED`
|
|
15842
|
+
* @returns `true` once the server has processed the withdrawal
|
|
15843
|
+
* @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when `reportType` is not a `UserReportTypeFilterEnum` value, before any request is made
|
|
15296
15844
|
*
|
|
15297
15845
|
* @category User API
|
|
15298
15846
|
* @async
|
|
15299
15847
|
* */
|
|
15300
|
-
const unflagUser = async (userId) => {
|
|
15301
|
-
const client = getActiveClient();
|
|
15302
|
-
client.log('user/unflag', userId);
|
|
15303
|
-
const
|
|
15848
|
+
const unflagUser = async (userId, options) => {
|
|
15849
|
+
const client = getActiveClient();
|
|
15850
|
+
client.log('user/unflag', { userId, options });
|
|
15851
|
+
const params = toUserReportTypeFilter(options);
|
|
15852
|
+
const url = `/api/v4/me/flags/${encodeURIComponent(userId)}`;
|
|
15853
|
+
const { data } = params
|
|
15854
|
+
? await client.http.delete(url, { params })
|
|
15855
|
+
: await client.http.delete(url);
|
|
15304
15856
|
const payload = prepareUserPayload(data);
|
|
15305
15857
|
if (client.cache) {
|
|
15306
15858
|
ingestInCache(payload);
|
|
@@ -15316,11 +15868,33 @@ const unflagUser = async (userId) => {
|
|
|
15316
15868
|
/**
|
|
15317
15869
|
* ```js
|
|
15318
15870
|
* import { UserRepository } from '@amityco/ts-sdk'
|
|
15319
|
-
*
|
|
15871
|
+
*
|
|
15872
|
+
* const { isFlagByMe, flags } = await UserRepository.isUserFlaggedByMe('userId')
|
|
15873
|
+
*
|
|
15874
|
+
* // Grey out the report options already used; null marks the typeless report.
|
|
15875
|
+
* const used = new Set(flags.map(flag => flag.reportType))
|
|
15320
15876
|
* ```
|
|
15321
15877
|
*
|
|
15322
|
-
*
|
|
15323
|
-
*
|
|
15878
|
+
* Returns whether the caller has reported a user, with each of the caller's own reports
|
|
15879
|
+
* on that user, exactly as `GET /api/v4/users/:userId/isFlagByMe` answers. Each report
|
|
15880
|
+
* carries `reportType`, `reason`, `comment` and `createdAt`; a field the report does not
|
|
15881
|
+
* have is `null`. `reportType: null` is the typeless (legacy) report, which `unflagUser`
|
|
15882
|
+
* withdraws with `UserReportTypeFilterEnum.UNTYPED`. Only the caller's reports come back.
|
|
15883
|
+
*
|
|
15884
|
+
* This is the only source of per-type state. Nothing is filtered, cached or persisted:
|
|
15885
|
+
* the call reads the server every time and does not consult `hashFlag`, which is a
|
|
15886
|
+
* type-blind bloom filter. Call it again after a flag or unflag when the UI needs the
|
|
15887
|
+
* updated per-type state.
|
|
15888
|
+
*
|
|
15889
|
+
* Breaking change: earlier versions returned a boolean from the v3 endpoint, which the
|
|
15890
|
+
* backend has deprecated. Read `isFlagByMe` on the result for the same answer.
|
|
15891
|
+
*
|
|
15892
|
+
* @deprecated On an {@link Amity.User} you already hold, read `user.isFlaggedByMe` for a
|
|
15893
|
+
* request-free badge and call `user.getUserFlagsByMe()` for the reports; it skips the request
|
|
15894
|
+
* when the Bloom test says the user is not flagged. This function remains the underlying request.
|
|
15895
|
+
*
|
|
15896
|
+
* @param userId The ID of the user to check
|
|
15897
|
+
* @returns the caller's {@link Amity.UserFlagByMe} on that user
|
|
15324
15898
|
*
|
|
15325
15899
|
* @category Report API
|
|
15326
15900
|
* @async
|
|
@@ -15328,8 +15902,7 @@ const unflagUser = async (userId) => {
|
|
|
15328
15902
|
const isUserFlaggedByMe = async (userId) => {
|
|
15329
15903
|
const client = getActiveClient();
|
|
15330
15904
|
client.log('user/isUserFlaggedByMe', userId);
|
|
15331
|
-
|
|
15332
|
-
return isFlagByMe;
|
|
15905
|
+
return getFlagsByMe(userId);
|
|
15333
15906
|
};
|
|
15334
15907
|
/* end_public_function */
|
|
15335
15908
|
|
|
@@ -15579,16 +16152,14 @@ const getUser = (userId, callback) => {
|
|
|
15579
16152
|
};
|
|
15580
16153
|
/* end_public_function */
|
|
15581
16154
|
|
|
15582
|
-
|
|
15583
|
-
|
|
15584
|
-
|
|
15585
|
-
|
|
15586
|
-
|
|
15587
|
-
|
|
15588
|
-
|
|
15589
|
-
|
|
15590
|
-
}
|
|
15591
|
-
}
|
|
16155
|
+
var EnumUserActions;
|
|
16156
|
+
(function (EnumUserActions) {
|
|
16157
|
+
EnumUserActions["OnUserDeleted"] = "onUserDeleted";
|
|
16158
|
+
EnumUserActions["OnUserUpdated"] = "onUserUpdated";
|
|
16159
|
+
EnumUserActions["OnUserFlagged"] = "onUserFlagged";
|
|
16160
|
+
EnumUserActions["OnUserUnflagged"] = "onUserUnflagged";
|
|
16161
|
+
EnumUserActions["OnUserFlagCleared"] = "onUserFlagCleared";
|
|
16162
|
+
})(EnumUserActions || (EnumUserActions = {}));
|
|
15592
16163
|
|
|
15593
16164
|
class UserQueryStreamController extends QueryStreamController {
|
|
15594
16165
|
constructor(query, cacheKey, notifyChange, preparePayload) {
|
|
@@ -15636,14 +16207,16 @@ class UserQueryStreamController extends QueryStreamController {
|
|
|
15636
16207
|
}
|
|
15637
16208
|
}
|
|
15638
16209
|
|
|
15639
|
-
|
|
15640
|
-
(
|
|
15641
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
|
|
15645
|
-
|
|
15646
|
-
|
|
16210
|
+
class UserPaginationController extends PaginationController {
|
|
16211
|
+
async getRequest(queryParams, token) {
|
|
16212
|
+
const { limit = COLLECTION_DEFAULT_PAGINATION_LIMIT } = queryParams, params = __rest(queryParams, ["limit"]);
|
|
16213
|
+
const options = token ? { token } : { limit };
|
|
16214
|
+
const { data: queryResponse } = await this.http.get(`/api/v3/users`, {
|
|
16215
|
+
params: Object.assign(Object.assign({}, params), { options, isDeleted: false }),
|
|
16216
|
+
});
|
|
16217
|
+
return queryResponse;
|
|
16218
|
+
}
|
|
16219
|
+
}
|
|
15647
16220
|
|
|
15648
16221
|
class UserLiveCollectionController extends LiveCollectionController {
|
|
15649
16222
|
constructor(query, callback) {
|
|
@@ -15760,16 +16333,17 @@ const getUsers = (params, callback, config) => {
|
|
|
15760
16333
|
};
|
|
15761
16334
|
/* end_public_function */
|
|
15762
16335
|
|
|
15763
|
-
|
|
15764
|
-
|
|
15765
|
-
|
|
15766
|
-
|
|
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;
|
|
16336
|
+
const convertEventPayload = (eventHandler, sourceModelProp, destinationDomain) => (callback) => eventHandler(sourceModel => {
|
|
16337
|
+
var _a;
|
|
16338
|
+
if (!sourceModel) {
|
|
16339
|
+
return sourceModel;
|
|
15771
16340
|
}
|
|
15772
|
-
}
|
|
16341
|
+
const cacheKey = [destinationDomain, 'get', `${sourceModel[sourceModelProp]}`];
|
|
16342
|
+
const model = (_a = pullFromCache(cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
|
|
16343
|
+
if (!model)
|
|
16344
|
+
return;
|
|
16345
|
+
return callback(model);
|
|
16346
|
+
});
|
|
15773
16347
|
|
|
15774
16348
|
var EnumBlockActions;
|
|
15775
16349
|
(function (EnumBlockActions) {
|
|
@@ -15825,17 +16399,16 @@ class BlockedUserQueryStreamController extends QueryStreamController {
|
|
|
15825
16399
|
}
|
|
15826
16400
|
}
|
|
15827
16401
|
|
|
15828
|
-
|
|
15829
|
-
|
|
15830
|
-
|
|
15831
|
-
|
|
16402
|
+
class BlockedUserPaginationController extends PaginationController {
|
|
16403
|
+
async getRequest(queryParams, token) {
|
|
16404
|
+
const { limit = COLLECTION_DEFAULT_PAGINATION_LIMIT } = queryParams, params = __rest(queryParams, ["limit"]);
|
|
16405
|
+
const options = token ? { token } : { limit };
|
|
16406
|
+
const { data: queryResponse } = await this.http.get('/api/v4/me/user-blocks', {
|
|
16407
|
+
params: Object.assign(Object.assign({}, params), { options, isDeleted: false }),
|
|
16408
|
+
});
|
|
16409
|
+
return queryResponse;
|
|
15832
16410
|
}
|
|
15833
|
-
|
|
15834
|
-
const model = (_a = pullFromCache(cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
|
|
15835
|
-
if (!model)
|
|
15836
|
-
return;
|
|
15837
|
-
return callback(model);
|
|
15838
|
-
});
|
|
16411
|
+
}
|
|
15839
16412
|
|
|
15840
16413
|
class BlockedUserLiveCollectionController extends LiveCollectionController {
|
|
15841
16414
|
constructor(query, callback) {
|
|
@@ -16145,17 +16718,6 @@ const getBlockingUsers = (callback) => {
|
|
|
16145
16718
|
};
|
|
16146
16719
|
/* end_public_function */
|
|
16147
16720
|
|
|
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
16721
|
class SearchUserQueryStreamController extends QueryStreamController {
|
|
16160
16722
|
constructor(query, cacheKey, notifyChange, preparePayload) {
|
|
16161
16723
|
super(query, cacheKey);
|
|
@@ -16202,6 +16764,17 @@ class SearchUserQueryStreamController extends QueryStreamController {
|
|
|
16202
16764
|
}
|
|
16203
16765
|
}
|
|
16204
16766
|
|
|
16767
|
+
class SearchUserPaginationController extends PaginationController {
|
|
16768
|
+
async getRequest(queryParams, token) {
|
|
16769
|
+
const { limit = COLLECTION_DEFAULT_PAGINATION_LIMIT, displayName } = queryParams, params = __rest(queryParams, ["limit", "displayName"]);
|
|
16770
|
+
const options = token ? { token } : { limit };
|
|
16771
|
+
const { data: queryResponse } = await this.http.get(`/api/v3/users`, {
|
|
16772
|
+
params: Object.assign(Object.assign({}, params), { keyword: displayName, options, isDeleted: false }),
|
|
16773
|
+
});
|
|
16774
|
+
return queryResponse;
|
|
16775
|
+
}
|
|
16776
|
+
}
|
|
16777
|
+
|
|
16205
16778
|
class SearchUserLiveCollectionController extends LiveCollectionController {
|
|
16206
16779
|
constructor(query, callback) {
|
|
16207
16780
|
var _a;
|
|
@@ -16456,6 +17029,8 @@ var index$r = /*#__PURE__*/Object.freeze({
|
|
|
16456
17029
|
getUserByIds: getUserByIds,
|
|
16457
17030
|
updateUser: updateUser,
|
|
16458
17031
|
flagUser: flagUser,
|
|
17032
|
+
flagUserProfile: flagUserProfile,
|
|
17033
|
+
flagUserBehavior: flagUserBehavior,
|
|
16459
17034
|
unflagUser: unflagUser,
|
|
16460
17035
|
isUserFlaggedByMe: isUserFlaggedByMe,
|
|
16461
17036
|
getAllBlockedUsers: getAllBlockedUsers,
|
|
@@ -17066,7 +17641,7 @@ const preparePostPayload = (payload) => {
|
|
|
17066
17641
|
// map users with community
|
|
17067
17642
|
const mappedCommunityUsers = postPayload.communityUsers.map(communityUser => {
|
|
17068
17643
|
const user = postPayload.users.find(user => user.userId === communityUser.userId);
|
|
17069
|
-
return Object.assign(Object.assign({}, communityUser), { user });
|
|
17644
|
+
return Object.assign(Object.assign({}, communityUser), { user: user ? userLinkedObject(convertRawUserToInternalUser(user)) : undefined });
|
|
17070
17645
|
});
|
|
17071
17646
|
const communityWithMembershipStatus = updateMembershipStatus(communitiesWithPostSetting, mappedCommunityUsers);
|
|
17072
17647
|
let mappedNewStream = [];
|
|
@@ -18877,7 +19452,8 @@ const getPostReport = async ({ client, referenceId, }) => {
|
|
|
18877
19452
|
};
|
|
18878
19453
|
const getUserReport = async ({ client, referenceId, }) => {
|
|
18879
19454
|
var _a;
|
|
18880
|
-
|
|
19455
|
+
// v3 isFlagByMe is deprecated by the backend; v4 answers the same boolean plus the caller's flags.
|
|
19456
|
+
const { data } = await client.http.get(`/api/v4/users/${encodeURIComponent(referenceId)}/isFlagByMe`);
|
|
18881
19457
|
const { result, isFlagByMe } = data !== null && data !== void 0 ? data : {};
|
|
18882
19458
|
return (_a = result !== null && result !== void 0 ? result : isFlagByMe) !== null && _a !== void 0 ? _a : false;
|
|
18883
19459
|
};
|
|
@@ -19494,7 +20070,7 @@ const getLocalId = () => `LOCAL_${uuid()}`;
|
|
|
19494
20070
|
// FIXME: temp solution
|
|
19495
20071
|
let uniqueId;
|
|
19496
20072
|
const createMessageOptimistic = (bundle) => {
|
|
19497
|
-
var _a, _b;
|
|
20073
|
+
var _a, _b, _c, _d;
|
|
19498
20074
|
const client = getActiveClient();
|
|
19499
20075
|
if (!client.cache)
|
|
19500
20076
|
return;
|
|
@@ -19525,9 +20101,20 @@ const createMessageOptimistic = (bundle) => {
|
|
|
19525
20101
|
}
|
|
19526
20102
|
// as reused to update created and updated time, which should be the same
|
|
19527
20103
|
const createdTime = new Date().toISOString();
|
|
19528
|
-
const message = Object.assign({
|
|
20104
|
+
const message = Object.assign({
|
|
20105
|
+
// The optimistic message must name its channel (public id) so the own-send
|
|
20106
|
+
// update can move lastActivity / lastMessageAt at send time. Take it from the
|
|
20107
|
+
// cached sub-channel; when that is not cached, resolve the sub-channel id
|
|
20108
|
+
// through the channel cache, which for a channel's default sub-channel is
|
|
20109
|
+
// the channel itself (ChannelActivityService migration row I2). A thread or
|
|
20110
|
+
// unknown channel resolves to undefined, as before.
|
|
20111
|
+
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
20112
|
const cachedAt = UNSYNCED_OBJECT_CACHED_AT_VALUE;
|
|
19530
20113
|
pushToCache(['message', 'get', message.messageId], message, { cachedAt });
|
|
20114
|
+
// Own-send update (ChannelActivityService row 1): move the channel at send
|
|
20115
|
+
// time, synchronously, so the confirmation on the send response always sees
|
|
20116
|
+
// this value rather than racing the scheduled local event below.
|
|
20117
|
+
updateChannelActivityTimestamps(message.channelId, message.createdAt);
|
|
19531
20118
|
fireEvent('local.message.created', { messages: [message] });
|
|
19532
20119
|
return message;
|
|
19533
20120
|
};
|
|
@@ -19567,6 +20154,9 @@ const createMessage = async (bundle) => {
|
|
|
19567
20154
|
if (client.cache) {
|
|
19568
20155
|
ingestInCache(data, { cachedAt });
|
|
19569
20156
|
}
|
|
20157
|
+
// The own-send update wrote the device-clock createdAt; replace it with the
|
|
20158
|
+
// server's now that the message is confirmed (ChannelActivityService REQ-009).
|
|
20159
|
+
confirmOwnMessage(optimisticData === null || optimisticData === void 0 ? void 0 : optimisticData.channelId, optimisticData === null || optimisticData === void 0 ? void 0 : optimisticData.createdAt, messages[0].createdAt);
|
|
19570
20160
|
fireEvent('local.message.created', {
|
|
19571
20161
|
messages: [Object.assign(Object.assign({}, messages[0]), { syncState: "synced" /* Amity.SyncState.Synced */ })],
|
|
19572
20162
|
});
|
|
@@ -20160,23 +20750,85 @@ getDeliveredUsers.locally = (query) => {
|
|
|
20160
20750
|
return { data: users, cachedAt, prevPage, nextPage };
|
|
20161
20751
|
};
|
|
20162
20752
|
|
|
20163
|
-
|
|
20164
|
-
id: message.flag
|
|
20165
|
-
*/
|
|
20753
|
+
const CONTENT_FLAG_REASONS = Object.values(ContentFlagReasonEnum);
|
|
20166
20754
|
/**
|
|
20167
|
-
*
|
|
20168
|
-
*
|
|
20169
|
-
*
|
|
20170
|
-
|
|
20755
|
+
* True when a public `flagX(id, input)` argument is the legacy shape (a reason string
|
|
20756
|
+
* or nothing). Anything else is routed to the `Amity.ContentFlagOptions` path, whose
|
|
20757
|
+
* validation rejects malformed values before any request is made.
|
|
20758
|
+
*/
|
|
20759
|
+
const isLegacyContentFlagReason = (input) => input === undefined || typeof input === 'string';
|
|
20760
|
+
/**
|
|
20761
|
+
* Validates an `Amity.ContentFlagOptions` argument. Untyped (plain JS) callers can pass
|
|
20762
|
+
* anything at runtime, so the checks do not trust the declared type: the value must be a
|
|
20763
|
+
* plain object, `reason` must be one of the nine `ContentFlagReasonEnum` values and
|
|
20764
|
+
* `detail`, when supplied, must be a string (`null` is not a valid supplied detail).
|
|
20171
20765
|
*
|
|
20172
|
-
* @param
|
|
20173
|
-
* @
|
|
20174
|
-
|
|
20766
|
+
* @param apiName used in the error message, e.g. `flagPost`
|
|
20767
|
+
* @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when the options are malformed
|
|
20768
|
+
*/
|
|
20769
|
+
const validateContentFlagOptions = (apiName, options) => {
|
|
20770
|
+
if (typeof options !== 'object' || options === null || Array.isArray(options)) {
|
|
20771
|
+
throw new ASCInvalidParameterError(`${apiName}: expected an options object \`{ reason, detail? }\``);
|
|
20772
|
+
}
|
|
20773
|
+
const { reason, detail } = options;
|
|
20774
|
+
if (typeof reason !== 'string' || !CONTENT_FLAG_REASONS.includes(reason)) {
|
|
20775
|
+
throw new ASCInvalidParameterError(`${apiName}: \`reason\` must be a \`ContentFlagReasonEnum\` value`);
|
|
20776
|
+
}
|
|
20777
|
+
if (detail !== undefined && typeof detail !== 'string') {
|
|
20778
|
+
throw new ASCInvalidParameterError(`${apiName}: \`detail\` must be a string when supplied`);
|
|
20779
|
+
}
|
|
20780
|
+
return { reason: reason, detail };
|
|
20781
|
+
};
|
|
20782
|
+
/**
|
|
20783
|
+
* Builds the request body for validated options. `detail` is forwarded unchanged (empty
|
|
20784
|
+
* and whitespace-only strings included) and omitted from the body when `undefined`; the
|
|
20785
|
+
* backend enforces the 300-character limit.
|
|
20786
|
+
*/
|
|
20787
|
+
const toContentFlagBody = ({ reason, detail }) => detail === undefined ? { reason } : { reason, detail };
|
|
20788
|
+
|
|
20789
|
+
/**
|
|
20790
|
+
* New `flagMessage(messageId, { reason, detail? })` implementation. Any category,
|
|
20791
|
+
* including `Others`, may carry a `detail`. `detail` is forwarded unchanged (empty and
|
|
20792
|
+
* whitespace-only strings included) and omitted from the request body when `undefined`;
|
|
20793
|
+
* the backend enforces the 300-character limit.
|
|
20175
20794
|
*
|
|
20176
|
-
*
|
|
20177
|
-
*
|
|
20178
|
-
*
|
|
20179
|
-
|
|
20795
|
+
* Not exported from the public API. It is reached through the
|
|
20796
|
+
* `MessageRepository.flagMessage(messageId, options)` overload; see `api/flagMessage.ts`.
|
|
20797
|
+
*
|
|
20798
|
+
* @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when `options` is malformed, before any request is made
|
|
20799
|
+
*/
|
|
20800
|
+
const flagMessageWithOptions = async (messageId, options) => {
|
|
20801
|
+
// Validate first: an input error must surface as INVALID_PARAMETERS regardless of
|
|
20802
|
+
// whether a client is active, and validation needs nothing from the client.
|
|
20803
|
+
const body = toContentFlagBody(validateContentFlagOptions('flagMessage', options));
|
|
20804
|
+
const client = getActiveClient();
|
|
20805
|
+
client.log('message/flag', messageId);
|
|
20806
|
+
const { data: payload } = await client.http.post(`/api/v5/messages/${encodeURIComponent(messageId)}/flags`, body);
|
|
20807
|
+
if (client.cache) {
|
|
20808
|
+
const messagePayload = await prepareMessagePayload(payload);
|
|
20809
|
+
ingestInCache(messagePayload);
|
|
20810
|
+
}
|
|
20811
|
+
fireEvent('message.flagged', payload);
|
|
20812
|
+
return !!payload;
|
|
20813
|
+
};
|
|
20814
|
+
|
|
20815
|
+
/**
|
|
20816
|
+
* Legacy `flagMessage(messageId, reason?)` implementation, kept verbatim so existing
|
|
20817
|
+
* callers keep the exact request body they had before the options contract existed:
|
|
20818
|
+
*
|
|
20819
|
+
* - predefined `ContentFlagReasonEnum` value → `{ reason: <value>, detail: '' }`
|
|
20820
|
+
* - any other non-empty string (including the literal `'Others'`) → `{ reason: 'Others', detail: <string> }`
|
|
20821
|
+
* - `undefined` or `''` → `{ reason: 'Others', detail: '' }`
|
|
20822
|
+
* (for `undefined` the message body carries `detail: undefined`, which JSON serialization omits)
|
|
20823
|
+
*
|
|
20824
|
+
* Not exported from the public API. It is reached through the deprecated
|
|
20825
|
+
* `MessageRepository.flagMessage(messageId, reason)` overload; see `api/flagMessage.ts`.
|
|
20826
|
+
*
|
|
20827
|
+
* Not itself deprecated: it implements the deprecated legacy-string overload of the public
|
|
20828
|
+
* `flagMessage`, which carries the deprecation tag. New code should use `flagMessageWithOptions`
|
|
20829
|
+
* (the `{ reason, detail? }` contract). This path stays available throughout the current major version.
|
|
20830
|
+
*/
|
|
20831
|
+
const flagMessageWithReasonString = async (messageId, reason) => {
|
|
20180
20832
|
const client = getActiveClient();
|
|
20181
20833
|
client.log('message/flag', messageId);
|
|
20182
20834
|
const isPredefinedReason = reason &&
|
|
@@ -20193,6 +20845,42 @@ const flagMessage = async (messageId, reason) => {
|
|
|
20193
20845
|
fireEvent('message.flagged', payload);
|
|
20194
20846
|
return !!payload;
|
|
20195
20847
|
};
|
|
20848
|
+
|
|
20849
|
+
/* begin_public_function
|
|
20850
|
+
id: message.flag
|
|
20851
|
+
*/
|
|
20852
|
+
/**
|
|
20853
|
+
* ```js
|
|
20854
|
+
* import { MessageRepository, ContentFlagReasonEnum } from '@amityco/ts-sdk'
|
|
20855
|
+
*
|
|
20856
|
+
* // No reason.
|
|
20857
|
+
* await MessageRepository.flagMessage(messageId)
|
|
20858
|
+
*
|
|
20859
|
+
* // Category plus optional reporter context (any category, including Others).
|
|
20860
|
+
* await MessageRepository.flagMessage(messageId, {
|
|
20861
|
+
* reason: ContentFlagReasonEnum.SpamOrScams,
|
|
20862
|
+
* detail: 'Repeated promotional links',
|
|
20863
|
+
* })
|
|
20864
|
+
* ```
|
|
20865
|
+
*
|
|
20866
|
+
* Reports a message to moderation. Three call shapes are accepted (see the overloads):
|
|
20867
|
+
* id-only, an options object, and the deprecated legacy reason string. The public
|
|
20868
|
+
* name only dispatches on the input shape; the two implementations are kept separate
|
|
20869
|
+
* so the legacy path is untouched by the new contract:
|
|
20870
|
+
*
|
|
20871
|
+
* - `undefined` or a string → `flagMessageWithReasonString` (unchanged legacy mapping)
|
|
20872
|
+
* - anything else → `flagMessageWithOptions` (validated `{ reason, detail? }`)
|
|
20873
|
+
*
|
|
20874
|
+
* @param messageId The ID of the message to flag
|
|
20875
|
+
* @param input an options object `{ reason, detail? }`, a legacy reason string, or nothing
|
|
20876
|
+
* @returns `true` when the report was created
|
|
20877
|
+
*
|
|
20878
|
+
* @category Message API
|
|
20879
|
+
* @async
|
|
20880
|
+
* */
|
|
20881
|
+
const flagMessage = (messageId, input) => isLegacyContentFlagReason(input)
|
|
20882
|
+
? flagMessageWithReasonString(messageId, input)
|
|
20883
|
+
: flagMessageWithOptions(messageId, input);
|
|
20196
20884
|
/* end_public_function */
|
|
20197
20885
|
|
|
20198
20886
|
/* begin_public_function
|
|
@@ -21912,6 +22600,9 @@ class ChannelLiveCollectionController extends LiveCollectionController {
|
|
|
21912
22600
|
case 'lastCreated':
|
|
21913
22601
|
channels = channels.sort(sortByLastCreated);
|
|
21914
22602
|
break;
|
|
22603
|
+
case 'lastMessage':
|
|
22604
|
+
channels = channels.sort(sortByLastMessage);
|
|
22605
|
+
break;
|
|
21915
22606
|
case 'displayName':
|
|
21916
22607
|
/*
|
|
21917
22608
|
* The server returns channels with empty | null displayName's first before
|
|
@@ -21972,9 +22663,8 @@ class ChannelLiveCollectionController extends LiveCollectionController {
|
|
|
21972
22663
|
objectResolverEngine.resolve(message.channelId, "channel" /* Amity.ReferenceType.CHANNEL */);
|
|
21973
22664
|
}
|
|
21974
22665
|
else {
|
|
21975
|
-
|
|
21976
|
-
|
|
21977
|
-
});
|
|
22666
|
+
// lastActivity / lastMessageAt are moved by channelActivityOnLoginHandler
|
|
22667
|
+
// (session-wide, monotonic, one write). This listener only re-sorts.
|
|
21978
22668
|
callback(message);
|
|
21979
22669
|
}
|
|
21980
22670
|
});
|
|
@@ -21992,9 +22682,7 @@ class ChannelLiveCollectionController extends LiveCollectionController {
|
|
|
21992
22682
|
])) === null || _a === void 0 ? void 0 : _a.data;
|
|
21993
22683
|
if (!cacheData)
|
|
21994
22684
|
return;
|
|
21995
|
-
|
|
21996
|
-
lastActivity: message.createdAt,
|
|
21997
|
-
});
|
|
22685
|
+
// Timestamps are moved by channelActivityOnLoginHandler; only re-sort here.
|
|
21998
22686
|
callback(message);
|
|
21999
22687
|
});
|
|
22000
22688
|
}, 'channelId', 'channel'),
|
|
@@ -22732,9 +23420,7 @@ class SearchChannelLiveCollectionController extends LiveCollectionController {
|
|
|
22732
23420
|
])) === null || _a === void 0 ? void 0 : _a.data;
|
|
22733
23421
|
if (!cacheData)
|
|
22734
23422
|
return;
|
|
22735
|
-
|
|
22736
|
-
lastActivity: message.createdAt,
|
|
22737
|
-
});
|
|
23423
|
+
// Timestamps are moved by channelActivityOnLoginHandler; only re-sort here.
|
|
22738
23424
|
callback(message);
|
|
22739
23425
|
});
|
|
22740
23426
|
}, 'channelId', 'channel'),
|
|
@@ -22751,9 +23437,7 @@ class SearchChannelLiveCollectionController extends LiveCollectionController {
|
|
|
22751
23437
|
])) === null || _a === void 0 ? void 0 : _a.data;
|
|
22752
23438
|
if (!cacheData)
|
|
22753
23439
|
return;
|
|
22754
|
-
|
|
22755
|
-
lastActivity: message.createdAt,
|
|
22756
|
-
});
|
|
23440
|
+
// Timestamps are moved by channelActivityOnLoginHandler; only re-sort here.
|
|
22757
23441
|
callback(message);
|
|
22758
23442
|
});
|
|
22759
23443
|
}, 'channelId', 'channel'),
|
|
@@ -23176,7 +23860,7 @@ const onUserDeleted$1 = (channelId) => (callback) => {
|
|
|
23176
23860
|
'get',
|
|
23177
23861
|
channelUserCacheKey,
|
|
23178
23862
|
])) === null || _a === void 0 ? void 0 : _a.data;
|
|
23179
|
-
upsertInCache(['channelUsers', 'get', channelUserCacheKey], Object.assign(Object.assign({}, cacheData), { user }));
|
|
23863
|
+
upsertInCache(['channelUsers', 'get', channelUserCacheKey], Object.assign(Object.assign({}, cacheData), { user: userLinkedObject(user) }));
|
|
23180
23864
|
const channel = (_b = pullFromCache([
|
|
23181
23865
|
'channel',
|
|
23182
23866
|
'get',
|
|
@@ -24649,10 +25333,10 @@ const onUserDeleted = (communityId) => (callback) => {
|
|
|
24649
25333
|
'get',
|
|
24650
25334
|
communityUserCacheKey,
|
|
24651
25335
|
])) === null || _a === void 0 ? void 0 : _a.data;
|
|
24652
|
-
pushToCache(['communityUsers', 'get', communityUserCacheKey], Object.assign(Object.assign({}, cacheData), { user }));
|
|
25336
|
+
pushToCache(['communityUsers', 'get', communityUserCacheKey], Object.assign(Object.assign({}, cacheData), { user: userLinkedObject(user) }));
|
|
24653
25337
|
const community = (_b = pullFromCache(['community', 'get', communityId])) === null || _b === void 0 ? void 0 : _b.data;
|
|
24654
25338
|
callback(community, [
|
|
24655
|
-
Object.assign(Object.assign({}, cacheData), { user }),
|
|
25339
|
+
Object.assign(Object.assign({}, cacheData), { user: userLinkedObject(user) }),
|
|
24656
25340
|
]);
|
|
24657
25341
|
};
|
|
24658
25342
|
return createEventSubscriber(client, 'user.deleted', 'user.deleted', filter);
|
|
@@ -27338,23 +28022,22 @@ const declineComment = async (commentId, reason) => {
|
|
|
27338
28022
|
};
|
|
27339
28023
|
/* end_public_function */
|
|
27340
28024
|
|
|
27341
|
-
/* begin_public_function
|
|
27342
|
-
id: comment.flag
|
|
27343
|
-
*/
|
|
27344
28025
|
/**
|
|
27345
|
-
*
|
|
27346
|
-
*
|
|
27347
|
-
* const flagged = await CommentRepository.flagComment(commentId, reason)
|
|
27348
|
-
* ```
|
|
28026
|
+
* Legacy `flagComment(commentId, reason?)` implementation, kept verbatim so existing
|
|
28027
|
+
* callers keep the exact request body they had before the options contract existed:
|
|
27349
28028
|
*
|
|
27350
|
-
*
|
|
27351
|
-
*
|
|
27352
|
-
*
|
|
28029
|
+
* - predefined `ContentFlagReasonEnum` value → `{ reason: <value>, detail: '' }`
|
|
28030
|
+
* - any other non-empty string (including the literal `'Others'`) → `{ reason: 'Others', detail: <string> }`
|
|
28031
|
+
* - `undefined` or `''` → `{ reason: 'Others', detail: '' }`
|
|
27353
28032
|
*
|
|
27354
|
-
*
|
|
27355
|
-
*
|
|
27356
|
-
*
|
|
27357
|
-
|
|
28033
|
+
* Not exported from the public API. It is reached through the deprecated
|
|
28034
|
+
* `CommentRepository.flagComment(commentId, reason)` overload; see `api/flagComment.ts`.
|
|
28035
|
+
*
|
|
28036
|
+
* Not itself deprecated: it implements the deprecated legacy-string overload of the public
|
|
28037
|
+
* `flagComment`, which carries the deprecation tag. New code should use `flagCommentWithOptions`
|
|
28038
|
+
* (the `{ reason, detail? }` contract). This path stays available throughout the current major version.
|
|
28039
|
+
*/
|
|
28040
|
+
const flagCommentWithReasonString = async (commentId, reason) => {
|
|
27358
28041
|
const client = getActiveClient();
|
|
27359
28042
|
client.log('comment/flagComment', commentId);
|
|
27360
28043
|
const isPredefinedReason = reason &&
|
|
@@ -27370,6 +28053,67 @@ const flagComment = async (commentId, reason) => {
|
|
|
27370
28053
|
fireEvent('comment.flagged', payload);
|
|
27371
28054
|
return !!payload;
|
|
27372
28055
|
};
|
|
28056
|
+
|
|
28057
|
+
/**
|
|
28058
|
+
* New `flagComment(commentId, { reason, detail? })` implementation. Any category,
|
|
28059
|
+
* including `Others`, may carry a `detail`. `detail` is forwarded unchanged (empty and
|
|
28060
|
+
* whitespace-only strings included) and omitted from the request body when `undefined`;
|
|
28061
|
+
* the backend enforces the 300-character limit.
|
|
28062
|
+
*
|
|
28063
|
+
* Not exported from the public API. It is reached through the
|
|
28064
|
+
* `CommentRepository.flagComment(commentId, options)` overload; see `api/flagComment.ts`.
|
|
28065
|
+
*
|
|
28066
|
+
* @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when `options` is malformed, before any request is made
|
|
28067
|
+
*/
|
|
28068
|
+
const flagCommentWithOptions = async (commentId, options) => {
|
|
28069
|
+
// Validate first: an input error must surface as INVALID_PARAMETERS regardless of
|
|
28070
|
+
// whether a client is active, and validation needs nothing from the client.
|
|
28071
|
+
const body = toContentFlagBody(validateContentFlagOptions('flagComment', options));
|
|
28072
|
+
const client = getActiveClient();
|
|
28073
|
+
client.log('comment/flagComment', commentId);
|
|
28074
|
+
const { data: payload } = await client.http.post(`/api/v3/comments/${encodeURIComponent(commentId)}/flag`, body);
|
|
28075
|
+
if (client.cache) {
|
|
28076
|
+
ingestInCache(payload);
|
|
28077
|
+
}
|
|
28078
|
+
fireEvent('comment.flagged', payload);
|
|
28079
|
+
return !!payload;
|
|
28080
|
+
};
|
|
28081
|
+
|
|
28082
|
+
/* begin_public_function
|
|
28083
|
+
id: comment.flag
|
|
28084
|
+
*/
|
|
28085
|
+
/**
|
|
28086
|
+
* ```js
|
|
28087
|
+
* import { CommentRepository, ContentFlagReasonEnum } from '@amityco/ts-sdk'
|
|
28088
|
+
*
|
|
28089
|
+
* // No reason: sent as Others with an empty detail.
|
|
28090
|
+
* await CommentRepository.flagComment(commentId)
|
|
28091
|
+
*
|
|
28092
|
+
* // Category plus optional reporter context (any category, including Others).
|
|
28093
|
+
* await CommentRepository.flagComment(commentId, {
|
|
28094
|
+
* reason: ContentFlagReasonEnum.SpamOrScams,
|
|
28095
|
+
* detail: 'Repeated promotional links',
|
|
28096
|
+
* })
|
|
28097
|
+
* ```
|
|
28098
|
+
*
|
|
28099
|
+
* Reports a comment to moderation. Three call shapes are accepted (see the overloads):
|
|
28100
|
+
* id-only, an options object, and the deprecated legacy reason string. The public
|
|
28101
|
+
* name only dispatches on the input shape; the two implementations are kept separate
|
|
28102
|
+
* so the legacy path is untouched by the new contract:
|
|
28103
|
+
*
|
|
28104
|
+
* - `undefined` or a string → `flagCommentWithReasonString` (unchanged legacy mapping)
|
|
28105
|
+
* - anything else → `flagCommentWithOptions` (validated `{ reason, detail? }`)
|
|
28106
|
+
*
|
|
28107
|
+
* @param commentId The ID of the comment to flag
|
|
28108
|
+
* @param input an options object `{ reason, detail? }`, a legacy reason string, or nothing
|
|
28109
|
+
* @returns `true` when the report was created
|
|
28110
|
+
*
|
|
28111
|
+
* @category Comment API
|
|
28112
|
+
* @async
|
|
28113
|
+
* */
|
|
28114
|
+
const flagComment = (commentId, input) => isLegacyContentFlagReason(input)
|
|
28115
|
+
? flagCommentWithReasonString(commentId, input)
|
|
28116
|
+
: flagCommentWithOptions(commentId, input);
|
|
27373
28117
|
/* end_public_function */
|
|
27374
28118
|
|
|
27375
28119
|
/* begin_public_function
|
|
@@ -29723,23 +30467,47 @@ const declinePost = async (postId) => {
|
|
|
29723
30467
|
};
|
|
29724
30468
|
/* end_public_function */
|
|
29725
30469
|
|
|
29726
|
-
/* begin_public_function
|
|
29727
|
-
id: post.flag
|
|
29728
|
-
*/
|
|
29729
30470
|
/**
|
|
29730
|
-
*
|
|
29731
|
-
*
|
|
29732
|
-
*
|
|
29733
|
-
*
|
|
30471
|
+
* New `flagPost(postId, { reason, detail? })` implementation. Any category, including
|
|
30472
|
+
* `Others`, may carry a `detail`. `detail` is forwarded unchanged (empty and
|
|
30473
|
+
* whitespace-only strings included) and omitted from the request body when `undefined`;
|
|
30474
|
+
* the backend enforces the 300-character limit.
|
|
29734
30475
|
*
|
|
29735
|
-
*
|
|
29736
|
-
*
|
|
29737
|
-
* @returns a boolean
|
|
30476
|
+
* Not exported from the public API. It is reached through the
|
|
30477
|
+
* `PostRepository.flagPost(postId, options)` overload; see `api/flagPost.ts`.
|
|
29738
30478
|
*
|
|
29739
|
-
* @
|
|
29740
|
-
|
|
29741
|
-
|
|
29742
|
-
|
|
30479
|
+
* @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when `options` is malformed, before any request is made
|
|
30480
|
+
*/
|
|
30481
|
+
const flagPostWithOptions = async (postId, options) => {
|
|
30482
|
+
// Validate first: an input error must surface as INVALID_PARAMETERS regardless of
|
|
30483
|
+
// whether a client is active, and validation needs nothing from the client.
|
|
30484
|
+
const body = toContentFlagBody(validateContentFlagOptions('flagPost', options));
|
|
30485
|
+
const client = getActiveClient();
|
|
30486
|
+
client.log('post/flagPost', postId);
|
|
30487
|
+
const { data: payload } = await client.http.post(`/api/v3/posts/${encodeURIComponent(postId)}/flag`, body);
|
|
30488
|
+
if (client.cache) {
|
|
30489
|
+
ingestInCache(prepareMembershipPayload(payload, 'communityUsers'));
|
|
30490
|
+
}
|
|
30491
|
+
fireEvent('post.flagged', payload);
|
|
30492
|
+
return !!payload;
|
|
30493
|
+
};
|
|
30494
|
+
|
|
30495
|
+
/**
|
|
30496
|
+
* Legacy `flagPost(postId, reason?)` implementation, kept verbatim so existing
|
|
30497
|
+
* callers keep the exact request body they had before the options contract existed:
|
|
30498
|
+
*
|
|
30499
|
+
* - predefined `ContentFlagReasonEnum` value → `{ reason: <value>, detail: '' }`
|
|
30500
|
+
* - any other non-empty string (including the literal `'Others'`) → `{ reason: 'Others', detail: <string> }`
|
|
30501
|
+
* - `undefined` or `''` → `{ reason: 'Others', detail: '' }`
|
|
30502
|
+
*
|
|
30503
|
+
* Not exported from the public API. It is reached through the deprecated
|
|
30504
|
+
* `PostRepository.flagPost(postId, reason)` overload; see `api/flagPost.ts`.
|
|
30505
|
+
*
|
|
30506
|
+
* Not itself deprecated: it implements the deprecated legacy-string overload of the public
|
|
30507
|
+
* `flagPost`, which carries the deprecation tag. New code should use `flagPostWithOptions`
|
|
30508
|
+
* (the `{ reason, detail? }` contract). This path stays available throughout the current major version.
|
|
30509
|
+
*/
|
|
30510
|
+
const flagPostWithReasonString = async (postId, reason) => {
|
|
29743
30511
|
const client = getActiveClient();
|
|
29744
30512
|
client.log('post/flagPost', postId);
|
|
29745
30513
|
const isPredefinedReason = reason &&
|
|
@@ -29755,6 +30523,42 @@ const flagPost = async (postId, reason) => {
|
|
|
29755
30523
|
fireEvent('post.flagged', payload);
|
|
29756
30524
|
return !!payload;
|
|
29757
30525
|
};
|
|
30526
|
+
|
|
30527
|
+
/* begin_public_function
|
|
30528
|
+
id: post.flag
|
|
30529
|
+
*/
|
|
30530
|
+
/**
|
|
30531
|
+
* ```js
|
|
30532
|
+
* import { PostRepository, ContentFlagReasonEnum } from '@amityco/ts-sdk'
|
|
30533
|
+
*
|
|
30534
|
+
* // No reason.
|
|
30535
|
+
* await PostRepository.flagPost(postId)
|
|
30536
|
+
*
|
|
30537
|
+
* // Category plus optional reporter context (any category, including Others).
|
|
30538
|
+
* await PostRepository.flagPost(postId, {
|
|
30539
|
+
* reason: ContentFlagReasonEnum.SpamOrScams,
|
|
30540
|
+
* detail: 'Repeated promotional links',
|
|
30541
|
+
* })
|
|
30542
|
+
* ```
|
|
30543
|
+
*
|
|
30544
|
+
* Reports a post to moderation. Three call shapes are accepted (see the overloads):
|
|
30545
|
+
* id-only, an options object, and the deprecated legacy reason string. The public
|
|
30546
|
+
* name only dispatches on the input shape; the two implementations are kept separate
|
|
30547
|
+
* so the legacy path is untouched by the new contract:
|
|
30548
|
+
*
|
|
30549
|
+
* - `undefined` or a string → `flagPostWithReasonString` (unchanged legacy mapping)
|
|
30550
|
+
* - anything else → `flagPostWithOptions` (validated `{ reason, detail? }`)
|
|
30551
|
+
*
|
|
30552
|
+
* @param postId The ID of the post to flag
|
|
30553
|
+
* @param input an options object `{ reason, detail? }`, a legacy reason string, or nothing
|
|
30554
|
+
* @returns `true` when the report was created
|
|
30555
|
+
*
|
|
30556
|
+
* @category Post API
|
|
30557
|
+
* @async
|
|
30558
|
+
* */
|
|
30559
|
+
const flagPost = (postId, input) => isLegacyContentFlagReason(input)
|
|
30560
|
+
? flagPostWithReasonString(postId, input)
|
|
30561
|
+
: flagPostWithOptions(postId, input);
|
|
29758
30562
|
/* end_public_function */
|
|
29759
30563
|
|
|
29760
30564
|
/* begin_public_function
|
|
@@ -37135,4 +37939,4 @@ class AmityAttachmentProductTags {
|
|
|
37135
37939
|
}
|
|
37136
37940
|
}
|
|
37137
37941
|
|
|
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 };
|
|
37942
|
+
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 };
|