@amityco/ts-sdk 7.29.1-cc0fba8.0 → 7.30.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 +2 -1
- package/dist/@types/core/errors.d.ts.map +1 -1
- package/dist/@types/core/events.d.ts +4 -0
- package/dist/@types/core/events.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/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 +70 -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/communityRepository/communityMembership/events/onUserDeleted.d.ts.map +1 -1
- package/dist/communityRepository/utils/payload.d.ts.map +1 -1
- package/dist/core/events.d.ts +3 -3
- package/dist/core/events.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/group/utils/withUser.d.ts +3 -3
- package/dist/group/utils/withUser.d.ts.map +1 -1
- package/dist/index.cjs.js +826 -167
- package/dist/index.esm.js +827 -169
- 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/postRepository/api/tests/integration/setup.d.ts +3 -3
- 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.map +1 -1
- package/dist/userRepository/api/isUserFlaggedByMe.d.ts +11 -3
- package/dist/userRepository/api/isUserFlaggedByMe.d.ts.map +1 -1
- package/dist/userRepository/api/queryBlockedUsers.d.ts.map +1 -1
- package/dist/userRepository/api/unflagUser.d.ts +27 -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 +22 -0
- package/dist/userRepository/internalApi/getFlagsByMe.d.ts.map +1 -0
- package/dist/userRepository/internalApi/tests/getFlagsByMe.test.d.ts +2 -0
- package/dist/userRepository/internalApi/tests/getFlagsByMe.test.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/getUser.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.map +1 -1
- package/dist/userRepository/utils/userFlagByMe.d.ts +68 -0
- package/dist/userRepository/utils/userFlagByMe.d.ts.map +1 -0
- package/dist/userRepository/utils/userReport.d.ts +16 -1
- package/dist/userRepository/utils/userReport.d.ts.map +1 -1
- package/dist/utils/bloomFilter.d.ts +14 -0
- package/dist/utils/bloomFilter.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';
|
|
@@ -54,6 +54,21 @@ var UserProfileFieldEnum;
|
|
|
54
54
|
UserProfileFieldEnum["DESCRIPTION"] = "description";
|
|
55
55
|
UserProfileFieldEnum["AVATAR"] = "avatarFileId";
|
|
56
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 `user.getUserFlagsByMe()` 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 = {}));
|
|
57
72
|
|
|
58
73
|
const FileType = Object.freeze({
|
|
59
74
|
FILE: 'file',
|
|
@@ -328,8 +343,8 @@ var AmityLiveViewerCountMode;
|
|
|
328
343
|
|
|
329
344
|
function getVersion() {
|
|
330
345
|
try {
|
|
331
|
-
// the string ''v7.
|
|
332
|
-
return 'v7.
|
|
346
|
+
// the string ''v7.30.0-esm'' should be replaced by actual value by @rollup/plugin-replace
|
|
347
|
+
return 'v7.30.0-esm';
|
|
333
348
|
}
|
|
334
349
|
catch (error) {
|
|
335
350
|
return '__dev__';
|
|
@@ -1615,6 +1630,24 @@ const sortByLastUpdated = ({ updatedAt: a = 0 }, { updatedAt: b = 0 }) => new Da
|
|
|
1615
1630
|
* Sorting a collection by the items with most recent activity
|
|
1616
1631
|
*/
|
|
1617
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
|
+
};
|
|
1618
1651
|
/**
|
|
1619
1652
|
* Filter items by untilAt timestamp based on sort direction
|
|
1620
1653
|
* - For 'lastCreated' (newest first): untilAt acts as lower bound, excludes items older than boundary
|
|
@@ -1699,8 +1732,13 @@ const scheduleTask = (task) => {
|
|
|
1699
1732
|
clearTimeout(timer);
|
|
1700
1733
|
tasks.push(task);
|
|
1701
1734
|
timer = setTimeout(() => {
|
|
1702
|
-
|
|
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;
|
|
1703
1740
|
tasks = [];
|
|
1741
|
+
batch.forEach(fn => fn());
|
|
1704
1742
|
}, 0);
|
|
1705
1743
|
};
|
|
1706
1744
|
|
|
@@ -1798,18 +1836,434 @@ async function modifyMqttConnection() {
|
|
|
1798
1836
|
}
|
|
1799
1837
|
}
|
|
1800
1838
|
|
|
1839
|
+
class SessionWatcher {
|
|
1840
|
+
constructor() {
|
|
1841
|
+
this._sessionState = "notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */;
|
|
1842
|
+
this._listener = new Map();
|
|
1843
|
+
}
|
|
1844
|
+
onSessionStateChange(callback) {
|
|
1845
|
+
this._listener.set(callback, callback);
|
|
1846
|
+
return () => {
|
|
1847
|
+
this._listener.delete(callback);
|
|
1848
|
+
};
|
|
1849
|
+
}
|
|
1850
|
+
setSessionState(state, reason) {
|
|
1851
|
+
if (this._sessionState === state)
|
|
1852
|
+
return;
|
|
1853
|
+
this._sessionState = state;
|
|
1854
|
+
this._listener.forEach(cb => cb(state, reason));
|
|
1855
|
+
}
|
|
1856
|
+
destroy() {
|
|
1857
|
+
this._listener.clear();
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
let instance$b;
|
|
1861
|
+
var SessionWatcher$1 = {
|
|
1862
|
+
getInstance: () => {
|
|
1863
|
+
if (!instance$b) {
|
|
1864
|
+
instance$b = new SessionWatcher();
|
|
1865
|
+
}
|
|
1866
|
+
return instance$b;
|
|
1867
|
+
},
|
|
1868
|
+
};
|
|
1869
|
+
|
|
1870
|
+
/**
|
|
1871
|
+
* ```js
|
|
1872
|
+
* import { onSessionStateChange } from '@amityco/ts-sdk'
|
|
1873
|
+
* const dispose = onSessionStateChange((state: Amity.SessionStates) => {
|
|
1874
|
+
* // ...
|
|
1875
|
+
* })
|
|
1876
|
+
* ```
|
|
1877
|
+
*
|
|
1878
|
+
* Fired when any {@link Amity.Client} has a session state change
|
|
1879
|
+
*
|
|
1880
|
+
* @param callback The function to call when the event was fired
|
|
1881
|
+
* @returns an {@link Amity.Unsubscriber} function to stop listening
|
|
1882
|
+
*
|
|
1883
|
+
* @category Client Events
|
|
1884
|
+
*/
|
|
1885
|
+
const onSessionStateChange = (callback) => SessionWatcher$1.getInstance().onSessionStateChange(callback);
|
|
1886
|
+
|
|
1887
|
+
/**
|
|
1888
|
+
* ```js
|
|
1889
|
+
* import { getFlagsByMe } from '~/userRepository/internalApi/getFlagsByMe'
|
|
1890
|
+
* const { isFlagByMe, flags } = await getFlagsByMe('userId')
|
|
1891
|
+
* ```
|
|
1892
|
+
*
|
|
1893
|
+
* The v4 read behind `user.getUserFlagsByMe()`, the background confirm of
|
|
1894
|
+
* `user.isFlaggedByMe`, and the deprecated `UserRepository.isUserFlaggedByMe`:
|
|
1895
|
+
* `GET /api/v4/users/:userId/isFlagByMe`, returned as received. Nothing is filtered, cached or
|
|
1896
|
+
* persisted here, and `hashFlag` is not consulted.
|
|
1897
|
+
* This is the Web counterpart of iOS `ASCUserService.getFlagsByMe` and Android
|
|
1898
|
+
* `UserRepository.getFlagsByMe`, kept apart from the deprecated public function so the
|
|
1899
|
+
* user-object read does not change if that function ever does.
|
|
1900
|
+
*
|
|
1901
|
+
* @param userId The ID of the user whose reports by the caller are read
|
|
1902
|
+
* @returns the caller's {@link Amity.UserFlagByMe} on that user
|
|
1903
|
+
*
|
|
1904
|
+
* @category Private
|
|
1905
|
+
* @async
|
|
1906
|
+
*/
|
|
1907
|
+
const getFlagsByMe = async (userId) => {
|
|
1908
|
+
const client = getActiveClient();
|
|
1909
|
+
client.log('user/getFlagsByMe', userId);
|
|
1910
|
+
const { data } = await client.http.get(`/api/v4/users/${encodeURIComponent(userId)}/isFlagByMe`);
|
|
1911
|
+
return data;
|
|
1912
|
+
};
|
|
1913
|
+
|
|
1914
|
+
/* eslint-disable no-bitwise */
|
|
1915
|
+
/**
|
|
1916
|
+
* Client-side test of the `hashFlag` Bloom filter the backend attaches to flaggable objects.
|
|
1917
|
+
*
|
|
1918
|
+
* The filter holds the ids of every user who flagged the object: `bits` is its size, `hashes`
|
|
1919
|
+
* the number of hash functions per id and `hash` the bit array, base64-encoded. This is a port
|
|
1920
|
+
* of the test iOS (`_BloomFilter`) and Android (`EkoBloomFilter`) already run: FNV-1a 32-bit
|
|
1921
|
+
* over the UTF-8 bytes of the id with seeds `0` and `1576284489`, combined by double hashing
|
|
1922
|
+
* (`(a + i·b) mod bits`). A Bloom filter has no false negatives, so `false` is exact; `true`
|
|
1923
|
+
* means "probably", because another id's hashes may have set the same bits. The filter knows
|
|
1924
|
+
* nothing about report types.
|
|
1925
|
+
*/
|
|
1926
|
+
const FNV_OFFSET_BASIS = 0x811c9dc5;
|
|
1927
|
+
const FNV_PRIME = 16777619;
|
|
1928
|
+
const SECOND_SEED = 1576284489;
|
|
1929
|
+
/** FNV-1a with the same mixing step and signed-byte handling as the native SDKs. */
|
|
1930
|
+
const fnv1a = (bytes, seed) => {
|
|
1931
|
+
let a = (FNV_OFFSET_BASIS ^ seed) >>> 0;
|
|
1932
|
+
for (let i = 0; i < bytes.length; i += 1) {
|
|
1933
|
+
// Bytes are `char` on iOS and `byte` on Android: both signed, so 0x80..0xff sign-extend.
|
|
1934
|
+
const byte = bytes[i] > 127 ? bytes[i] - 256 : bytes[i];
|
|
1935
|
+
a = Math.imul((a ^ byte) >>> 0, FNV_PRIME) >>> 0;
|
|
1936
|
+
}
|
|
1937
|
+
a = (a + (a << 13)) >>> 0;
|
|
1938
|
+
a = (a ^ (a >>> 7)) >>> 0;
|
|
1939
|
+
a = (a + (a << 3)) >>> 0;
|
|
1940
|
+
a = (a ^ (a >>> 17)) >>> 0;
|
|
1941
|
+
a = (a + (a << 5)) >>> 0;
|
|
1942
|
+
return a;
|
|
1943
|
+
};
|
|
1944
|
+
/** Bit positions for `value`, reproducing the native SDKs' signed 32-bit arithmetic. */
|
|
1945
|
+
const locationsFor = (value, bits, hashes) => {
|
|
1946
|
+
const bytes = toUint8Array(encode(value)); // UTF-8 bytes of the id
|
|
1947
|
+
const a = fnv1a(bytes, 0) | 0;
|
|
1948
|
+
const b = fnv1a(bytes, SECOND_SEED) | 0;
|
|
1949
|
+
const locations = [];
|
|
1950
|
+
let x = a % bits;
|
|
1951
|
+
for (let i = 0; i < hashes; i += 1) {
|
|
1952
|
+
locations.push(x < 0 ? x + bits : x);
|
|
1953
|
+
x = ((x + b) | 0) % bits;
|
|
1954
|
+
}
|
|
1955
|
+
return locations;
|
|
1956
|
+
};
|
|
1957
|
+
/** Standard or URL-safe base64, padding optional. Anything else is not a filter we can read. */
|
|
1958
|
+
const BASE64 = /^[A-Za-z0-9+/_-]*={0,2}$/;
|
|
1959
|
+
/**
|
|
1960
|
+
* Whether `value` is probably in the filter. `false` is exact; `true` means "maybe", which may be
|
|
1961
|
+
* a false positive. `null` (the server's value when nobody has flagged the object), a missing
|
|
1962
|
+
* filter or a missing value answer `false`.
|
|
1963
|
+
*
|
|
1964
|
+
* A filter that cannot be read answers `true` ("maybe") and never throws, so the caller asks the
|
|
1965
|
+
* server instead of trusting a "definitely not" it cannot back up (REQ-017): `bits` or `hashes`
|
|
1966
|
+
* not a positive integer, a `hash` that is not base64, or a `hash` shorter than `ceil(bits / 8)`
|
|
1967
|
+
* bytes.
|
|
1968
|
+
*/
|
|
1969
|
+
const testBloomFilter = (hashFlag, value) => {
|
|
1970
|
+
if (!hashFlag || !value)
|
|
1971
|
+
return false;
|
|
1972
|
+
const { bits, hashes, hash } = hashFlag;
|
|
1973
|
+
if (!Number.isInteger(bits) || bits <= 0)
|
|
1974
|
+
return true;
|
|
1975
|
+
if (!Number.isInteger(hashes) || hashes <= 0)
|
|
1976
|
+
return true;
|
|
1977
|
+
if (typeof hash !== 'string' || !BASE64.test(hash))
|
|
1978
|
+
return true;
|
|
1979
|
+
let buckets;
|
|
1980
|
+
try {
|
|
1981
|
+
buckets = toUint8Array(hash);
|
|
1982
|
+
}
|
|
1983
|
+
catch (_a) {
|
|
1984
|
+
return true;
|
|
1985
|
+
}
|
|
1986
|
+
if (buckets.length < Math.ceil(bits / 8))
|
|
1987
|
+
return true;
|
|
1988
|
+
const locations = locationsFor(value, bits, hashes);
|
|
1989
|
+
for (let i = 0; i < locations.length; i += 1) {
|
|
1990
|
+
const index = Math.floor(locations[i] / 8);
|
|
1991
|
+
const bit = 1 << locations[i] % 8;
|
|
1992
|
+
if ((buckets[index] & bit) === 0)
|
|
1993
|
+
return false;
|
|
1994
|
+
}
|
|
1995
|
+
return true;
|
|
1996
|
+
};
|
|
1997
|
+
|
|
1998
|
+
/**
|
|
1999
|
+
* Flag state of the current user on a target user, behind `Amity.User.isFlaggedByMe` and
|
|
2000
|
+
* `Amity.User.getUserFlagsByMe()` (`user.check_flag_by_me` v1, REQ-011, REQ-012, REQ-016).
|
|
2001
|
+
*
|
|
2002
|
+
* `hashFlag` is a Bloom filter of reporter ids. "Definitely not" is exact. "Maybe" can be a
|
|
2003
|
+
* false positive caused by other reporters, so the SDK confirms it with the server once per
|
|
2004
|
+
* `hashFlag` value and records the answer in the cache under its own key, never on the user's
|
|
2005
|
+
* cache row (REQ-015). Observers are told through `local.user.flagConfirmed` when a recorded
|
|
2006
|
+
* answer changes what a user object would show.
|
|
2007
|
+
*
|
|
2008
|
+
* Not exported from the public API.
|
|
2009
|
+
*/
|
|
2010
|
+
/** Confirms for the same user are at least this far apart, as Android's `EkoFlag.expired`. */
|
|
2011
|
+
const FLAG_CONFIRM_INTERVAL = 1000;
|
|
2012
|
+
const cacheKey = (userId) => ['userFlagByMe', 'get', userId];
|
|
2013
|
+
const lastConfirmAt = new Map();
|
|
2014
|
+
/**
|
|
2015
|
+
* Bumped by the current user's own flag and unflag. A confirm or exact read that started before
|
|
2016
|
+
* one of them must not record its answer, because the write may have changed it while the
|
|
2017
|
+
* `hashFlag` stayed the same.
|
|
2018
|
+
*/
|
|
2019
|
+
const generations = new Map();
|
|
2020
|
+
const generationOf = (userId) => { var _a; return (_a = generations.get(userId)) !== null && _a !== void 0 ? _a : 0; };
|
|
2021
|
+
const bumpGeneration = (userId) => generations.set(userId, generationOf(userId) + 1);
|
|
2022
|
+
/** The current user's id, or `undefined` when there is no active client. Never throws. */
|
|
2023
|
+
const currentUserId = () => {
|
|
2024
|
+
try {
|
|
2025
|
+
return getActiveClient().userId;
|
|
2026
|
+
}
|
|
2027
|
+
catch (_a) {
|
|
2028
|
+
return undefined;
|
|
2029
|
+
}
|
|
2030
|
+
};
|
|
2031
|
+
const readRecord = (userId) => {
|
|
2032
|
+
var _a;
|
|
2033
|
+
try {
|
|
2034
|
+
return (_a = pullFromCache(cacheKey(userId))) === null || _a === void 0 ? void 0 : _a.data;
|
|
2035
|
+
}
|
|
2036
|
+
catch (_b) {
|
|
2037
|
+
return undefined;
|
|
2038
|
+
}
|
|
2039
|
+
};
|
|
2040
|
+
/**
|
|
2041
|
+
* The server's answer recorded for this user and this exact `hashFlag`, or `undefined` when there
|
|
2042
|
+
* is none for the current user. Never throws.
|
|
2043
|
+
*/
|
|
2044
|
+
const readConfirmedFlag = (userId, hashFlag) => {
|
|
2045
|
+
const me = currentUserId();
|
|
2046
|
+
if (!me || !hashFlag || typeof hashFlag.hash !== 'string')
|
|
2047
|
+
return undefined;
|
|
2048
|
+
const record = readRecord(userId);
|
|
2049
|
+
if (!record || record.reporterId !== me || record.hash !== hashFlag.hash)
|
|
2050
|
+
return undefined;
|
|
2051
|
+
return record.isFlagByMe;
|
|
2052
|
+
};
|
|
2053
|
+
/**
|
|
2054
|
+
* The v4 request in flight per user, with the generation it started in. A confirm and an exact
|
|
2055
|
+
* read of the same user share one request; a request that started before the current user's own
|
|
2056
|
+
* flag or unflag is not joined.
|
|
2057
|
+
*/
|
|
2058
|
+
const inFlight = new Map();
|
|
2059
|
+
const requestFlagsByMe = (userId) => {
|
|
2060
|
+
const generation = generationOf(userId);
|
|
2061
|
+
const pending = inFlight.get(userId);
|
|
2062
|
+
if (pending && pending.generation === generation)
|
|
2063
|
+
return pending.promise;
|
|
2064
|
+
const promise = getFlagsByMe(userId).finally(() => {
|
|
2065
|
+
var _a;
|
|
2066
|
+
if (((_a = inFlight.get(userId)) === null || _a === void 0 ? void 0 : _a.promise) === promise)
|
|
2067
|
+
inFlight.delete(userId);
|
|
2068
|
+
});
|
|
2069
|
+
inFlight.set(userId, { generation, promise });
|
|
2070
|
+
return promise;
|
|
2071
|
+
};
|
|
2072
|
+
const notifyObservers = (userId) => fireEvent('local.user.flagConfirmed', { userId });
|
|
2073
|
+
/**
|
|
2074
|
+
* Records `isFlagByMe` for `hashFlag`. A "maybe" without a record shows `true`, so observers are
|
|
2075
|
+
* told only when the shown value changes.
|
|
2076
|
+
*/
|
|
2077
|
+
const recordFlag = (userId, hashFlag, isFlagByMe) => {
|
|
2078
|
+
var _a;
|
|
2079
|
+
const me = currentUserId();
|
|
2080
|
+
if (!me || typeof hashFlag.hash !== 'string')
|
|
2081
|
+
return;
|
|
2082
|
+
const shown = (_a = readConfirmedFlag(userId, hashFlag)) !== null && _a !== void 0 ? _a : true;
|
|
2083
|
+
const record = { reporterId: me, hash: hashFlag.hash, isFlagByMe };
|
|
2084
|
+
pushToCache(cacheKey(userId), record);
|
|
2085
|
+
if (shown !== isFlagByMe)
|
|
2086
|
+
notifyObservers(userId);
|
|
2087
|
+
};
|
|
2088
|
+
/**
|
|
2089
|
+
* Starts one background confirm of a "maybe" (REQ-016): at most one in flight per user, and
|
|
2090
|
+
* confirms for the same user at least {@link FLAG_CONFIRM_INTERVAL} apart. A failed confirm
|
|
2091
|
+
* records nothing, surfaces no error, and a later read tries again.
|
|
2092
|
+
*/
|
|
2093
|
+
const confirmFlagByMe = (userId, hashFlag) => {
|
|
2094
|
+
if (inFlight.has(userId))
|
|
2095
|
+
return;
|
|
2096
|
+
const now = Date.now();
|
|
2097
|
+
const last = lastConfirmAt.get(userId);
|
|
2098
|
+
if (last !== undefined && now - last < FLAG_CONFIRM_INTERVAL)
|
|
2099
|
+
return;
|
|
2100
|
+
const me = currentUserId();
|
|
2101
|
+
const generation = generationOf(userId);
|
|
2102
|
+
lastConfirmAt.set(userId, now);
|
|
2103
|
+
requestFlagsByMe(userId)
|
|
2104
|
+
.then(({ isFlagByMe }) => {
|
|
2105
|
+
if (currentUserId() !== me || generationOf(userId) !== generation)
|
|
2106
|
+
return;
|
|
2107
|
+
recordFlag(userId, hashFlag, isFlagByMe);
|
|
2108
|
+
})
|
|
2109
|
+
.catch(() => {
|
|
2110
|
+
// Not an answer: isFlaggedByMe stays true until a later read confirms again.
|
|
2111
|
+
});
|
|
2112
|
+
};
|
|
2113
|
+
/**
|
|
2114
|
+
* The value of `Amity.User.isFlaggedByMe` for a user object whose confirmed answer was captured
|
|
2115
|
+
* (`confirmed`) when the object was linked, so a held object never changes (REQ-014).
|
|
2116
|
+
*
|
|
2117
|
+
* - No current user, `hashFlag: null` or a missing `hashFlag`, or "definitely not": `false`.
|
|
2118
|
+
* - "Maybe" with a confirmed answer: that answer, no request.
|
|
2119
|
+
* - "Maybe" without one: `true`, and one background confirm (REQ-016).
|
|
2120
|
+
*
|
|
2121
|
+
* The linked object declares this as a getter inside an object spread, which the build compiles
|
|
2122
|
+
* to `Object.assign`, so it runs once when the user is linked and the object keeps that value.
|
|
2123
|
+
*/
|
|
2124
|
+
const isFlaggedByMeFor = (user, confirmed) => {
|
|
2125
|
+
const me = currentUserId();
|
|
2126
|
+
if (!me || !user.hashFlag || !testBloomFilter(user.hashFlag, me))
|
|
2127
|
+
return false;
|
|
2128
|
+
if (confirmed !== undefined)
|
|
2129
|
+
return confirmed;
|
|
2130
|
+
confirmFlagByMe(user.userId, user.hashFlag);
|
|
2131
|
+
return true;
|
|
2132
|
+
};
|
|
2133
|
+
const NOT_FLAGGED = { isFlagByMe: false, flags: [] };
|
|
2134
|
+
const isNotOlder = (next, current) => {
|
|
2135
|
+
var _a, _b;
|
|
2136
|
+
const nextAt = Date.parse((_a = next.updatedAt) !== null && _a !== void 0 ? _a : '');
|
|
2137
|
+
const currentAt = Date.parse((_b = current.updatedAt) !== null && _b !== void 0 ? _b : '');
|
|
2138
|
+
return Number.isNaN(nextAt) || Number.isNaN(currentAt) || nextAt >= currentAt;
|
|
2139
|
+
};
|
|
2140
|
+
/**
|
|
2141
|
+
* One `getUserFlagsByMe` closure per userId, remembering the newest user object linked for that
|
|
2142
|
+
* id. Live objects and collections compare snapshots with `isEqual`, which compares functions by
|
|
2143
|
+
* reference, so a fresh closure per linked object would re-dispatch unchanged snapshots
|
|
2144
|
+
* (REQ-015). An older snapshot linked later does not replace a newer one.
|
|
2145
|
+
*/
|
|
2146
|
+
const flagsByMeEntries = new Map();
|
|
2147
|
+
/**
|
|
2148
|
+
* `Amity.User.getUserFlagsByMe()` (REQ-012): `{ isFlagByMe: false, flags: [] }` with no request
|
|
2149
|
+
* when the newest linked user says "not flagged", including a confirmed `false`. Otherwise the
|
|
2150
|
+
* v4 read, returned as is; a confirm already in flight for the user is joined instead of sending
|
|
2151
|
+
* a second request. Its `isFlagByMe` is recorded as the confirmed answer for the
|
|
2152
|
+
* `hashFlag` it was read for; `flags[]` is never stored (REQ-009). A missing `hashFlag` is
|
|
2153
|
+
* unknown, so the server is asked.
|
|
2154
|
+
*/
|
|
2155
|
+
const readFlagsByMe = async (userId) => {
|
|
2156
|
+
var _a, _b;
|
|
2157
|
+
const me = currentUserId();
|
|
2158
|
+
const { hashFlag } = (_b = (_a = flagsByMeEntries.get(userId)) === null || _a === void 0 ? void 0 : _a.latest) !== null && _b !== void 0 ? _b : {};
|
|
2159
|
+
if (me && hashFlag !== undefined) {
|
|
2160
|
+
if (!hashFlag || !testBloomFilter(hashFlag, me))
|
|
2161
|
+
return Object.assign(Object.assign({}, NOT_FLAGGED), { flags: [] });
|
|
2162
|
+
if (readConfirmedFlag(userId, hashFlag) === false)
|
|
2163
|
+
return Object.assign(Object.assign({}, NOT_FLAGGED), { flags: [] });
|
|
2164
|
+
}
|
|
2165
|
+
const generation = generationOf(userId);
|
|
2166
|
+
const result = await requestFlagsByMe(userId);
|
|
2167
|
+
if (hashFlag && currentUserId() === me && generationOf(userId) === generation) {
|
|
2168
|
+
recordFlag(userId, hashFlag, result.isFlagByMe);
|
|
2169
|
+
}
|
|
2170
|
+
return result;
|
|
2171
|
+
};
|
|
2172
|
+
const getUserFlagsByMeFor = (user) => {
|
|
2173
|
+
let entry = flagsByMeEntries.get(user.userId);
|
|
2174
|
+
if (!entry) {
|
|
2175
|
+
entry = { latest: user, getUserFlagsByMe: () => readFlagsByMe(user.userId) };
|
|
2176
|
+
flagsByMeEntries.set(user.userId, entry);
|
|
2177
|
+
}
|
|
2178
|
+
else if (isNotOlder(user, entry.latest)) {
|
|
2179
|
+
entry.latest = user;
|
|
2180
|
+
}
|
|
2181
|
+
return entry.getUserFlagsByMe;
|
|
2182
|
+
};
|
|
2183
|
+
/**
|
|
2184
|
+
* The current user's own flag succeeded (REQ-016). Their report may leave `hashFlag` unchanged
|
|
2185
|
+
* when other reporters had already set all of their bits, so the answer is known: record `true`
|
|
2186
|
+
* for the `hashFlag` the response carries, or drop the answer when it carries none.
|
|
2187
|
+
*/
|
|
2188
|
+
const onFlaggedByMe = (user, userId) => {
|
|
2189
|
+
bumpGeneration(userId);
|
|
2190
|
+
if (user === null || user === void 0 ? void 0 : user.hashFlag) {
|
|
2191
|
+
recordFlag(userId, user.hashFlag, true);
|
|
2192
|
+
return;
|
|
2193
|
+
}
|
|
2194
|
+
const dropped = readRecord(userId);
|
|
2195
|
+
dropFromCache(cacheKey(userId), true);
|
|
2196
|
+
if ((dropped === null || dropped === void 0 ? void 0 : dropped.isFlagByMe) === false)
|
|
2197
|
+
notifyObservers(userId);
|
|
2198
|
+
};
|
|
2199
|
+
/**
|
|
2200
|
+
* The current user's own unflag succeeded (`user.unflag` REQ-010, REQ-016). A per-type unflag can
|
|
2201
|
+
* leave other reports in place, so the answer is dropped, never set to `false`; the next "maybe"
|
|
2202
|
+
* is confirmed again.
|
|
2203
|
+
*/
|
|
2204
|
+
const onUnflaggedByMe = (userId) => {
|
|
2205
|
+
bumpGeneration(userId);
|
|
2206
|
+
const dropped = readRecord(userId);
|
|
2207
|
+
dropFromCache(cacheKey(userId), true);
|
|
2208
|
+
if ((dropped === null || dropped === void 0 ? void 0 : dropped.isFlagByMe) === false)
|
|
2209
|
+
notifyObservers(userId);
|
|
2210
|
+
};
|
|
2211
|
+
/** Forgets every in-memory trace of the current user's flag state. Records live in the cache. */
|
|
2212
|
+
const resetUserFlagByMe = () => {
|
|
2213
|
+
flagsByMeEntries.clear();
|
|
2214
|
+
inFlight.clear();
|
|
2215
|
+
lastConfirmAt.clear();
|
|
2216
|
+
generations.clear();
|
|
2217
|
+
};
|
|
2218
|
+
/**
|
|
2219
|
+
* Registered through `setupLoginSubscriptions`. Every confirmed answer belongs to the current
|
|
2220
|
+
* user, so the state is cleared on logout; the records themselves go with the cache, and each
|
|
2221
|
+
* also carries the reporter's id.
|
|
2222
|
+
*/
|
|
2223
|
+
const userFlagByMeOnLoginHandler = () => {
|
|
2224
|
+
const unsubscribe = onSessionStateChange(state => {
|
|
2225
|
+
if (state === "notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */ || state === "terminated" /* Amity.SessionStates.TERMINATED */) {
|
|
2226
|
+
resetUserFlagByMe();
|
|
2227
|
+
}
|
|
2228
|
+
});
|
|
2229
|
+
return () => {
|
|
2230
|
+
unsubscribe();
|
|
2231
|
+
resetUserFlagByMe();
|
|
2232
|
+
};
|
|
2233
|
+
};
|
|
2234
|
+
/**
|
|
2235
|
+
* Called with the cached user when a recorded answer changes what that user would show, so live
|
|
2236
|
+
* objects and live collections deliver it again (REQ-016, delivery).
|
|
2237
|
+
*/
|
|
2238
|
+
const onUserFlagConfirmed = (callback) => createEventSubscriber(getActiveClient(), 'user/onUserFlagConfirmed', 'local.user.flagConfirmed', ({ userId }) => {
|
|
2239
|
+
var _a;
|
|
2240
|
+
const user = (_a = pullFromCache(['user', 'get', userId])) === null || _a === void 0 ? void 0 : _a.data;
|
|
2241
|
+
if (user)
|
|
2242
|
+
callback(user);
|
|
2243
|
+
});
|
|
2244
|
+
|
|
1801
2245
|
const userLinkedObject = (user) => {
|
|
2246
|
+
// Captured now, so a user object the app holds never changes after a confirm (REQ-014).
|
|
2247
|
+
const confirmedFlagByMe = readConfirmedFlag(user.userId, user.hashFlag);
|
|
1802
2248
|
return Object.assign(Object.assign({}, user), { get avatar() {
|
|
1803
2249
|
var _a;
|
|
1804
2250
|
if (!user.avatarFileId)
|
|
1805
|
-
return
|
|
1806
|
-
const avatar = (_a = pullFromCache([
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
`${user.avatarFileId}`,
|
|
1810
|
-
])) === null || _a === void 0 ? void 0 : _a.data;
|
|
2251
|
+
return null;
|
|
2252
|
+
const avatar = (_a = pullFromCache(['file', 'get', user.avatarFileId])) === null || _a === void 0 ? void 0 : _a.data;
|
|
2253
|
+
if (!avatar)
|
|
2254
|
+
return null;
|
|
1811
2255
|
return avatar;
|
|
1812
|
-
}
|
|
2256
|
+
},
|
|
2257
|
+
get isFlaggedByMe() {
|
|
2258
|
+
// Bloom test on this object's hashFlag: "definitely not" is exact and sends nothing.
|
|
2259
|
+
// "Maybe" returns the answer confirmed for this hashFlag, or true while one background
|
|
2260
|
+
// confirm runs (REQ-011, REQ-016). Never throws.
|
|
2261
|
+
return isFlaggedByMeFor(user, confirmedFlagByMe);
|
|
2262
|
+
},
|
|
2263
|
+
/* begin_public_function
|
|
2264
|
+
id: user.get_flags_by_me
|
|
2265
|
+
*/
|
|
2266
|
+
getUserFlagsByMe: getUserFlagsByMeFor(user) });
|
|
1813
2267
|
};
|
|
1814
2268
|
|
|
1815
2269
|
/* begin_public_function
|
|
@@ -2076,13 +2530,13 @@ class NetworkActivitiesWatcher {
|
|
|
2076
2530
|
this._listener.clear();
|
|
2077
2531
|
}
|
|
2078
2532
|
}
|
|
2079
|
-
let instance$
|
|
2533
|
+
let instance$a;
|
|
2080
2534
|
var NetworkActivitiesWatcher$1 = {
|
|
2081
2535
|
getInstance: () => {
|
|
2082
|
-
if (!instance$
|
|
2083
|
-
instance$
|
|
2536
|
+
if (!instance$a) {
|
|
2537
|
+
instance$a = new NetworkActivitiesWatcher();
|
|
2084
2538
|
}
|
|
2085
|
-
return instance$
|
|
2539
|
+
return instance$a;
|
|
2086
2540
|
},
|
|
2087
2541
|
};
|
|
2088
2542
|
|
|
@@ -2266,54 +2720,6 @@ const createHttpTransport = (endpoint) => {
|
|
|
2266
2720
|
return instance;
|
|
2267
2721
|
};
|
|
2268
2722
|
|
|
2269
|
-
class SessionWatcher {
|
|
2270
|
-
constructor() {
|
|
2271
|
-
this._sessionState = "notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */;
|
|
2272
|
-
this._listener = new Map();
|
|
2273
|
-
}
|
|
2274
|
-
onSessionStateChange(callback) {
|
|
2275
|
-
this._listener.set(callback, callback);
|
|
2276
|
-
return () => {
|
|
2277
|
-
this._listener.delete(callback);
|
|
2278
|
-
};
|
|
2279
|
-
}
|
|
2280
|
-
setSessionState(state, reason) {
|
|
2281
|
-
if (this._sessionState === state)
|
|
2282
|
-
return;
|
|
2283
|
-
this._sessionState = state;
|
|
2284
|
-
this._listener.forEach(cb => cb(state, reason));
|
|
2285
|
-
}
|
|
2286
|
-
destroy() {
|
|
2287
|
-
this._listener.clear();
|
|
2288
|
-
}
|
|
2289
|
-
}
|
|
2290
|
-
let instance$a;
|
|
2291
|
-
var SessionWatcher$1 = {
|
|
2292
|
-
getInstance: () => {
|
|
2293
|
-
if (!instance$a) {
|
|
2294
|
-
instance$a = new SessionWatcher();
|
|
2295
|
-
}
|
|
2296
|
-
return instance$a;
|
|
2297
|
-
},
|
|
2298
|
-
};
|
|
2299
|
-
|
|
2300
|
-
/**
|
|
2301
|
-
* ```js
|
|
2302
|
-
* import { onSessionStateChange } from '@amityco/ts-sdk'
|
|
2303
|
-
* const dispose = onSessionStateChange((state: Amity.SessionStates) => {
|
|
2304
|
-
* // ...
|
|
2305
|
-
* })
|
|
2306
|
-
* ```
|
|
2307
|
-
*
|
|
2308
|
-
* Fired when any {@link Amity.Client} has a session state change
|
|
2309
|
-
*
|
|
2310
|
-
* @param callback The function to call when the event was fired
|
|
2311
|
-
* @returns an {@link Amity.Unsubscriber} function to stop listening
|
|
2312
|
-
*
|
|
2313
|
-
* @category Client Events
|
|
2314
|
-
*/
|
|
2315
|
-
const onSessionStateChange = (callback) => SessionWatcher$1.getInstance().onSessionStateChange(callback);
|
|
2316
|
-
|
|
2317
2723
|
const TOPICS = {
|
|
2318
2724
|
[AmityAutoSubscription.NETWORK]: [getNetworkTopic],
|
|
2319
2725
|
[AmityAutoSubscription.CHAT]: [
|
|
@@ -2886,7 +3292,8 @@ const convertRawMembershipToMembership = (member) => {
|
|
|
2886
3292
|
if (!userCache) {
|
|
2887
3293
|
return undefined;
|
|
2888
3294
|
}
|
|
2889
|
-
|
|
3295
|
+
// Every user the SDK hands out is a linked object (isFlaggedByMe, getUserFlagsByMe, avatar).
|
|
3296
|
+
return userLinkedObject(userCache);
|
|
2890
3297
|
} });
|
|
2891
3298
|
};
|
|
2892
3299
|
/**
|
|
@@ -2920,7 +3327,7 @@ const withUser = (member, userIdProp = 'userId', userProp = 'user') => {
|
|
|
2920
3327
|
if (!userCache) {
|
|
2921
3328
|
return undefined;
|
|
2922
3329
|
}
|
|
2923
|
-
return userCache;
|
|
3330
|
+
return userLinkedObject(userCache);
|
|
2924
3331
|
} }, member);
|
|
2925
3332
|
};
|
|
2926
3333
|
/**
|
|
@@ -3074,12 +3481,19 @@ const getSubChannelMessagePreview = (subChannelId) => {
|
|
|
3074
3481
|
};
|
|
3075
3482
|
|
|
3076
3483
|
const getSubChannelMessagePreviewWithUser = (subChannel) => {
|
|
3077
|
-
var _a;
|
|
3078
3484
|
const messagePreview = subChannel.messagePreviewId
|
|
3079
3485
|
? getSubChannelMessagePreview(subChannel.subChannelId)
|
|
3080
3486
|
: null;
|
|
3081
3487
|
const messagePreviewWithUser = messagePreview
|
|
3082
|
-
? Object.assign(Object.assign({}, messagePreview), { user: (
|
|
3488
|
+
? Object.assign(Object.assign({}, messagePreview), { user: (() => {
|
|
3489
|
+
var _a;
|
|
3490
|
+
const internalUser = (_a = pullFromCache([
|
|
3491
|
+
'user',
|
|
3492
|
+
'get',
|
|
3493
|
+
messagePreview === null || messagePreview === void 0 ? void 0 : messagePreview.creatorId,
|
|
3494
|
+
])) === null || _a === void 0 ? void 0 : _a.data;
|
|
3495
|
+
return internalUser ? userLinkedObject(internalUser) : undefined;
|
|
3496
|
+
})() }) : null;
|
|
3083
3497
|
return Object.assign(Object.assign({}, subChannel), { messagePreview: messagePreviewWithUser });
|
|
3084
3498
|
};
|
|
3085
3499
|
|
|
@@ -8066,6 +8480,140 @@ const postCommentCountEngineOnLoginHandler = () => onSessionStateChange(state =>
|
|
|
8066
8480
|
}
|
|
8067
8481
|
});
|
|
8068
8482
|
|
|
8483
|
+
const BOTH_FIELDS = { lastActivity: true, lastMessageAt: true };
|
|
8484
|
+
/**
|
|
8485
|
+
* The channel cache is keyed by `channelInternalId`, but a message names its
|
|
8486
|
+
* channel by whichever id the producer had: the raw MQTT payload carries the
|
|
8487
|
+
* internal id, a converted `Amity.InternalMessage` carries the public id, and
|
|
8488
|
+
* the optimistic message carries the sub-channel's `channelId`. Try the direct
|
|
8489
|
+
* key first, then match any of the three ids.
|
|
8490
|
+
*/
|
|
8491
|
+
const findCachedChannel = (channelId) => {
|
|
8492
|
+
var _a, _b, _c;
|
|
8493
|
+
const direct = (_a = pullFromCache(['channel', 'get', channelId])) === null || _a === void 0 ? void 0 : _a.data;
|
|
8494
|
+
if (direct)
|
|
8495
|
+
return direct;
|
|
8496
|
+
return (_c = (_b = queryCache(['channel', 'get'])) === null || _b === void 0 ? void 0 : _b.find(({ data }) => data.channelInternalId === channelId ||
|
|
8497
|
+
data.channelId === channelId ||
|
|
8498
|
+
data.channelPublicId === channelId)) === null || _c === void 0 ? void 0 : _c.data;
|
|
8499
|
+
};
|
|
8500
|
+
const isNewer = (stored, at) => !stored || convertDateStringToTimestamp(stored) < convertDateStringToTimestamp(at);
|
|
8501
|
+
const isSameInstant = (stored, at) => !!stored && convertDateStringToTimestamp(stored) === convertDateStringToTimestamp(at);
|
|
8502
|
+
/** Pushes the changed fields in one write and emits one `local.channel.updated`. */
|
|
8503
|
+
const commit = (channel, changes) => {
|
|
8504
|
+
if (Object.keys(changes).length === 0)
|
|
8505
|
+
return false;
|
|
8506
|
+
const updated = shallowClone(channel, changes);
|
|
8507
|
+
// Write back under the key the cache actually uses (the resolver key), not
|
|
8508
|
+
// under `channel.channelId`, which can differ from `channelInternalId`.
|
|
8509
|
+
pushToCache(['channel', 'get', getResolver('channel')(updated)], updated);
|
|
8510
|
+
fireEvent('local.channel.updated', {
|
|
8511
|
+
channels: [updated],
|
|
8512
|
+
channelUsers: [],
|
|
8513
|
+
users: [],
|
|
8514
|
+
files: [],
|
|
8515
|
+
});
|
|
8516
|
+
return true;
|
|
8517
|
+
};
|
|
8518
|
+
/**
|
|
8519
|
+
* Single owner of every local write to a channel's ordering timestamps,
|
|
8520
|
+
* `lastActivity` and `lastMessageAt` (ChannelActivityService v1).
|
|
8521
|
+
*
|
|
8522
|
+
* - Monotonic per field: a field moves only when `at` is newer than the stored
|
|
8523
|
+
* value (or the stored value is `null`), so a late or replayed event never
|
|
8524
|
+
* moves a channel down the list.
|
|
8525
|
+
* - One write: both fields are read, compared and pushed in a single cache
|
|
8526
|
+
* update, followed by one `local.channel.updated` event so every open channel
|
|
8527
|
+
* collection re-sorts once.
|
|
8528
|
+
* - Server channel payloads never go through here; they overwrite unguarded.
|
|
8529
|
+
*
|
|
8530
|
+
* @returns `true` when at least one field moved
|
|
8531
|
+
* @category private
|
|
8532
|
+
*/
|
|
8533
|
+
const updateChannelActivityTimestamps = (channelId, at, fields = BOTH_FIELDS) => {
|
|
8534
|
+
const client = getActiveClient();
|
|
8535
|
+
if (!client.cache || !channelId || !at)
|
|
8536
|
+
return false;
|
|
8537
|
+
const channel = findCachedChannel(channelId);
|
|
8538
|
+
if (!channel)
|
|
8539
|
+
return false;
|
|
8540
|
+
const changes = {};
|
|
8541
|
+
if (fields.lastActivity && isNewer(channel.lastActivity, at))
|
|
8542
|
+
changes.lastActivity = at;
|
|
8543
|
+
if (fields.lastMessageAt && isNewer(channel.lastMessageAt, at))
|
|
8544
|
+
changes.lastMessageAt = at;
|
|
8545
|
+
return commit(channel, changes);
|
|
8546
|
+
};
|
|
8547
|
+
/**
|
|
8548
|
+
* Local correction for the current user's own send (ChannelActivityService v1
|
|
8549
|
+
* REQ-009, Trigger Matrix row 1b). Once the server confirms the message, each
|
|
8550
|
+
* field that still holds the device-clock `localAt` written at send time is
|
|
8551
|
+
* replaced by the server's `serverAt`, even when that is older. A field a newer
|
|
8552
|
+
* message has already moved is left alone. One write, one change notification,
|
|
8553
|
+
* nothing when neither field still holds `localAt`.
|
|
8554
|
+
*
|
|
8555
|
+
* @returns `true` when at least one field was replaced
|
|
8556
|
+
* @category private
|
|
8557
|
+
*/
|
|
8558
|
+
const confirmOwnMessage = (channelId, localAt, serverAt) => {
|
|
8559
|
+
const client = getActiveClient();
|
|
8560
|
+
if (!client.cache || !channelId || !localAt || !serverAt)
|
|
8561
|
+
return false;
|
|
8562
|
+
if (isSameInstant(localAt, serverAt))
|
|
8563
|
+
return false;
|
|
8564
|
+
const channel = findCachedChannel(channelId);
|
|
8565
|
+
if (!channel)
|
|
8566
|
+
return false;
|
|
8567
|
+
const changes = {};
|
|
8568
|
+
if (isSameInstant(channel.lastActivity, localAt))
|
|
8569
|
+
changes.lastActivity = serverAt;
|
|
8570
|
+
if (isSameInstant(channel.lastMessageAt, localAt))
|
|
8571
|
+
changes.lastMessageAt = serverAt;
|
|
8572
|
+
return commit(channel, changes);
|
|
8573
|
+
};
|
|
8574
|
+
|
|
8575
|
+
/**
|
|
8576
|
+
* Session-wide owner of the local `lastActivity` / `lastMessageAt` updates
|
|
8577
|
+
* (ChannelActivityService v1, Web migration rows W1 – W5).
|
|
8578
|
+
*
|
|
8579
|
+
* Registered once per session from `setupLoginSubscriptions` and
|
|
8580
|
+
* `resumeSession`, so the update runs whether or not a channel collection is
|
|
8581
|
+
* open, and exactly once per message however many collections are open.
|
|
8582
|
+
*
|
|
8583
|
+
* It listens to the raw events rather than `onMessageCreatedMqtt`, so it does
|
|
8584
|
+
* not duplicate that subscriber's side effects (mark-read, unread recount).
|
|
8585
|
+
*
|
|
8586
|
+
* @category private
|
|
8587
|
+
*/
|
|
8588
|
+
const channelActivityOnLoginHandler = () => {
|
|
8589
|
+
const client = getActiveClient();
|
|
8590
|
+
const disposers = [
|
|
8591
|
+
// Any user's new message, in any message feed of the channel. The raw
|
|
8592
|
+
// payload's `channelId` is the channel's internal id.
|
|
8593
|
+
createEventSubscriber(client, 'channelActivity', 'message.created', payload => {
|
|
8594
|
+
payload.messages.forEach(message => {
|
|
8595
|
+
updateChannelActivityTimestamps(message.channelId, message.createdAt);
|
|
8596
|
+
});
|
|
8597
|
+
}),
|
|
8598
|
+
// The current user's own send fires this three times: with the optimistic
|
|
8599
|
+
// message at send time (Syncing), on the create response (Synced), and on
|
|
8600
|
+
// failure (Error). The device-clock time of the Syncing / Error fires was
|
|
8601
|
+
// already applied synchronously in createMessageOptimistic, and replaying it
|
|
8602
|
+
// here could undo the server correction made on the response
|
|
8603
|
+
// (confirmOwnMessage). Only the server-confirmed fire goes through the guard.
|
|
8604
|
+
createEventSubscriber(client, 'channelActivity', 'local.message.created', payload => {
|
|
8605
|
+
payload.messages.forEach(message => {
|
|
8606
|
+
if (message.syncState !== "synced" /* Amity.SyncState.Synced */)
|
|
8607
|
+
return;
|
|
8608
|
+
updateChannelActivityTimestamps(message.channelId, message.createdAt);
|
|
8609
|
+
});
|
|
8610
|
+
}),
|
|
8611
|
+
];
|
|
8612
|
+
return () => {
|
|
8613
|
+
disposers.forEach(fn => fn());
|
|
8614
|
+
};
|
|
8615
|
+
};
|
|
8616
|
+
|
|
8069
8617
|
const EVENTS = [
|
|
8070
8618
|
'disconnected',
|
|
8071
8619
|
'error',
|
|
@@ -8255,7 +8803,7 @@ const setupLoginSubscriptions = (unsubWatcher) => {
|
|
|
8255
8803
|
// NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
|
|
8256
8804
|
// the channel because currently backend can't handle this, so every time a user is banned from
|
|
8257
8805
|
// a channel or the channel is deleted the channel's unread count will not be reset to zero
|
|
8258
|
-
onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler(), reactionSyncEngineOnLoginHandler(), blockedUserSyncEngineOnLoginHandler(), blockingUserSyncEngineOnLoginHandler(), blockRealtimeOnLoginHandler(), resetAutoSubscriptionsOnLogout(), CursorController.clearOnLogout(), postCommentCountEngineOnLoginHandler());
|
|
8806
|
+
onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler(), reactionSyncEngineOnLoginHandler(), blockedUserSyncEngineOnLoginHandler(), blockingUserSyncEngineOnLoginHandler(), blockRealtimeOnLoginHandler(), resetAutoSubscriptionsOnLogout(), CursorController.clearOnLogout(), postCommentCountEngineOnLoginHandler(), channelActivityOnLoginHandler(), userFlagByMeOnLoginHandler());
|
|
8259
8807
|
if (client.useLegacyUnreadCount) {
|
|
8260
8808
|
subscriptions.push(readReceiptSyncEngineOnLoginHandler());
|
|
8261
8809
|
}
|
|
@@ -9516,7 +10064,7 @@ const resumeSession = async (params, sessionHandler, config) => {
|
|
|
9516
10064
|
// NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
|
|
9517
10065
|
// the channel because currently backend can't handle this, so every time a user is banned from
|
|
9518
10066
|
// a channel or the channel is deleted the channel's unread count will not be reset to zero
|
|
9519
|
-
onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler());
|
|
10067
|
+
onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler(), channelActivityOnLoginHandler());
|
|
9520
10068
|
if (client.useLegacyUnreadCount) {
|
|
9521
10069
|
subscriptions$1.push(readReceiptSyncEngineOnLoginHandler());
|
|
9522
10070
|
}
|
|
@@ -11808,7 +12356,7 @@ const prepareCommunityPayload = (rawPayload) => {
|
|
|
11808
12356
|
// map users with community
|
|
11809
12357
|
const mappedCommunityUsers = rawPayload.communityUsers.map(communityUser => {
|
|
11810
12358
|
const user = internalUsers.find(user => user.userId === communityUser.userId);
|
|
11811
|
-
return Object.assign(Object.assign({}, communityUser), { user });
|
|
12359
|
+
return Object.assign(Object.assign({}, communityUser), { user: user ? userLinkedObject(user) : undefined });
|
|
11812
12360
|
});
|
|
11813
12361
|
const communityWithMembershipStatus = updateMembershipStatus(communitiesWithPostSetting, mappedCommunityUsers);
|
|
11814
12362
|
return Object.assign(Object.assign({}, rawPayload), { users: rawPayload.users.map(convertRawUserToInternalUser), communities: communityWithMembershipStatus, communityUsers: mappedCommunityUsers });
|
|
@@ -11825,7 +12373,7 @@ const prepareCommunityMembershipPayload = (rawPayload) => {
|
|
|
11825
12373
|
// map users with community
|
|
11826
12374
|
const mappedCommunityUsers = rawPayload.communityUsers.map(communityUser => {
|
|
11827
12375
|
const user = rawPayload.users.find(user => user.userId === communityUser.userId);
|
|
11828
|
-
return Object.assign(Object.assign({}, communityUser), { user });
|
|
12376
|
+
return Object.assign(Object.assign({}, communityUser), { user: user ? userLinkedObject(convertRawUserToInternalUser(user)) : undefined });
|
|
11829
12377
|
});
|
|
11830
12378
|
const communityWithMembershipStatus = updateMembershipStatus(communitiesWithPostSetting, mappedCommunityUsers);
|
|
11831
12379
|
return Object.assign(Object.assign({}, rawPayload), { communities: communityWithMembershipStatus, communityUsers: mappedCommunityUsers });
|
|
@@ -13475,7 +14023,7 @@ const getWatchSessionStorage = () => {
|
|
|
13475
14023
|
return storageInstance;
|
|
13476
14024
|
};
|
|
13477
14025
|
|
|
13478
|
-
const privateKey = "
|
|
14026
|
+
const privateKey = "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHo80SecH7FuF2\nhFYnb+l26/VN8UMLXAQFLnxciNTEwkGVFMpdezlH8rU2HtUJL4RETogbAOLVY0XM\njs6sPn8G1nALmh9qeDpUtVqFOVtBHxEZ910TLOtQiunjqJKO5nWdqZ71EC3OFluR\niGQkO84BiIFbv37ub7xl3S8XarbtKoLcyVpkDHi+1wx1pgCAn6gtBUgckPL5NR8j\nLseabl3HAXQfhTCKo4tmOFM2Dxwl1IUMmIJrJg/aIU/U0tj/1Eoo7mG0JcNWX19l\nW3EecCbi0ncCJOrkUdwlBrcjaMayaX/ubEwyUeTGiLdyc4L3GRLHjyK8xgVNXRMH\nbZWJ2a5NAgMBAAECggEASxuE+35zTFO/XydKgmvIGcWL9FbgMlXb7Vcf0nBoG945\nbiz0NVc2paraIhJXc608xbYF3qLmtAE1MVBI0ORyRdBHNxY024l/6H6SH60Ed+uI\nM4ysp5ourY6Vj+DLwpdRiI9YDjqYAQDIUmhNxJP7XPhOMoZI6st+xZQBM34ic/bv\nAMSJm9OZphSp3+qXVkFZztr2mxD2EZSJJLYxi8BCdgM2qhazalbcJ6zDKHCZWVWm\n8RRxDGldyMb/237JxETzP40tAlzOZDmBAbUgEnurDJ93RVDIE3rbZUshwgeQd18a\nem096mWgvB1AIKYgsTAR3pw+V19YWAjq/glP6fz8wQKBgQD/oQq+ukKF0PRgBeM5\ngeTjSwsdGppQLmf5ndujvoiz/TpdjDEPu6R8kigQr1rG2t4K/yfdZoI8RdmJD1al\n3Q7N9hofooSy4rj6E3txzWZCHJjHad2cnCp/O26HiReGAl7wTcfTmNdiFHhZQzm5\nJBkvWAiwuvQMNfEbnXxw6/vIDwKBgQDH7fX8gsc77JLvAWgp1MaQN/sbqVb6JeT1\nFQfR8E/WFCSmzQBtNzd5KgYuCeelwr/8DyYytvN2BzCYZXp73gI1jF3YlW5jVn74\nOY6TwQ095digwo6Z0yuxopdIOApKgAkL9PRKgNrqAf3NAyMua6lOGifzjDojC3KU\nfylQmxMn4wKBgHp2B9O/H0dEBw5JQ8W0+JX6yWQz7mEjGiR2/1W+XXb8hQ1zr709\nw1r6Gb+EghRpnZ3fBpYGGbYOMFx8wKHM+N6qW3F0ReX8v2juFGE8aRSa5oYBrWzt\nU16Idjbv8hj84cZ1PJmdyvDtpYn9rpWHOZl4rxEbPvbqkIsOMyNVqdT5AoGAOSge\nmwIIU2le2FVeohbibXiToWTYKMuMmURZ5/r72AgKMmWJKbAPe+Q3wBG01/7FRBpQ\noU8Ma0HC8s6QJbliiEyIx9JwrJWd1vkdecBHONrtA4ibm/5zD2WcOllLF+FitLhi\n3qnX6+6F0IaFGFBPJrTzlv0P4dTz/OAdv52V7GECgYEA2TttOKBAqWllgOaZOkql\nLVMJVmgR7s6tLi1+cEP8ZcapV9aRbRzTAKXm4f8AEhtlG9F9kCOvHYCYGi6JaiWJ\nZkHjeex3T+eE6Di6y5Bm/Ift5jtVhJ4jCVwHOKTMej79NPUFTJfv8hCo29haBDv6\nRXFrv+T21KCcw8k3sJeJWWQ=\n-----END PRIVATE KEY-----";
|
|
13479
14027
|
/*
|
|
13480
14028
|
* The crypto algorithm used for importing key and signing string
|
|
13481
14029
|
*/
|
|
@@ -15250,7 +15798,7 @@ const updateUser = async (userId, patch) => {
|
|
|
15250
15798
|
ingestInCache(payload, { cachedAt });
|
|
15251
15799
|
fireEvent('user.updated', data);
|
|
15252
15800
|
return {
|
|
15253
|
-
data: payload.users.find(user => user.userId === userId),
|
|
15801
|
+
data: userLinkedObject(payload.users.find(user => user.userId === userId)),
|
|
15254
15802
|
cachedAt,
|
|
15255
15803
|
};
|
|
15256
15804
|
};
|
|
@@ -15301,8 +15849,11 @@ const flagUser = async (userId, options) => {
|
|
|
15301
15849
|
if (client.cache) {
|
|
15302
15850
|
ingestInCache(payload);
|
|
15303
15851
|
}
|
|
15852
|
+
const flagged = (_a = payload.users.find(user => user.userId === userId)) !== null && _a !== void 0 ? _a : payload.users[0];
|
|
15853
|
+
// The caller's own report may leave hashFlag unchanged; record that they flagged (REQ-016).
|
|
15854
|
+
onFlaggedByMe(flagged, userId);
|
|
15304
15855
|
fireEvent('user.flagged', data);
|
|
15305
|
-
return (
|
|
15856
|
+
return userLinkedObject(flagged);
|
|
15306
15857
|
};
|
|
15307
15858
|
/* end_public_function */
|
|
15308
15859
|
|
|
@@ -15322,14 +15873,18 @@ const submitUserReport = async (apiName, userId, body) => {
|
|
|
15322
15873
|
if (client.cache) {
|
|
15323
15874
|
ingestInCache(payload);
|
|
15324
15875
|
}
|
|
15876
|
+
const flagged = (_a = payload.users.find(user => user.userId === userId)) !== null && _a !== void 0 ? _a : payload.users[0];
|
|
15877
|
+
// The caller's own report may leave hashFlag unchanged; record that they flagged (REQ-016).
|
|
15878
|
+
onFlaggedByMe(flagged, userId);
|
|
15325
15879
|
fireEvent('user.flagged', data);
|
|
15326
|
-
return (
|
|
15880
|
+
return userLinkedObject(flagged);
|
|
15327
15881
|
};
|
|
15328
15882
|
|
|
15329
15883
|
/** Maximum `comment` length, in string length units, after trimming. */
|
|
15330
15884
|
const USER_REPORT_COMMENT_MAX_LENGTH = 300;
|
|
15331
15885
|
const PROFILE_FIELDS = Object.values(UserProfileFieldEnum);
|
|
15332
15886
|
const BEHAVIOR_REASONS = Object.values(ContentFlagReasonEnum);
|
|
15887
|
+
const REPORT_TYPE_FILTERS = Object.values(UserReportTypeFilterEnum);
|
|
15333
15888
|
const assertOptionsObject = (apiName, options, shape) => {
|
|
15334
15889
|
if (typeof options !== 'object' || options === null || Array.isArray(options)) {
|
|
15335
15890
|
throw new ASCInvalidParameterError(`${apiName}: expected an options object \`${shape}\``);
|
|
@@ -15389,6 +15944,28 @@ const toUserBehaviorReportBody = (options) => {
|
|
|
15389
15944
|
}
|
|
15390
15945
|
return withComment({ reportType: 'behavior', reason }, normalizeUserReportComment(apiName, comment));
|
|
15391
15946
|
};
|
|
15947
|
+
/**
|
|
15948
|
+
* Validates the optional `{ reportType? }` of `unflagUser` and builds its query parameters.
|
|
15949
|
+
* Returns `undefined` when no `reportType` is given, so the request carries no query string
|
|
15950
|
+
* and withdraws every report the caller filed on the user. The server validates the value
|
|
15951
|
+
* too (422); this local check only saves the round-trip for the string alias, which admits
|
|
15952
|
+
* raw strings. Untyped callers can pass anything at runtime, so nothing is trusted from the
|
|
15953
|
+
* declared type.
|
|
15954
|
+
*
|
|
15955
|
+
* @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when the options are malformed or `reportType` is unknown
|
|
15956
|
+
*/
|
|
15957
|
+
const toUserReportTypeFilter = (options) => {
|
|
15958
|
+
if (options === undefined)
|
|
15959
|
+
return undefined;
|
|
15960
|
+
const apiName = 'unflagUser';
|
|
15961
|
+
const { reportType } = assertOptionsObject(apiName, options, '{ reportType? }');
|
|
15962
|
+
if (reportType === undefined)
|
|
15963
|
+
return undefined;
|
|
15964
|
+
if (typeof reportType !== 'string' || !REPORT_TYPE_FILTERS.includes(reportType)) {
|
|
15965
|
+
throw new ASCInvalidParameterError(`${apiName}: \`reportType\` must be a \`UserReportTypeFilterEnum\` value`);
|
|
15966
|
+
}
|
|
15967
|
+
return { reportType: reportType };
|
|
15968
|
+
};
|
|
15392
15969
|
|
|
15393
15970
|
/* begin_public_function
|
|
15394
15971
|
id: user.flag_profile
|
|
@@ -15461,24 +16038,53 @@ const flagUserBehavior = async (userId, options) => submitUserReport('flagUserBe
|
|
|
15461
16038
|
*/
|
|
15462
16039
|
/**
|
|
15463
16040
|
* ```js
|
|
15464
|
-
* import { UserRepository } from '@amityco/ts-sdk'
|
|
15465
|
-
*
|
|
16041
|
+
* import { UserRepository, UserReportTypeFilterEnum } from '@amityco/ts-sdk'
|
|
16042
|
+
*
|
|
16043
|
+
* // Withdraw every report I filed on the user.
|
|
16044
|
+
* const unflagged = await UserRepository.unflagUser('userId')
|
|
16045
|
+
*
|
|
16046
|
+
* // Withdraw only my behavior report; my other reports on the user stay.
|
|
16047
|
+
* await UserRepository.unflagUser('userId', { reportType: UserReportTypeFilterEnum.BEHAVIOR })
|
|
16048
|
+
*
|
|
16049
|
+
* // Withdraw only my typeless (legacy) report.
|
|
16050
|
+
* await UserRepository.unflagUser('userId', { reportType: UserReportTypeFilterEnum.UNTYPED })
|
|
15466
16051
|
* ```
|
|
15467
16052
|
*
|
|
16053
|
+
* Withdraws the caller's reports on a user. Without `reportType` every report the caller
|
|
16054
|
+
* filed on that user is withdrawn, as before. With `reportType` only the caller's report
|
|
16055
|
+
* of that type is withdrawn; the caller's other reports and other users' reports are
|
|
16056
|
+
* untouched. Withdrawing a report that does not exist succeeds and changes nothing.
|
|
16057
|
+
*
|
|
16058
|
+
* The returned user is persisted to the cache, so its `hashFlag` says whether the caller
|
|
16059
|
+
* may still hold a report on the user. Do not flip a local "flagged by me" flag to `false`
|
|
16060
|
+
* after a per-type withdrawal; observe the user and read `isFlaggedByMe`, or call
|
|
16061
|
+
* `user.getUserFlagsByMe()` for the remaining per-type state (`flags`). The SDK drops the
|
|
16062
|
+
* answer it confirmed for this user, so the next "maybe" is confirmed again.
|
|
16063
|
+
*
|
|
16064
|
+
* Sends `DELETE /api/v4/me/flags/:userId`, with `?reportType=<type>` when one is given.
|
|
16065
|
+
*
|
|
15468
16066
|
* @param userId The ID of the user to unflag
|
|
15469
|
-
* @
|
|
16067
|
+
* @param options Optional; `reportType` selects a single report to withdraw. For a report that `user.getUserFlagsByMe()` lists with `reportType: null`, pass `UserReportTypeFilterEnum.UNTYPED`
|
|
16068
|
+
* @returns `true` once the server has processed the withdrawal
|
|
16069
|
+
* @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when `reportType` is not a `UserReportTypeFilterEnum` value, before any request is made
|
|
15470
16070
|
*
|
|
15471
16071
|
* @category User API
|
|
15472
16072
|
* @async
|
|
15473
16073
|
* */
|
|
15474
|
-
const unflagUser = async (userId) => {
|
|
15475
|
-
const client = getActiveClient();
|
|
15476
|
-
client.log('user/unflag', userId);
|
|
15477
|
-
const
|
|
16074
|
+
const unflagUser = async (userId, options) => {
|
|
16075
|
+
const client = getActiveClient();
|
|
16076
|
+
client.log('user/unflag', { userId, options });
|
|
16077
|
+
const params = toUserReportTypeFilter(options);
|
|
16078
|
+
const url = `/api/v4/me/flags/${encodeURIComponent(userId)}`;
|
|
16079
|
+
const { data } = params
|
|
16080
|
+
? await client.http.delete(url, { params })
|
|
16081
|
+
: await client.http.delete(url);
|
|
15478
16082
|
const payload = prepareUserPayload(data);
|
|
15479
16083
|
if (client.cache) {
|
|
15480
16084
|
ingestInCache(payload);
|
|
15481
16085
|
}
|
|
16086
|
+
// Drop the confirmed answer; never set it to false: other reports may remain (REQ-010).
|
|
16087
|
+
onUnflaggedByMe(userId);
|
|
15482
16088
|
fireEvent('user.unflagged', data);
|
|
15483
16089
|
return !!payload;
|
|
15484
16090
|
};
|
|
@@ -15490,11 +16096,19 @@ const unflagUser = async (userId) => {
|
|
|
15490
16096
|
/**
|
|
15491
16097
|
* ```js
|
|
15492
16098
|
* import { UserRepository } from '@amityco/ts-sdk'
|
|
15493
|
-
* const isFlagged = await UserRepository.isUserFlaggedByMe(
|
|
16099
|
+
* const isFlagged = await UserRepository.isUserFlaggedByMe(userId)
|
|
15494
16100
|
* ```
|
|
15495
16101
|
*
|
|
15496
|
-
*
|
|
15497
|
-
*
|
|
16102
|
+
* Whether the current user has reported a user. Sends one request per call and caches
|
|
16103
|
+
* nothing.
|
|
16104
|
+
*
|
|
16105
|
+
* @deprecated Read `user.isFlaggedByMe` on an {@link Amity.User} for a yes/no hint that
|
|
16106
|
+
* usually needs no request, and call `user.getUserFlagsByMe()` for the exact answer with the
|
|
16107
|
+
* caller's reports by type. Observe the user with `UserRepository.getUser(userId, callback)`
|
|
16108
|
+
* to follow a flag or unflag.
|
|
16109
|
+
*
|
|
16110
|
+
* @param userId The ID of the user to check
|
|
16111
|
+
* @returns `true` if the current user has reported the user, `false` if not
|
|
15498
16112
|
*
|
|
15499
16113
|
* @category Report API
|
|
15500
16114
|
* @async
|
|
@@ -15502,7 +16116,7 @@ const unflagUser = async (userId) => {
|
|
|
15502
16116
|
const isUserFlaggedByMe = async (userId) => {
|
|
15503
16117
|
const client = getActiveClient();
|
|
15504
16118
|
client.log('user/isUserFlaggedByMe', userId);
|
|
15505
|
-
const {
|
|
16119
|
+
const { isFlagByMe } = await getFlagsByMe(userId);
|
|
15506
16120
|
return isFlagByMe;
|
|
15507
16121
|
};
|
|
15508
16122
|
/* end_public_function */
|
|
@@ -15742,7 +16356,7 @@ const getUser = (userId, callback) => {
|
|
|
15742
16356
|
const reactor = (response) => {
|
|
15743
16357
|
return callback(Object.assign(Object.assign({}, response), { data: response.data ? LinkedObject.user(response.data) : response.data }));
|
|
15744
16358
|
};
|
|
15745
|
-
|
|
16359
|
+
const unsubscribeUser = liveObject(userId, reactor, 'userId', getUser$1, [
|
|
15746
16360
|
onUserFetched,
|
|
15747
16361
|
onUserUpdated,
|
|
15748
16362
|
onUserDeleted$2,
|
|
@@ -15750,19 +16364,28 @@ const getUser = (userId, callback) => {
|
|
|
15750
16364
|
onUserUnflagged,
|
|
15751
16365
|
onUserFlagCleared,
|
|
15752
16366
|
]);
|
|
16367
|
+
// A confirmed flag answer leaves the cached user row unchanged, so the live object's own
|
|
16368
|
+
// dedupe would drop it. Deliver the re-linked user directly (user.check_flag_by_me REQ-016).
|
|
16369
|
+
const unsubscribeFlag = onUserFlagConfirmed(user => {
|
|
16370
|
+
if (user.userId !== userId)
|
|
16371
|
+
return;
|
|
16372
|
+
callback({ data: LinkedObject.user(user), loading: false, origin: 'event' });
|
|
16373
|
+
});
|
|
16374
|
+
return () => {
|
|
16375
|
+
unsubscribeUser();
|
|
16376
|
+
unsubscribeFlag();
|
|
16377
|
+
};
|
|
15753
16378
|
};
|
|
15754
16379
|
/* end_public_function */
|
|
15755
16380
|
|
|
15756
|
-
|
|
15757
|
-
|
|
15758
|
-
|
|
15759
|
-
|
|
15760
|
-
|
|
15761
|
-
|
|
15762
|
-
|
|
15763
|
-
|
|
15764
|
-
}
|
|
15765
|
-
}
|
|
16381
|
+
var EnumUserActions;
|
|
16382
|
+
(function (EnumUserActions) {
|
|
16383
|
+
EnumUserActions["OnUserDeleted"] = "onUserDeleted";
|
|
16384
|
+
EnumUserActions["OnUserUpdated"] = "onUserUpdated";
|
|
16385
|
+
EnumUserActions["OnUserFlagged"] = "onUserFlagged";
|
|
16386
|
+
EnumUserActions["OnUserUnflagged"] = "onUserUnflagged";
|
|
16387
|
+
EnumUserActions["OnUserFlagCleared"] = "onUserFlagCleared";
|
|
16388
|
+
})(EnumUserActions || (EnumUserActions = {}));
|
|
15766
16389
|
|
|
15767
16390
|
class UserQueryStreamController extends QueryStreamController {
|
|
15768
16391
|
constructor(query, cacheKey, notifyChange, preparePayload) {
|
|
@@ -15810,14 +16433,16 @@ class UserQueryStreamController extends QueryStreamController {
|
|
|
15810
16433
|
}
|
|
15811
16434
|
}
|
|
15812
16435
|
|
|
15813
|
-
|
|
15814
|
-
(
|
|
15815
|
-
|
|
15816
|
-
|
|
15817
|
-
|
|
15818
|
-
|
|
15819
|
-
|
|
15820
|
-
|
|
16436
|
+
class UserPaginationController extends PaginationController {
|
|
16437
|
+
async getRequest(queryParams, token) {
|
|
16438
|
+
const { limit = COLLECTION_DEFAULT_PAGINATION_LIMIT } = queryParams, params = __rest(queryParams, ["limit"]);
|
|
16439
|
+
const options = token ? { token } : { limit };
|
|
16440
|
+
const { data: queryResponse } = await this.http.get(`/api/v3/users`, {
|
|
16441
|
+
params: Object.assign(Object.assign({}, params), { options, isDeleted: false }),
|
|
16442
|
+
});
|
|
16443
|
+
return queryResponse;
|
|
16444
|
+
}
|
|
16445
|
+
}
|
|
15821
16446
|
|
|
15822
16447
|
class UserLiveCollectionController extends LiveCollectionController {
|
|
15823
16448
|
constructor(query, callback) {
|
|
@@ -15847,13 +16472,24 @@ class UserLiveCollectionController extends LiveCollectionController {
|
|
|
15847
16472
|
this.queryStreamController.appendToQueryStream(response, direction, refresh);
|
|
15848
16473
|
}
|
|
15849
16474
|
startSubscription() {
|
|
15850
|
-
return
|
|
15851
|
-
|
|
15852
|
-
|
|
15853
|
-
|
|
15854
|
-
|
|
15855
|
-
|
|
15856
|
-
|
|
16475
|
+
return [
|
|
16476
|
+
...this.queryStreamController.subscribeRTE([
|
|
16477
|
+
{ fn: onUserDeleted$2, action: EnumUserActions.OnUserDeleted },
|
|
16478
|
+
{ fn: onUserUpdated, action: EnumUserActions.OnUserUpdated },
|
|
16479
|
+
{ fn: onUserFlagged, action: EnumUserActions.OnUserFlagged },
|
|
16480
|
+
{ fn: onUserUnflagged, action: EnumUserActions.OnUserUnflagged },
|
|
16481
|
+
{ fn: onUserFlagCleared, action: EnumUserActions.OnUserFlagCleared },
|
|
16482
|
+
]),
|
|
16483
|
+
// A confirmed flag answer changes isFlaggedByMe on a listed user without changing its
|
|
16484
|
+
// cache row (user.check_flag_by_me REQ-016). Re-link only collections that hold it.
|
|
16485
|
+
onUserFlagConfirmed(user => {
|
|
16486
|
+
var _a;
|
|
16487
|
+
const collection = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
|
|
16488
|
+
if (!(collection === null || collection === void 0 ? void 0 : collection.data.includes(user.userId)))
|
|
16489
|
+
return;
|
|
16490
|
+
this.notifyChange({ origin: "event" /* Amity.LiveDataOrigin.EVENT */, loading: false });
|
|
16491
|
+
}),
|
|
16492
|
+
];
|
|
15857
16493
|
}
|
|
15858
16494
|
notifyChange({ origin, loading, error }) {
|
|
15859
16495
|
var _a, _b;
|
|
@@ -15934,16 +16570,17 @@ const getUsers = (params, callback, config) => {
|
|
|
15934
16570
|
};
|
|
15935
16571
|
/* end_public_function */
|
|
15936
16572
|
|
|
15937
|
-
|
|
15938
|
-
|
|
15939
|
-
|
|
15940
|
-
|
|
15941
|
-
const { data: queryResponse } = await this.http.get('/api/v4/me/user-blocks', {
|
|
15942
|
-
params: Object.assign(Object.assign({}, params), { options, isDeleted: false }),
|
|
15943
|
-
});
|
|
15944
|
-
return queryResponse;
|
|
16573
|
+
const convertEventPayload = (eventHandler, sourceModelProp, destinationDomain) => (callback) => eventHandler(sourceModel => {
|
|
16574
|
+
var _a;
|
|
16575
|
+
if (!sourceModel) {
|
|
16576
|
+
return sourceModel;
|
|
15945
16577
|
}
|
|
15946
|
-
}
|
|
16578
|
+
const cacheKey = [destinationDomain, 'get', `${sourceModel[sourceModelProp]}`];
|
|
16579
|
+
const model = (_a = pullFromCache(cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
|
|
16580
|
+
if (!model)
|
|
16581
|
+
return;
|
|
16582
|
+
return callback(model);
|
|
16583
|
+
});
|
|
15947
16584
|
|
|
15948
16585
|
var EnumBlockActions;
|
|
15949
16586
|
(function (EnumBlockActions) {
|
|
@@ -15999,17 +16636,16 @@ class BlockedUserQueryStreamController extends QueryStreamController {
|
|
|
15999
16636
|
}
|
|
16000
16637
|
}
|
|
16001
16638
|
|
|
16002
|
-
|
|
16003
|
-
|
|
16004
|
-
|
|
16005
|
-
|
|
16639
|
+
class BlockedUserPaginationController extends PaginationController {
|
|
16640
|
+
async getRequest(queryParams, token) {
|
|
16641
|
+
const { limit = COLLECTION_DEFAULT_PAGINATION_LIMIT } = queryParams, params = __rest(queryParams, ["limit"]);
|
|
16642
|
+
const options = token ? { token } : { limit };
|
|
16643
|
+
const { data: queryResponse } = await this.http.get('/api/v4/me/user-blocks', {
|
|
16644
|
+
params: Object.assign(Object.assign({}, params), { options, isDeleted: false }),
|
|
16645
|
+
});
|
|
16646
|
+
return queryResponse;
|
|
16006
16647
|
}
|
|
16007
|
-
|
|
16008
|
-
const model = (_a = pullFromCache(cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
|
|
16009
|
-
if (!model)
|
|
16010
|
-
return;
|
|
16011
|
-
return callback(model);
|
|
16012
|
-
});
|
|
16648
|
+
}
|
|
16013
16649
|
|
|
16014
16650
|
class BlockedUserLiveCollectionController extends LiveCollectionController {
|
|
16015
16651
|
constructor(query, callback) {
|
|
@@ -16319,17 +16955,6 @@ const getBlockingUsers = (callback) => {
|
|
|
16319
16955
|
};
|
|
16320
16956
|
/* end_public_function */
|
|
16321
16957
|
|
|
16322
|
-
class SearchUserPaginationController extends PaginationController {
|
|
16323
|
-
async getRequest(queryParams, token) {
|
|
16324
|
-
const { limit = COLLECTION_DEFAULT_PAGINATION_LIMIT, displayName } = queryParams, params = __rest(queryParams, ["limit", "displayName"]);
|
|
16325
|
-
const options = token ? { token } : { limit };
|
|
16326
|
-
const { data: queryResponse } = await this.http.get(`/api/v3/users`, {
|
|
16327
|
-
params: Object.assign(Object.assign({}, params), { keyword: displayName, options, isDeleted: false }),
|
|
16328
|
-
});
|
|
16329
|
-
return queryResponse;
|
|
16330
|
-
}
|
|
16331
|
-
}
|
|
16332
|
-
|
|
16333
16958
|
class SearchUserQueryStreamController extends QueryStreamController {
|
|
16334
16959
|
constructor(query, cacheKey, notifyChange, preparePayload) {
|
|
16335
16960
|
super(query, cacheKey);
|
|
@@ -16376,6 +17001,17 @@ class SearchUserQueryStreamController extends QueryStreamController {
|
|
|
16376
17001
|
}
|
|
16377
17002
|
}
|
|
16378
17003
|
|
|
17004
|
+
class SearchUserPaginationController extends PaginationController {
|
|
17005
|
+
async getRequest(queryParams, token) {
|
|
17006
|
+
const { limit = COLLECTION_DEFAULT_PAGINATION_LIMIT, displayName } = queryParams, params = __rest(queryParams, ["limit", "displayName"]);
|
|
17007
|
+
const options = token ? { token } : { limit };
|
|
17008
|
+
const { data: queryResponse } = await this.http.get(`/api/v3/users`, {
|
|
17009
|
+
params: Object.assign(Object.assign({}, params), { keyword: displayName, options, isDeleted: false }),
|
|
17010
|
+
});
|
|
17011
|
+
return queryResponse;
|
|
17012
|
+
}
|
|
17013
|
+
}
|
|
17014
|
+
|
|
16379
17015
|
class SearchUserLiveCollectionController extends LiveCollectionController {
|
|
16380
17016
|
constructor(query, callback) {
|
|
16381
17017
|
var _a;
|
|
@@ -16405,13 +17041,24 @@ class SearchUserLiveCollectionController extends LiveCollectionController {
|
|
|
16405
17041
|
this.queryStreamController.appendToQueryStream(response, direction, refresh);
|
|
16406
17042
|
}
|
|
16407
17043
|
startSubscription() {
|
|
16408
|
-
return
|
|
16409
|
-
|
|
16410
|
-
|
|
16411
|
-
|
|
16412
|
-
|
|
16413
|
-
|
|
16414
|
-
|
|
17044
|
+
return [
|
|
17045
|
+
...this.queryStreamController.subscribeRTE([
|
|
17046
|
+
{ fn: onUserDeleted$2, action: EnumUserActions.OnUserDeleted },
|
|
17047
|
+
{ fn: onUserUpdated, action: EnumUserActions.OnUserUpdated },
|
|
17048
|
+
{ fn: onUserFlagged, action: EnumUserActions.OnUserFlagged },
|
|
17049
|
+
{ fn: onUserUnflagged, action: EnumUserActions.OnUserUnflagged },
|
|
17050
|
+
{ fn: onUserFlagCleared, action: EnumUserActions.OnUserFlagCleared },
|
|
17051
|
+
]),
|
|
17052
|
+
// A confirmed flag answer changes isFlaggedByMe on a listed user without changing its
|
|
17053
|
+
// cache row (user.check_flag_by_me REQ-016). Re-link only collections that hold it.
|
|
17054
|
+
onUserFlagConfirmed(user => {
|
|
17055
|
+
var _a;
|
|
17056
|
+
const collection = (_a = pullFromCache(this.cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
|
|
17057
|
+
if (!(collection === null || collection === void 0 ? void 0 : collection.data.includes(user.userId)))
|
|
17058
|
+
return;
|
|
17059
|
+
this.notifyChange({ origin: "event" /* Amity.LiveDataOrigin.EVENT */, loading: false });
|
|
17060
|
+
}),
|
|
17061
|
+
];
|
|
16415
17062
|
}
|
|
16416
17063
|
notifyChange({ origin, loading, error }) {
|
|
16417
17064
|
var _a, _b;
|
|
@@ -17242,7 +17889,7 @@ const preparePostPayload = (payload) => {
|
|
|
17242
17889
|
// map users with community
|
|
17243
17890
|
const mappedCommunityUsers = postPayload.communityUsers.map(communityUser => {
|
|
17244
17891
|
const user = postPayload.users.find(user => user.userId === communityUser.userId);
|
|
17245
|
-
return Object.assign(Object.assign({}, communityUser), { user });
|
|
17892
|
+
return Object.assign(Object.assign({}, communityUser), { user: user ? userLinkedObject(convertRawUserToInternalUser(user)) : undefined });
|
|
17246
17893
|
});
|
|
17247
17894
|
const communityWithMembershipStatus = updateMembershipStatus(communitiesWithPostSetting, mappedCommunityUsers);
|
|
17248
17895
|
let mappedNewStream = [];
|
|
@@ -19053,7 +19700,8 @@ const getPostReport = async ({ client, referenceId, }) => {
|
|
|
19053
19700
|
};
|
|
19054
19701
|
const getUserReport = async ({ client, referenceId, }) => {
|
|
19055
19702
|
var _a;
|
|
19056
|
-
|
|
19703
|
+
// v3 isFlagByMe is deprecated by the backend; v4 answers the same boolean plus the caller's flags.
|
|
19704
|
+
const { data } = await client.http.get(`/api/v4/users/${encodeURIComponent(referenceId)}/isFlagByMe`);
|
|
19057
19705
|
const { result, isFlagByMe } = data !== null && data !== void 0 ? data : {};
|
|
19058
19706
|
return (_a = result !== null && result !== void 0 ? result : isFlagByMe) !== null && _a !== void 0 ? _a : false;
|
|
19059
19707
|
};
|
|
@@ -19670,7 +20318,7 @@ const getLocalId = () => `LOCAL_${uuid()}`;
|
|
|
19670
20318
|
// FIXME: temp solution
|
|
19671
20319
|
let uniqueId;
|
|
19672
20320
|
const createMessageOptimistic = (bundle) => {
|
|
19673
|
-
var _a, _b;
|
|
20321
|
+
var _a, _b, _c, _d;
|
|
19674
20322
|
const client = getActiveClient();
|
|
19675
20323
|
if (!client.cache)
|
|
19676
20324
|
return;
|
|
@@ -19701,9 +20349,20 @@ const createMessageOptimistic = (bundle) => {
|
|
|
19701
20349
|
}
|
|
19702
20350
|
// as reused to update created and updated time, which should be the same
|
|
19703
20351
|
const createdTime = new Date().toISOString();
|
|
19704
|
-
const message = Object.assign({
|
|
20352
|
+
const message = Object.assign({
|
|
20353
|
+
// The optimistic message must name its channel (public id) so the own-send
|
|
20354
|
+
// update can move lastActivity / lastMessageAt at send time. Take it from the
|
|
20355
|
+
// cached sub-channel; when that is not cached, resolve the sub-channel id
|
|
20356
|
+
// through the channel cache, which for a channel's default sub-channel is
|
|
20357
|
+
// the channel itself (ChannelActivityService migration row I2). A thread or
|
|
20358
|
+
// unknown channel resolves to undefined, as before.
|
|
20359
|
+
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);
|
|
19705
20360
|
const cachedAt = UNSYNCED_OBJECT_CACHED_AT_VALUE;
|
|
19706
20361
|
pushToCache(['message', 'get', message.messageId], message, { cachedAt });
|
|
20362
|
+
// Own-send update (ChannelActivityService row 1): move the channel at send
|
|
20363
|
+
// time, synchronously, so the confirmation on the send response always sees
|
|
20364
|
+
// this value rather than racing the scheduled local event below.
|
|
20365
|
+
updateChannelActivityTimestamps(message.channelId, message.createdAt);
|
|
19707
20366
|
fireEvent('local.message.created', { messages: [message] });
|
|
19708
20367
|
return message;
|
|
19709
20368
|
};
|
|
@@ -19743,6 +20402,9 @@ const createMessage = async (bundle) => {
|
|
|
19743
20402
|
if (client.cache) {
|
|
19744
20403
|
ingestInCache(data, { cachedAt });
|
|
19745
20404
|
}
|
|
20405
|
+
// The own-send update wrote the device-clock createdAt; replace it with the
|
|
20406
|
+
// server's now that the message is confirmed (ChannelActivityService REQ-009).
|
|
20407
|
+
confirmOwnMessage(optimisticData === null || optimisticData === void 0 ? void 0 : optimisticData.channelId, optimisticData === null || optimisticData === void 0 ? void 0 : optimisticData.createdAt, messages[0].createdAt);
|
|
19746
20408
|
fireEvent('local.message.created', {
|
|
19747
20409
|
messages: [Object.assign(Object.assign({}, messages[0]), { syncState: "synced" /* Amity.SyncState.Synced */ })],
|
|
19748
20410
|
});
|
|
@@ -22186,6 +22848,9 @@ class ChannelLiveCollectionController extends LiveCollectionController {
|
|
|
22186
22848
|
case 'lastCreated':
|
|
22187
22849
|
channels = channels.sort(sortByLastCreated);
|
|
22188
22850
|
break;
|
|
22851
|
+
case 'lastMessage':
|
|
22852
|
+
channels = channels.sort(sortByLastMessage);
|
|
22853
|
+
break;
|
|
22189
22854
|
case 'displayName':
|
|
22190
22855
|
/*
|
|
22191
22856
|
* The server returns channels with empty | null displayName's first before
|
|
@@ -22246,9 +22911,8 @@ class ChannelLiveCollectionController extends LiveCollectionController {
|
|
|
22246
22911
|
objectResolverEngine.resolve(message.channelId, "channel" /* Amity.ReferenceType.CHANNEL */);
|
|
22247
22912
|
}
|
|
22248
22913
|
else {
|
|
22249
|
-
|
|
22250
|
-
|
|
22251
|
-
});
|
|
22914
|
+
// lastActivity / lastMessageAt are moved by channelActivityOnLoginHandler
|
|
22915
|
+
// (session-wide, monotonic, one write). This listener only re-sorts.
|
|
22252
22916
|
callback(message);
|
|
22253
22917
|
}
|
|
22254
22918
|
});
|
|
@@ -22266,9 +22930,7 @@ class ChannelLiveCollectionController extends LiveCollectionController {
|
|
|
22266
22930
|
])) === null || _a === void 0 ? void 0 : _a.data;
|
|
22267
22931
|
if (!cacheData)
|
|
22268
22932
|
return;
|
|
22269
|
-
|
|
22270
|
-
lastActivity: message.createdAt,
|
|
22271
|
-
});
|
|
22933
|
+
// Timestamps are moved by channelActivityOnLoginHandler; only re-sort here.
|
|
22272
22934
|
callback(message);
|
|
22273
22935
|
});
|
|
22274
22936
|
}, 'channelId', 'channel'),
|
|
@@ -23006,9 +23668,7 @@ class SearchChannelLiveCollectionController extends LiveCollectionController {
|
|
|
23006
23668
|
])) === null || _a === void 0 ? void 0 : _a.data;
|
|
23007
23669
|
if (!cacheData)
|
|
23008
23670
|
return;
|
|
23009
|
-
|
|
23010
|
-
lastActivity: message.createdAt,
|
|
23011
|
-
});
|
|
23671
|
+
// Timestamps are moved by channelActivityOnLoginHandler; only re-sort here.
|
|
23012
23672
|
callback(message);
|
|
23013
23673
|
});
|
|
23014
23674
|
}, 'channelId', 'channel'),
|
|
@@ -23025,9 +23685,7 @@ class SearchChannelLiveCollectionController extends LiveCollectionController {
|
|
|
23025
23685
|
])) === null || _a === void 0 ? void 0 : _a.data;
|
|
23026
23686
|
if (!cacheData)
|
|
23027
23687
|
return;
|
|
23028
|
-
|
|
23029
|
-
lastActivity: message.createdAt,
|
|
23030
|
-
});
|
|
23688
|
+
// Timestamps are moved by channelActivityOnLoginHandler; only re-sort here.
|
|
23031
23689
|
callback(message);
|
|
23032
23690
|
});
|
|
23033
23691
|
}, 'channelId', 'channel'),
|
|
@@ -23450,7 +24108,7 @@ const onUserDeleted$1 = (channelId) => (callback) => {
|
|
|
23450
24108
|
'get',
|
|
23451
24109
|
channelUserCacheKey,
|
|
23452
24110
|
])) === null || _a === void 0 ? void 0 : _a.data;
|
|
23453
|
-
upsertInCache(['channelUsers', 'get', channelUserCacheKey], Object.assign(Object.assign({}, cacheData), { user }));
|
|
24111
|
+
upsertInCache(['channelUsers', 'get', channelUserCacheKey], Object.assign(Object.assign({}, cacheData), { user: userLinkedObject(user) }));
|
|
23454
24112
|
const channel = (_b = pullFromCache([
|
|
23455
24113
|
'channel',
|
|
23456
24114
|
'get',
|
|
@@ -24923,10 +25581,10 @@ const onUserDeleted = (communityId) => (callback) => {
|
|
|
24923
25581
|
'get',
|
|
24924
25582
|
communityUserCacheKey,
|
|
24925
25583
|
])) === null || _a === void 0 ? void 0 : _a.data;
|
|
24926
|
-
pushToCache(['communityUsers', 'get', communityUserCacheKey], Object.assign(Object.assign({}, cacheData), { user }));
|
|
25584
|
+
pushToCache(['communityUsers', 'get', communityUserCacheKey], Object.assign(Object.assign({}, cacheData), { user: userLinkedObject(user) }));
|
|
24927
25585
|
const community = (_b = pullFromCache(['community', 'get', communityId])) === null || _b === void 0 ? void 0 : _b.data;
|
|
24928
25586
|
callback(community, [
|
|
24929
|
-
Object.assign(Object.assign({}, cacheData), { user }),
|
|
25587
|
+
Object.assign(Object.assign({}, cacheData), { user: userLinkedObject(user) }),
|
|
24930
25588
|
]);
|
|
24931
25589
|
};
|
|
24932
25590
|
return createEventSubscriber(client, 'user.deleted', 'user.deleted', filter);
|
|
@@ -37529,4 +38187,4 @@ class AmityAttachmentProductTags {
|
|
|
37529
38187
|
}
|
|
37530
38188
|
}
|
|
37531
38189
|
|
|
37532
|
-
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, 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 };
|
|
38190
|
+
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 };
|