@amityco/ts-sdk 7.29.1-e932a95f.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.
Files changed (29) hide show
  1. package/dist/@types/core/events.d.ts +4 -0
  2. package/dist/@types/core/events.d.ts.map +1 -1
  3. package/dist/@types/domains/user.d.ts +16 -10
  4. package/dist/@types/domains/user.d.ts.map +1 -1
  5. package/dist/client/api/setupLoginSubscriptions.d.ts.map +1 -1
  6. package/dist/core/events.d.ts +3 -3
  7. package/dist/core/events.d.ts.map +1 -1
  8. package/dist/index.cjs.js +384 -136
  9. package/dist/index.esm.js +384 -136
  10. package/dist/index.umd.js +2 -2
  11. package/dist/userRepository/api/flagUser.d.ts.map +1 -1
  12. package/dist/userRepository/api/isUserFlaggedByMe.d.ts +9 -23
  13. package/dist/userRepository/api/isUserFlaggedByMe.d.ts.map +1 -1
  14. package/dist/userRepository/api/unflagUser.d.ts +5 -4
  15. package/dist/userRepository/api/unflagUser.d.ts.map +1 -1
  16. package/dist/userRepository/internalApi/getFlagsByMe.d.ts +4 -3
  17. package/dist/userRepository/internalApi/getFlagsByMe.d.ts.map +1 -1
  18. package/dist/userRepository/internalApi/tests/getFlagsByMe.test.d.ts +2 -0
  19. package/dist/userRepository/internalApi/tests/getFlagsByMe.test.d.ts.map +1 -0
  20. package/dist/userRepository/observers/getUser.d.ts.map +1 -1
  21. package/dist/userRepository/observers/getUsers/UserLiveCollectionController.d.ts.map +1 -1
  22. package/dist/userRepository/observers/searchUserByDisplayName/SearchUserLiveCollectionController.d.ts.map +1 -1
  23. package/dist/userRepository/utils/submitUserReport.d.ts.map +1 -1
  24. package/dist/userRepository/utils/userFlagByMe.d.ts +68 -0
  25. package/dist/userRepository/utils/userFlagByMe.d.ts.map +1 -0
  26. package/dist/utils/bloomFilter.d.ts +8 -3
  27. package/dist/utils/bloomFilter.d.ts.map +1 -1
  28. package/dist/utils/linkedObject/userLinkedObject.d.ts.map +1 -1
  29. package/package.json +1 -1
package/dist/index.cjs.js CHANGED
@@ -74,7 +74,7 @@ exports.UserProfileFieldEnum = void 0;
74
74
  * Selects which of the caller's reports `UserRepository.unflagUser` withdraws, sent as the
75
75
  * `reportType` query parameter. The four typed members are the backend `reportType` wire
76
76
  * values and match `UserReportTypeEnum`. `UNTYPED` selects the typeless (legacy) report,
77
- * which `isUserFlaggedByMe` returns as `reportType: null`; it is a selector only, never
77
+ * which `user.getUserFlagsByMe()` returns as `reportType: null`; it is a selector only, never
78
78
  * stored, and the flag endpoint refuses it.
79
79
  */
80
80
  exports.UserReportTypeFilterEnum = void 0;
@@ -359,8 +359,8 @@ exports.AmityLiveViewerCountMode = void 0;
359
359
 
360
360
  function getVersion() {
361
361
  try {
362
- // the string ''v7.29.0-cjs'' should be replaced by actual value by @rollup/plugin-replace
363
- return 'v7.29.0-cjs';
362
+ // the string ''v7.30.0-cjs'' should be replaced by actual value by @rollup/plugin-replace
363
+ return 'v7.30.0-cjs';
364
364
  }
365
365
  catch (error) {
366
366
  return '__dev__';
@@ -1852,15 +1852,64 @@ async function modifyMqttConnection() {
1852
1852
  }
1853
1853
  }
1854
1854
 
1855
+ class SessionWatcher {
1856
+ constructor() {
1857
+ this._sessionState = "notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */;
1858
+ this._listener = new Map();
1859
+ }
1860
+ onSessionStateChange(callback) {
1861
+ this._listener.set(callback, callback);
1862
+ return () => {
1863
+ this._listener.delete(callback);
1864
+ };
1865
+ }
1866
+ setSessionState(state, reason) {
1867
+ if (this._sessionState === state)
1868
+ return;
1869
+ this._sessionState = state;
1870
+ this._listener.forEach(cb => cb(state, reason));
1871
+ }
1872
+ destroy() {
1873
+ this._listener.clear();
1874
+ }
1875
+ }
1876
+ let instance$b;
1877
+ var SessionWatcher$1 = {
1878
+ getInstance: () => {
1879
+ if (!instance$b) {
1880
+ instance$b = new SessionWatcher();
1881
+ }
1882
+ return instance$b;
1883
+ },
1884
+ };
1885
+
1886
+ /**
1887
+ * ```js
1888
+ * import { onSessionStateChange } from '@amityco/ts-sdk'
1889
+ * const dispose = onSessionStateChange((state: Amity.SessionStates) => {
1890
+ * // ...
1891
+ * })
1892
+ * ```
1893
+ *
1894
+ * Fired when any {@link Amity.Client} has a session state change
1895
+ *
1896
+ * @param callback The function to call when the event was fired
1897
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
1898
+ *
1899
+ * @category Client Events
1900
+ */
1901
+ const onSessionStateChange = (callback) => SessionWatcher$1.getInstance().onSessionStateChange(callback);
1902
+
1855
1903
  /**
1856
1904
  * ```js
1857
1905
  * import { getFlagsByMe } from '~/userRepository/internalApi/getFlagsByMe'
1858
1906
  * const { isFlagByMe, flags } = await getFlagsByMe('userId')
1859
1907
  * ```
1860
1908
  *
1861
- * The v4 read behind `user.getUserFlagsByMe()` and the deprecated
1862
- * `UserRepository.isUserFlaggedByMe`: `GET /api/v4/users/:userId/isFlagByMe`, returned as
1863
- * received. Nothing is filtered, cached or persisted, and `hashFlag` is not consulted here.
1909
+ * The v4 read behind `user.getUserFlagsByMe()`, the background confirm of
1910
+ * `user.isFlaggedByMe`, and the deprecated `UserRepository.isUserFlaggedByMe`:
1911
+ * `GET /api/v4/users/:userId/isFlagByMe`, returned as received. Nothing is filtered, cached or
1912
+ * persisted here, and `hashFlag` is not consulted.
1864
1913
  * This is the Web counterpart of iOS `ASCUserService.getFlagsByMe` and Android
1865
1914
  * `UserRepository.getFlagsByMe`, kept apart from the deprecated public function so the
1866
1915
  * user-object read does not change if that function ever does.
@@ -1921,35 +1970,72 @@ const locationsFor = (value, bits, hashes) => {
1921
1970
  }
1922
1971
  return locations;
1923
1972
  };
1973
+ /** Standard or URL-safe base64, padding optional. Anything else is not a filter we can read. */
1974
+ const BASE64 = /^[A-Za-z0-9+/_-]*={0,2}$/;
1924
1975
  /**
1925
- * Whether `value` is probably in the filter. `false` is exact; `true` may be a false positive.
1926
- * A missing filter (`null`, the server's value when nobody has flagged the object), a missing
1927
- * value or a malformed filter all answer `false`.
1976
+ * Whether `value` is probably in the filter. `false` is exact; `true` means "maybe", which may be
1977
+ * a false positive. `null` (the server's value when nobody has flagged the object), a missing
1978
+ * filter or a missing value answer `false`.
1979
+ *
1980
+ * A filter that cannot be read answers `true` ("maybe") and never throws, so the caller asks the
1981
+ * server instead of trusting a "definitely not" it cannot back up (REQ-017): `bits` or `hashes`
1982
+ * not a positive integer, a `hash` that is not base64, or a `hash` shorter than `ceil(bits / 8)`
1983
+ * bytes.
1928
1984
  */
1929
1985
  const testBloomFilter = (hashFlag, value) => {
1930
1986
  if (!hashFlag || !value)
1931
1987
  return false;
1932
1988
  const { bits, hashes, hash } = hashFlag;
1933
- if (!(bits > 0) || !(hashes > 0) || typeof hash !== 'string')
1934
- return false;
1989
+ if (!Number.isInteger(bits) || bits <= 0)
1990
+ return true;
1991
+ if (!Number.isInteger(hashes) || hashes <= 0)
1992
+ return true;
1993
+ if (typeof hash !== 'string' || !BASE64.test(hash))
1994
+ return true;
1935
1995
  let buckets;
1936
1996
  try {
1937
1997
  buckets = jsBase64.toUint8Array(hash);
1938
1998
  }
1939
1999
  catch (_a) {
1940
- return false;
2000
+ return true;
1941
2001
  }
2002
+ if (buckets.length < Math.ceil(bits / 8))
2003
+ return true;
1942
2004
  const locations = locationsFor(value, bits, hashes);
1943
2005
  for (let i = 0; i < locations.length; i += 1) {
1944
2006
  const index = Math.floor(locations[i] / 8);
1945
2007
  const bit = 1 << locations[i] % 8;
1946
- if (index >= buckets.length || (buckets[index] & bit) === 0)
2008
+ if ((buckets[index] & bit) === 0)
1947
2009
  return false;
1948
2010
  }
1949
2011
  return true;
1950
2012
  };
1951
2013
 
1952
- /** The current user's id, or `undefined` when there is no active client (the getter never throws). */
2014
+ /**
2015
+ * Flag state of the current user on a target user, behind `Amity.User.isFlaggedByMe` and
2016
+ * `Amity.User.getUserFlagsByMe()` (`user.check_flag_by_me` v1, REQ-011, REQ-012, REQ-016).
2017
+ *
2018
+ * `hashFlag` is a Bloom filter of reporter ids. "Definitely not" is exact. "Maybe" can be a
2019
+ * false positive caused by other reporters, so the SDK confirms it with the server once per
2020
+ * `hashFlag` value and records the answer in the cache under its own key, never on the user's
2021
+ * cache row (REQ-015). Observers are told through `local.user.flagConfirmed` when a recorded
2022
+ * answer changes what a user object would show.
2023
+ *
2024
+ * Not exported from the public API.
2025
+ */
2026
+ /** Confirms for the same user are at least this far apart, as Android's `EkoFlag.expired`. */
2027
+ const FLAG_CONFIRM_INTERVAL = 1000;
2028
+ const cacheKey = (userId) => ['userFlagByMe', 'get', userId];
2029
+ const lastConfirmAt = new Map();
2030
+ /**
2031
+ * Bumped by the current user's own flag and unflag. A confirm or exact read that started before
2032
+ * one of them must not record its answer, because the write may have changed it while the
2033
+ * `hashFlag` stayed the same.
2034
+ */
2035
+ const generations = new Map();
2036
+ const generationOf = (userId) => { var _a; return (_a = generations.get(userId)) !== null && _a !== void 0 ? _a : 0; };
2037
+ const bumpGeneration = (userId) => generations.set(userId, generationOf(userId) + 1);
2038
+ /** The current user's id, or `undefined` when there is no active client. Never throws. */
1953
2039
  const currentUserId = () => {
1954
2040
  try {
1955
2041
  return getActiveClient().userId;
@@ -1958,43 +2044,223 @@ const currentUserId = () => {
1958
2044
  return undefined;
1959
2045
  }
1960
2046
  };
2047
+ const readRecord = (userId) => {
2048
+ var _a;
2049
+ try {
2050
+ return (_a = pullFromCache(cacheKey(userId))) === null || _a === void 0 ? void 0 : _a.data;
2051
+ }
2052
+ catch (_b) {
2053
+ return undefined;
2054
+ }
2055
+ };
1961
2056
  /**
1962
- * One `getUserFlagsByMe` closure per userId, remembering the newest user object linked for
1963
- * that id. Live objects and collections compare snapshots with `isEqual`, which compares
1964
- * functions by reference: a fresh closure on every linked object would make two reads of the
1965
- * same user unequal and re-dispatch snapshots that did not change.
2057
+ * The server's answer recorded for this user and this exact `hashFlag`, or `undefined` when there
2058
+ * is none for the current user. Never throws.
1966
2059
  */
1967
- const flagsByMeEntries = new Map();
2060
+ const readConfirmedFlag = (userId, hashFlag) => {
2061
+ const me = currentUserId();
2062
+ if (!me || !hashFlag || typeof hashFlag.hash !== 'string')
2063
+ return undefined;
2064
+ const record = readRecord(userId);
2065
+ if (!record || record.reporterId !== me || record.hash !== hashFlag.hash)
2066
+ return undefined;
2067
+ return record.isFlagByMe;
2068
+ };
1968
2069
  /**
1969
- * A Bloom filter has no false negatives, so `isFlaggedByMe === false` is exact and the request
1970
- * can be skipped (REQ-012). The newest user object linked for that id is tested, as of its last
1971
- * update. Without a filter to test, or without a current user, the server is asked.
2070
+ * The v4 request in flight per user, with the generation it started in. A confirm and an exact
2071
+ * read of the same user share one request; a request that started before the current user's own
2072
+ * flag or unflag is not joined.
1972
2073
  */
1973
- const knownNotFlaggedByMe = (userId) => {
2074
+ const inFlight = new Map();
2075
+ const requestFlagsByMe = (userId) => {
2076
+ const generation = generationOf(userId);
2077
+ const pending = inFlight.get(userId);
2078
+ if (pending && pending.generation === generation)
2079
+ return pending.promise;
2080
+ const promise = getFlagsByMe(userId).finally(() => {
2081
+ var _a;
2082
+ if (((_a = inFlight.get(userId)) === null || _a === void 0 ? void 0 : _a.promise) === promise)
2083
+ inFlight.delete(userId);
2084
+ });
2085
+ inFlight.set(userId, { generation, promise });
2086
+ return promise;
2087
+ };
2088
+ const notifyObservers = (userId) => fireEvent('local.user.flagConfirmed', { userId });
2089
+ /**
2090
+ * Records `isFlagByMe` for `hashFlag`. A "maybe" without a record shows `true`, so observers are
2091
+ * told only when the shown value changes.
2092
+ */
2093
+ const recordFlag = (userId, hashFlag, isFlagByMe) => {
1974
2094
  var _a;
1975
2095
  const me = currentUserId();
1976
- const user = (_a = flagsByMeEntries.get(userId)) === null || _a === void 0 ? void 0 : _a.latest;
1977
- if (!me || !user || user.hashFlag === undefined)
2096
+ if (!me || typeof hashFlag.hash !== 'string')
2097
+ return;
2098
+ const shown = (_a = readConfirmedFlag(userId, hashFlag)) !== null && _a !== void 0 ? _a : true;
2099
+ const record = { reporterId: me, hash: hashFlag.hash, isFlagByMe };
2100
+ pushToCache(cacheKey(userId), record);
2101
+ if (shown !== isFlagByMe)
2102
+ notifyObservers(userId);
2103
+ };
2104
+ /**
2105
+ * Starts one background confirm of a "maybe" (REQ-016): at most one in flight per user, and
2106
+ * confirms for the same user at least {@link FLAG_CONFIRM_INTERVAL} apart. A failed confirm
2107
+ * records nothing, surfaces no error, and a later read tries again.
2108
+ */
2109
+ const confirmFlagByMe = (userId, hashFlag) => {
2110
+ if (inFlight.has(userId))
2111
+ return;
2112
+ const now = Date.now();
2113
+ const last = lastConfirmAt.get(userId);
2114
+ if (last !== undefined && now - last < FLAG_CONFIRM_INTERVAL)
2115
+ return;
2116
+ const me = currentUserId();
2117
+ const generation = generationOf(userId);
2118
+ lastConfirmAt.set(userId, now);
2119
+ requestFlagsByMe(userId)
2120
+ .then(({ isFlagByMe }) => {
2121
+ if (currentUserId() !== me || generationOf(userId) !== generation)
2122
+ return;
2123
+ recordFlag(userId, hashFlag, isFlagByMe);
2124
+ })
2125
+ .catch(() => {
2126
+ // Not an answer: isFlaggedByMe stays true until a later read confirms again.
2127
+ });
2128
+ };
2129
+ /**
2130
+ * The value of `Amity.User.isFlaggedByMe` for a user object whose confirmed answer was captured
2131
+ * (`confirmed`) when the object was linked, so a held object never changes (REQ-014).
2132
+ *
2133
+ * - No current user, `hashFlag: null` or a missing `hashFlag`, or "definitely not": `false`.
2134
+ * - "Maybe" with a confirmed answer: that answer, no request.
2135
+ * - "Maybe" without one: `true`, and one background confirm (REQ-016).
2136
+ *
2137
+ * The linked object declares this as a getter inside an object spread, which the build compiles
2138
+ * to `Object.assign`, so it runs once when the user is linked and the object keeps that value.
2139
+ */
2140
+ const isFlaggedByMeFor = (user, confirmed) => {
2141
+ const me = currentUserId();
2142
+ if (!me || !user.hashFlag || !testBloomFilter(user.hashFlag, me))
1978
2143
  return false;
1979
- return !testBloomFilter(user.hashFlag, me);
2144
+ if (confirmed !== undefined)
2145
+ return confirmed;
2146
+ confirmFlagByMe(user.userId, user.hashFlag);
2147
+ return true;
2148
+ };
2149
+ const NOT_FLAGGED = { isFlagByMe: false, flags: [] };
2150
+ const isNotOlder = (next, current) => {
2151
+ var _a, _b;
2152
+ const nextAt = Date.parse((_a = next.updatedAt) !== null && _a !== void 0 ? _a : '');
2153
+ const currentAt = Date.parse((_b = current.updatedAt) !== null && _b !== void 0 ? _b : '');
2154
+ return Number.isNaN(nextAt) || Number.isNaN(currentAt) || nextAt >= currentAt;
2155
+ };
2156
+ /**
2157
+ * One `getUserFlagsByMe` closure per userId, remembering the newest user object linked for that
2158
+ * id. Live objects and collections compare snapshots with `isEqual`, which compares functions by
2159
+ * reference, so a fresh closure per linked object would re-dispatch unchanged snapshots
2160
+ * (REQ-015). An older snapshot linked later does not replace a newer one.
2161
+ */
2162
+ const flagsByMeEntries = new Map();
2163
+ /**
2164
+ * `Amity.User.getUserFlagsByMe()` (REQ-012): `{ isFlagByMe: false, flags: [] }` with no request
2165
+ * when the newest linked user says "not flagged", including a confirmed `false`. Otherwise the
2166
+ * v4 read, returned as is; a confirm already in flight for the user is joined instead of sending
2167
+ * a second request. Its `isFlagByMe` is recorded as the confirmed answer for the
2168
+ * `hashFlag` it was read for; `flags[]` is never stored (REQ-009). A missing `hashFlag` is
2169
+ * unknown, so the server is asked.
2170
+ */
2171
+ const readFlagsByMe = async (userId) => {
2172
+ var _a, _b;
2173
+ const me = currentUserId();
2174
+ const { hashFlag } = (_b = (_a = flagsByMeEntries.get(userId)) === null || _a === void 0 ? void 0 : _a.latest) !== null && _b !== void 0 ? _b : {};
2175
+ if (me && hashFlag !== undefined) {
2176
+ if (!hashFlag || !testBloomFilter(hashFlag, me))
2177
+ return Object.assign(Object.assign({}, NOT_FLAGGED), { flags: [] });
2178
+ if (readConfirmedFlag(userId, hashFlag) === false)
2179
+ return Object.assign(Object.assign({}, NOT_FLAGGED), { flags: [] });
2180
+ }
2181
+ const generation = generationOf(userId);
2182
+ const result = await requestFlagsByMe(userId);
2183
+ if (hashFlag && currentUserId() === me && generationOf(userId) === generation) {
2184
+ recordFlag(userId, hashFlag, result.isFlagByMe);
2185
+ }
2186
+ return result;
1980
2187
  };
1981
2188
  const getUserFlagsByMeFor = (user) => {
1982
2189
  let entry = flagsByMeEntries.get(user.userId);
1983
2190
  if (!entry) {
1984
- entry = {
1985
- latest: user,
1986
- getUserFlagsByMe: async () => {
1987
- if (knownNotFlaggedByMe(user.userId))
1988
- return { isFlagByMe: false, flags: [] };
1989
- return getFlagsByMe(user.userId);
1990
- },
1991
- };
2191
+ entry = { latest: user, getUserFlagsByMe: () => readFlagsByMe(user.userId) };
1992
2192
  flagsByMeEntries.set(user.userId, entry);
1993
2193
  }
1994
- entry.latest = user;
2194
+ else if (isNotOlder(user, entry.latest)) {
2195
+ entry.latest = user;
2196
+ }
1995
2197
  return entry.getUserFlagsByMe;
1996
2198
  };
2199
+ /**
2200
+ * The current user's own flag succeeded (REQ-016). Their report may leave `hashFlag` unchanged
2201
+ * when other reporters had already set all of their bits, so the answer is known: record `true`
2202
+ * for the `hashFlag` the response carries, or drop the answer when it carries none.
2203
+ */
2204
+ const onFlaggedByMe = (user, userId) => {
2205
+ bumpGeneration(userId);
2206
+ if (user === null || user === void 0 ? void 0 : user.hashFlag) {
2207
+ recordFlag(userId, user.hashFlag, true);
2208
+ return;
2209
+ }
2210
+ const dropped = readRecord(userId);
2211
+ dropFromCache(cacheKey(userId), true);
2212
+ if ((dropped === null || dropped === void 0 ? void 0 : dropped.isFlagByMe) === false)
2213
+ notifyObservers(userId);
2214
+ };
2215
+ /**
2216
+ * The current user's own unflag succeeded (`user.unflag` REQ-010, REQ-016). A per-type unflag can
2217
+ * leave other reports in place, so the answer is dropped, never set to `false`; the next "maybe"
2218
+ * is confirmed again.
2219
+ */
2220
+ const onUnflaggedByMe = (userId) => {
2221
+ bumpGeneration(userId);
2222
+ const dropped = readRecord(userId);
2223
+ dropFromCache(cacheKey(userId), true);
2224
+ if ((dropped === null || dropped === void 0 ? void 0 : dropped.isFlagByMe) === false)
2225
+ notifyObservers(userId);
2226
+ };
2227
+ /** Forgets every in-memory trace of the current user's flag state. Records live in the cache. */
2228
+ const resetUserFlagByMe = () => {
2229
+ flagsByMeEntries.clear();
2230
+ inFlight.clear();
2231
+ lastConfirmAt.clear();
2232
+ generations.clear();
2233
+ };
2234
+ /**
2235
+ * Registered through `setupLoginSubscriptions`. Every confirmed answer belongs to the current
2236
+ * user, so the state is cleared on logout; the records themselves go with the cache, and each
2237
+ * also carries the reporter's id.
2238
+ */
2239
+ const userFlagByMeOnLoginHandler = () => {
2240
+ const unsubscribe = onSessionStateChange(state => {
2241
+ if (state === "notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */ || state === "terminated" /* Amity.SessionStates.TERMINATED */) {
2242
+ resetUserFlagByMe();
2243
+ }
2244
+ });
2245
+ return () => {
2246
+ unsubscribe();
2247
+ resetUserFlagByMe();
2248
+ };
2249
+ };
2250
+ /**
2251
+ * Called with the cached user when a recorded answer changes what that user would show, so live
2252
+ * objects and live collections deliver it again (REQ-016, delivery).
2253
+ */
2254
+ const onUserFlagConfirmed = (callback) => createEventSubscriber(getActiveClient(), 'user/onUserFlagConfirmed', 'local.user.flagConfirmed', ({ userId }) => {
2255
+ var _a;
2256
+ const user = (_a = pullFromCache(['user', 'get', userId])) === null || _a === void 0 ? void 0 : _a.data;
2257
+ if (user)
2258
+ callback(user);
2259
+ });
2260
+
1997
2261
  const userLinkedObject = (user) => {
2262
+ // Captured now, so a user object the app holds never changes after a confirm (REQ-014).
2263
+ const confirmedFlagByMe = readConfirmedFlag(user.userId, user.hashFlag);
1998
2264
  return Object.assign(Object.assign({}, user), { get avatar() {
1999
2265
  var _a;
2000
2266
  if (!user.avatarFileId)
@@ -2005,9 +2271,10 @@ const userLinkedObject = (user) => {
2005
2271
  return avatar;
2006
2272
  },
2007
2273
  get isFlaggedByMe() {
2008
- // Local Bloom-filter test on this object's own hashFlag: exact when false, "probably"
2009
- // when true, type-blind, no request. getUserFlagsByMe() gives the exact, per-type answer.
2010
- return testBloomFilter(user.hashFlag, currentUserId());
2274
+ // Bloom test on this object's hashFlag: "definitely not" is exact and sends nothing.
2275
+ // "Maybe" returns the answer confirmed for this hashFlag, or true while one background
2276
+ // confirm runs (REQ-011, REQ-016). Never throws.
2277
+ return isFlaggedByMeFor(user, confirmedFlagByMe);
2011
2278
  },
2012
2279
  /* begin_public_function
2013
2280
  id: user.get_flags_by_me
@@ -2279,13 +2546,13 @@ class NetworkActivitiesWatcher {
2279
2546
  this._listener.clear();
2280
2547
  }
2281
2548
  }
2282
- let instance$b;
2549
+ let instance$a;
2283
2550
  var NetworkActivitiesWatcher$1 = {
2284
2551
  getInstance: () => {
2285
- if (!instance$b) {
2286
- instance$b = new NetworkActivitiesWatcher();
2552
+ if (!instance$a) {
2553
+ instance$a = new NetworkActivitiesWatcher();
2287
2554
  }
2288
- return instance$b;
2555
+ return instance$a;
2289
2556
  },
2290
2557
  };
2291
2558
 
@@ -2469,54 +2736,6 @@ const createHttpTransport = (endpoint) => {
2469
2736
  return instance;
2470
2737
  };
2471
2738
 
2472
- class SessionWatcher {
2473
- constructor() {
2474
- this._sessionState = "notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */;
2475
- this._listener = new Map();
2476
- }
2477
- onSessionStateChange(callback) {
2478
- this._listener.set(callback, callback);
2479
- return () => {
2480
- this._listener.delete(callback);
2481
- };
2482
- }
2483
- setSessionState(state, reason) {
2484
- if (this._sessionState === state)
2485
- return;
2486
- this._sessionState = state;
2487
- this._listener.forEach(cb => cb(state, reason));
2488
- }
2489
- destroy() {
2490
- this._listener.clear();
2491
- }
2492
- }
2493
- let instance$a;
2494
- var SessionWatcher$1 = {
2495
- getInstance: () => {
2496
- if (!instance$a) {
2497
- instance$a = new SessionWatcher();
2498
- }
2499
- return instance$a;
2500
- },
2501
- };
2502
-
2503
- /**
2504
- * ```js
2505
- * import { onSessionStateChange } from '@amityco/ts-sdk'
2506
- * const dispose = onSessionStateChange((state: Amity.SessionStates) => {
2507
- * // ...
2508
- * })
2509
- * ```
2510
- *
2511
- * Fired when any {@link Amity.Client} has a session state change
2512
- *
2513
- * @param callback The function to call when the event was fired
2514
- * @returns an {@link Amity.Unsubscriber} function to stop listening
2515
- *
2516
- * @category Client Events
2517
- */
2518
- const onSessionStateChange = (callback) => SessionWatcher$1.getInstance().onSessionStateChange(callback);
2519
-
2520
2739
  const TOPICS = {
2521
2740
  [exports.AmityAutoSubscription.NETWORK]: [getNetworkTopic],
2522
2741
  [exports.AmityAutoSubscription.CHAT]: [
@@ -8584,7 +8803,7 @@ const setupLoginSubscriptions = (unsubWatcher) => {
8584
8803
  // NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
8585
8804
  // the channel because currently backend can't handle this, so every time a user is banned from
8586
8805
  // a channel or the channel is deleted the channel's unread count will not be reset to zero
8587
- onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler(), reactionSyncEngineOnLoginHandler(), blockedUserSyncEngineOnLoginHandler(), blockingUserSyncEngineOnLoginHandler(), blockRealtimeOnLoginHandler(), resetAutoSubscriptionsOnLogout(), CursorController.clearOnLogout(), postCommentCountEngineOnLoginHandler(), channelActivityOnLoginHandler());
8806
+ onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler(), reactionSyncEngineOnLoginHandler(), blockedUserSyncEngineOnLoginHandler(), blockingUserSyncEngineOnLoginHandler(), blockRealtimeOnLoginHandler(), resetAutoSubscriptionsOnLogout(), CursorController.clearOnLogout(), postCommentCountEngineOnLoginHandler(), channelActivityOnLoginHandler(), userFlagByMeOnLoginHandler());
8588
8807
  if (client.useLegacyUnreadCount) {
8589
8808
  subscriptions.push(readReceiptSyncEngineOnLoginHandler());
8590
8809
  }
@@ -13804,7 +14023,7 @@ const getWatchSessionStorage = () => {
13804
14023
  return storageInstance;
13805
14024
  };
13806
14025
 
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=";
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-----";
13808
14027
  /*
13809
14028
  * The crypto algorithm used for importing key and signing string
13810
14029
  */
@@ -15630,8 +15849,11 @@ const flagUser = async (userId, options) => {
15630
15849
  if (client.cache) {
15631
15850
  ingestInCache(payload);
15632
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);
15633
15855
  fireEvent('user.flagged', data);
15634
- return userLinkedObject((_a = payload.users.find(user => user.userId === userId)) !== null && _a !== void 0 ? _a : payload.users[0]);
15856
+ return userLinkedObject(flagged);
15635
15857
  };
15636
15858
  /* end_public_function */
15637
15859
 
@@ -15651,8 +15873,11 @@ const submitUserReport = async (apiName, userId, body) => {
15651
15873
  if (client.cache) {
15652
15874
  ingestInCache(payload);
15653
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);
15654
15879
  fireEvent('user.flagged', data);
15655
- return userLinkedObject((_a = payload.users.find(user => user.userId === userId)) !== null && _a !== void 0 ? _a : payload.users[0]);
15880
+ return userLinkedObject(flagged);
15656
15881
  };
15657
15882
 
15658
15883
  /** Maximum `comment` length, in string length units, after trimming. */
@@ -15831,14 +16056,15 @@ const flagUserBehavior = async (userId, options) => submitUserReport('flagUserBe
15831
16056
  * untouched. Withdrawing a report that does not exist succeeds and changes nothing.
15832
16057
  *
15833
16058
  * 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`).
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.
15837
16063
  *
15838
16064
  * Sends `DELETE /api/v4/me/flags/:userId`, with `?reportType=<type>` when one is given.
15839
16065
  *
15840
16066
  * @param userId The ID of the user to unflag
15841
- * @param options Optional; `reportType` selects a single report to withdraw. For a report that `isUserFlaggedByMe` lists with `reportType: null`, pass `UserReportTypeFilterEnum.UNTYPED`
16067
+ * @param options Optional; `reportType` selects a single report to withdraw. For a report that `user.getUserFlagsByMe()` lists with `reportType: null`, pass `UserReportTypeFilterEnum.UNTYPED`
15842
16068
  * @returns `true` once the server has processed the withdrawal
15843
16069
  * @throws {ASCInvalidParameterError} `INVALID_PARAMETERS` when `reportType` is not a `UserReportTypeFilterEnum` value, before any request is made
15844
16070
  *
@@ -15857,6 +16083,8 @@ const unflagUser = async (userId, options) => {
15857
16083
  if (client.cache) {
15858
16084
  ingestInCache(payload);
15859
16085
  }
16086
+ // Drop the confirmed answer; never set it to false: other reports may remain (REQ-010).
16087
+ onUnflaggedByMe(userId);
15860
16088
  fireEvent('user.unflagged', data);
15861
16089
  return !!payload;
15862
16090
  };
@@ -15868,33 +16096,19 @@ const unflagUser = async (userId, options) => {
15868
16096
  /**
15869
16097
  * ```js
15870
16098
  * import { UserRepository } from '@amityco/ts-sdk'
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))
16099
+ * const isFlagged = await UserRepository.isUserFlaggedByMe(userId)
15876
16100
  * ```
15877
16101
  *
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.
16102
+ * Whether the current user has reported a user. Sends one request per call and caches
16103
+ * nothing.
15888
16104
  *
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.
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.
15895
16109
  *
15896
16110
  * @param userId The ID of the user to check
15897
- * @returns the caller's {@link Amity.UserFlagByMe} on that user
16111
+ * @returns `true` if the current user has reported the user, `false` if not
15898
16112
  *
15899
16113
  * @category Report API
15900
16114
  * @async
@@ -15902,7 +16116,8 @@ const unflagUser = async (userId, options) => {
15902
16116
  const isUserFlaggedByMe = async (userId) => {
15903
16117
  const client = getActiveClient();
15904
16118
  client.log('user/isUserFlaggedByMe', userId);
15905
- return getFlagsByMe(userId);
16119
+ const { isFlagByMe } = await getFlagsByMe(userId);
16120
+ return isFlagByMe;
15906
16121
  };
15907
16122
  /* end_public_function */
15908
16123
 
@@ -16141,7 +16356,7 @@ const getUser = (userId, callback) => {
16141
16356
  const reactor = (response) => {
16142
16357
  return callback(Object.assign(Object.assign({}, response), { data: response.data ? LinkedObject.user(response.data) : response.data }));
16143
16358
  };
16144
- return liveObject(userId, reactor, 'userId', getUser$1, [
16359
+ const unsubscribeUser = liveObject(userId, reactor, 'userId', getUser$1, [
16145
16360
  onUserFetched,
16146
16361
  onUserUpdated,
16147
16362
  onUserDeleted$2,
@@ -16149,6 +16364,17 @@ const getUser = (userId, callback) => {
16149
16364
  onUserUnflagged,
16150
16365
  onUserFlagCleared,
16151
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
+ };
16152
16378
  };
16153
16379
  /* end_public_function */
16154
16380
 
@@ -16246,13 +16472,24 @@ class UserLiveCollectionController extends LiveCollectionController {
16246
16472
  this.queryStreamController.appendToQueryStream(response, direction, refresh);
16247
16473
  }
16248
16474
  startSubscription() {
16249
- return this.queryStreamController.subscribeRTE([
16250
- { fn: onUserDeleted$2, action: EnumUserActions.OnUserDeleted },
16251
- { fn: onUserUpdated, action: EnumUserActions.OnUserUpdated },
16252
- { fn: onUserFlagged, action: EnumUserActions.OnUserFlagged },
16253
- { fn: onUserUnflagged, action: EnumUserActions.OnUserUnflagged },
16254
- { fn: onUserFlagCleared, action: EnumUserActions.OnUserFlagCleared },
16255
- ]);
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
+ ];
16256
16493
  }
16257
16494
  notifyChange({ origin, loading, error }) {
16258
16495
  var _a, _b;
@@ -16804,13 +17041,24 @@ class SearchUserLiveCollectionController extends LiveCollectionController {
16804
17041
  this.queryStreamController.appendToQueryStream(response, direction, refresh);
16805
17042
  }
16806
17043
  startSubscription() {
16807
- return this.queryStreamController.subscribeRTE([
16808
- { fn: onUserDeleted$2, action: EnumUserActions.OnUserDeleted },
16809
- { fn: onUserUpdated, action: EnumUserActions.OnUserUpdated },
16810
- { fn: onUserFlagged, action: EnumUserActions.OnUserFlagged },
16811
- { fn: onUserUnflagged, action: EnumUserActions.OnUserUnflagged },
16812
- { fn: onUserFlagCleared, action: EnumUserActions.OnUserFlagCleared },
16813
- ]);
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
+ ];
16814
17062
  }
16815
17063
  notifyChange({ origin, loading, error }) {
16816
17064
  var _a, _b;