@amityco/ts-sdk-react-native 7.5.2 → 7.5.3

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/index.esm.js CHANGED
@@ -101,8 +101,8 @@ const PostContentType = Object.freeze({
101
101
 
102
102
  function getVersion() {
103
103
  try {
104
- // the string ''v7.5.2-esm'' should be replaced by actual value by @rollup/plugin-replace
105
- return 'v7.5.2-esm';
104
+ // the string ''v7.5.3-esm'' should be replaced by actual value by @rollup/plugin-replace
105
+ return 'v7.5.3-esm';
106
106
  }
107
107
  catch (error) {
108
108
  return '__dev__';
@@ -24800,6 +24800,266 @@ const removeChannelMarkerCache = (channel) => {
24800
24800
  dropFromCache(['channelMarker', 'get', id], true);
24801
24801
  };
24802
24802
 
24803
+ /* eslint-disable no-param-reassign */
24804
+ /*
24805
+ * declared earlier to accomodate case when logging in with a different user
24806
+ * than the one already connected, in which case the existing subscriptions need
24807
+ * to be cleared
24808
+ */
24809
+ let subscriptions = [];
24810
+ async function runMqtt() {
24811
+ await modifyMqttConnection();
24812
+ }
24813
+ /* begin_public_function
24814
+ id: client.login
24815
+ */
24816
+ /**
24817
+ * ```js
24818
+ * import { login } from '@amityco/ts-sdk-react-native/client/api'
24819
+ * const success = await login({
24820
+ * userId: 'XYZ123456789',
24821
+ * })
24822
+ * ```
24823
+ *
24824
+ * Connects an {@link Amity.Client} instance to ASC servers
24825
+ *
24826
+ * @param params the connect parameters
24827
+ * @param params.userId the user ID for the current session
24828
+ * @param params.displayName the user's displayName for the current session
24829
+ * @param params.deviceId Manual override of the user's device id (for device management)
24830
+ * @param params.authToken The authentication token - necessary when network option is set to secure
24831
+ * @returns a success boolean if connected
24832
+ *
24833
+ * @category Client API
24834
+ * @async
24835
+ */
24836
+ const login = async (params, sessionHandler, config) => {
24837
+ var _a;
24838
+ const client = getActiveClient();
24839
+ let unsubWatcher;
24840
+ client.log('client/api/connectClient', Object.assign({ apiKey: client.apiKey, sessionState: client.sessionState }, params));
24841
+ // if connecting to a different userId than the one that is connected currently
24842
+ if (client.userId && client.userId !== params.userId) {
24843
+ await logout();
24844
+ // Remove subscription to ban and delete
24845
+ subscriptions.forEach(fn => fn());
24846
+ subscriptions = [];
24847
+ }
24848
+ // default values
24849
+ const defaultDeviceId = await getDeviceId();
24850
+ try {
24851
+ const { users } = await setClientToken({
24852
+ params: Object.assign(Object.assign({}, params), { displayName: params === null || params === void 0 ? void 0 : params.displayName, deviceId: (params === null || params === void 0 ? void 0 : params.deviceId) || defaultDeviceId }),
24853
+ options: {
24854
+ setAccessTokenCookie: true,
24855
+ },
24856
+ });
24857
+ const user = users.find(u => u.userId === params.userId);
24858
+ if (user == null) {
24859
+ throw new ASCError(`${params.userId} has not been founded`, 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "error" /* Amity.ErrorLevel.ERROR */);
24860
+ }
24861
+ if (user.isDeleted) {
24862
+ terminateClient("userDeleted" /* Amity.TokenTerminationReason.USER_DELETED */);
24863
+ return false;
24864
+ }
24865
+ if (user.isGlobalBanned) {
24866
+ terminateClient("globalBan" /* Amity.TokenTerminationReason.GLOBAL_BAN */);
24867
+ return false;
24868
+ }
24869
+ // FIXME: events are duplicated if connectClient is called few times without disconnectClient
24870
+ // wire websocket events to our event emitter
24871
+ proxyWebsocketEvents(client.ws, client.emitter);
24872
+ (_a = client.ws) === null || _a === void 0 ? void 0 : _a.open();
24873
+ client.userId = user.userId;
24874
+ client.sessionHandler = sessionHandler;
24875
+ /*
24876
+ * Cannot push to subscriptions as watcher needs to continue working even if
24877
+ * token expires
24878
+ */
24879
+ unsubWatcher = client.accessTokenExpiryWatcher(sessionHandler);
24880
+ setActiveUser(user);
24881
+ }
24882
+ catch (error) {
24883
+ /*
24884
+ * if getting token failed session state reverts to initial state when app
24885
+ * is first launched
24886
+ */
24887
+ SessionWatcher$1.getInstance().setSessionState("notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */);
24888
+ // pass error down tree so the calling function handle it
24889
+ throw error;
24890
+ }
24891
+ if ((config === null || config === void 0 ? void 0 : config.disableRTE) !== true) {
24892
+ runMqtt();
24893
+ }
24894
+ await initializeMessagePreviewSetting();
24895
+ if (subscriptions.length === 0) {
24896
+ subscriptions.push(
24897
+ // GLOBAL_BAN
24898
+ onClientBanned((_) => {
24899
+ terminateClient("globalBan" /* Amity.TokenTerminationReason.GLOBAL_BAN */);
24900
+ subscriptions.forEach(fn => fn());
24901
+ unsubWatcher();
24902
+ }), onTokenTerminated(_ => {
24903
+ terminateClient();
24904
+ subscriptions.forEach(fn => fn());
24905
+ unsubWatcher();
24906
+ }), onUserDeleted$2((user) => {
24907
+ if (user.userId === client.userId) {
24908
+ terminateClient("userDeleted" /* Amity.TokenTerminationReason.USER_DELETED */);
24909
+ subscriptions.forEach(fn => fn());
24910
+ unsubWatcher();
24911
+ }
24912
+ }), onTokenExpired(state => {
24913
+ SessionWatcher$1.getInstance().setSessionState(state);
24914
+ logout();
24915
+ subscriptions.forEach(fn => fn());
24916
+ }),
24917
+ // NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
24918
+ // the channel because currently backend can't handle this, so every time a user is banned from
24919
+ // a channel or the channel is deleted the channel's unread count will not be reset to zero
24920
+ onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler());
24921
+ if (client.useLegacyUnreadCount) {
24922
+ subscriptions.push(readReceiptSyncEngineOnLoginHandler());
24923
+ }
24924
+ else
24925
+ subscriptions.push(legacyReadReceiptSyncEngineOnLoginHandler());
24926
+ }
24927
+ return true;
24928
+ };
24929
+ /* end_public_function */
24930
+
24931
+ /* begin_public_function
24932
+ id: client.renew_access_token
24933
+ */
24934
+ /*
24935
+ * Renewal defintion accepted by SessionHandler interface
24936
+ *
24937
+ * Tech Spec:
24938
+ * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#Session-Handler
24939
+ *
24940
+ * @category private
24941
+ */
24942
+ const renewal = () => {
24943
+ let tokenRenewed = false;
24944
+ let renewTimeoutId;
24945
+ const client = getActiveClient();
24946
+ client.log('initiating access token renewal');
24947
+ /*
24948
+ * Renews a token if it is hasn't been renewed before. Also marks token as
24949
+ * renewed once done
24950
+ * Per instance of Renewal, only one renewal is allowed
24951
+ */
24952
+ const renewToken = async (authToken) => {
24953
+ const { userId, displayName } = getActiveUser();
24954
+ const deviceId = await getDeviceId();
24955
+ const params = { userId, displayName, authToken, deviceId };
24956
+ if (client.sessionState === "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */ && client.sessionHandler) {
24957
+ await login(params, client.sessionHandler);
24958
+ }
24959
+ else {
24960
+ // about to expire
24961
+ await setClientToken({
24962
+ params,
24963
+ options: {
24964
+ setAccessTokenCookie: true,
24965
+ },
24966
+ });
24967
+ }
24968
+ tokenRenewed = true;
24969
+ if (renewTimeoutId)
24970
+ clearTimeout(renewTimeoutId);
24971
+ };
24972
+ return {
24973
+ renew: () => {
24974
+ if (tokenRenewed) {
24975
+ console.log("'renew' method can be called only once per renewal instance");
24976
+ return;
24977
+ }
24978
+ renewToken();
24979
+ },
24980
+ renewWithAuthToken: (authToken) => {
24981
+ if (tokenRenewed) {
24982
+ console.log("'renewWithAuthToken' method can be called only once per renewal instance");
24983
+ return;
24984
+ }
24985
+ renewToken(authToken);
24986
+ },
24987
+ unableToRetrieveAuthToken: () => {
24988
+ renewTimeoutId = setTimeout(() => {
24989
+ var _a;
24990
+ (_a = client.sessionHandler) === null || _a === void 0 ? void 0 : _a.sessionWillRenewAccessToken(renewal());
24991
+ }, ACCESS_TOKEN_WATCHER_INTERVAL);
24992
+ },
24993
+ };
24994
+ };
24995
+ /* end_public_function */
24996
+
24997
+ const ABOUT_TO_EXPIRE_THRESHOLD = 80 / 100;
24998
+ const COMPENSATED_DELAY = 5 * MINUTE;
24999
+ /*
25000
+ * a helper function to check if the token has expires
25001
+ *
25002
+ * @param token to be checked
25003
+ * @returns boolean indicating if token expires
25004
+ *
25005
+ * @category private
25006
+ */
25007
+ const isExpired = (expiresAt) => Date.now() > Date.parse(expiresAt) - COMPENSATED_DELAY;
25008
+ /*
25009
+ * a helper function to check if the token is about to expire
25010
+ *
25011
+ * @param token to be checked
25012
+ * @returns boolean indicating if token is aboutToExpire
25013
+ *
25014
+ * @category private
25015
+ */
25016
+ const isAboutToExpire = (params) => {
25017
+ const { expiresAt, issuedAt } = params;
25018
+ const expires = Date.parse(expiresAt);
25019
+ const issued = Date.parse(issuedAt);
25020
+ const now = Date.now();
25021
+ const duration = expires - issued - COMPENSATED_DELAY;
25022
+ const aboutToExpireAt = issued + duration * ABOUT_TO_EXPIRE_THRESHOLD;
25023
+ return now > aboutToExpireAt && now < expires;
25024
+ };
25025
+ /*
25026
+ * Monitors time to expire of token and updates session state to aboutToExpire
25027
+ *
25028
+ * @returns intervalId to be cleared after trigger
25029
+ *
25030
+ * @category private
25031
+ */
25032
+ const accessTokenExpiryWatcher = (sessionHandler) => {
25033
+ const interval = setInterval(() => {
25034
+ const client = getActiveClient();
25035
+ if (!client.token)
25036
+ return;
25037
+ const { issuedAt, expiresAt } = client.token;
25038
+ if (isExpired(expiresAt)) {
25039
+ /*
25040
+ * the event handler will take care of updating session state
25041
+ * Note, this will also clear the interval id, so this event will only be
25042
+ * fired once
25043
+ */
25044
+ fireEvent('tokenExpired', "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */);
25045
+ /*
25046
+ * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#Automatically-initiate-renewal-flow
25047
+ *
25048
+ * Why sechduled task?
25049
+ * Since fireEvent is scheduled, it will be called
25050
+ * after sessionHandler leading to an invalid state change from
25051
+ * establishing to tokenExpired
25052
+ */
25053
+ scheduleTask(() => sessionHandler.sessionWillRenewAccessToken(renewal()));
25054
+ return;
25055
+ }
25056
+ if (isAboutToExpire({ expiresAt, issuedAt })) {
25057
+ sessionHandler.sessionWillRenewAccessToken(renewal());
25058
+ }
25059
+ }, ACCESS_TOKEN_WATCHER_INTERVAL);
25060
+ return () => clearInterval(interval);
25061
+ };
25062
+
24803
25063
  const callbacks$9 = [];
24804
25064
  let mainDisposer$8 = null;
24805
25065
  const dispose$9 = (cb) => {
@@ -25072,33 +25332,6 @@ const onChannelMemberRoleRemoved = (callback) => {
25072
25332
  return () => dispose(callback);
25073
25333
  };
25074
25334
 
25075
- /**
25076
- * ```js
25077
- * import { onUserMarkerSync } from '@amityco/ts-sdk-react-native'
25078
- * const dispose = onUserMarkerSync(UserMarker => {
25079
- * // ...
25080
- * })
25081
- * ```
25082
- *
25083
- * Fired when an {@link Amity.UserMarker} has been sync
25084
- *
25085
- * @param callback The function to call when the event was fired
25086
- * @returns an {@link Amity.Unsubscriber} function to stop listening
25087
- *
25088
- * @category UserMarker Events
25089
- */
25090
- const onUserMarkerSync = (callback) => {
25091
- const client = getActiveClient();
25092
- const filter = (payload) => {
25093
- const { userMarkers, userEntityMarkers: userEntityMarkersPayload, userFeedMarkers: userFeedMarkersPayload } = payload, rest = __rest(payload, ["userMarkers", "userEntityMarkers", "userFeedMarkers"]);
25094
- const userEntityMarkers = convertChannelMarkerResponse(userEntityMarkersPayload);
25095
- const userFeedMarker = convertSubChannelMarkerResponse(userFeedMarkersPayload);
25096
- ingestInCache(Object.assign({ userMarkers, userEntityMarkers, userFeedMarker }, rest));
25097
- callback(userMarkers[0]);
25098
- };
25099
- return createEventSubscriber(client, 'UserMarker/onUserMarkerSync', 'marker.user-sync', filter);
25100
- };
25101
-
25102
25335
  /**
25103
25336
  * ```js
25104
25337
  * import { onFeedMarkerUpdated } from '@amityco/ts-sdk-react-native'
@@ -25370,52 +25603,11 @@ const markerSync = async (deviceLastSyncAt) => {
25370
25603
  };
25371
25604
  };
25372
25605
 
25373
- const enableUnreadCount = () => {
25374
- const client = getActiveClient();
25375
- client.log('client/api/isUnreadCountEnabled', client.isUnreadCountEnabled);
25376
- if (!client) {
25377
- throw new ASCError('There is no active client', 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "fatal" /* Amity.ErrorLevel.FATAL */);
25378
- }
25379
- if (client.isUnreadCountEnabled)
25380
- return false;
25381
- client.isUnreadCountEnabled = true;
25382
- client.useLegacyUnreadCount = false;
25383
- client.emitter.emit('unreadCountEnabled', true);
25384
- return true;
25385
- };
25386
-
25387
- /**
25388
- * ```js
25389
- * import { onFeedMarkerUpdated } from '@amityco/ts-sdk-react-native'
25390
- * const dispose = onFeedMarkerUpdated(feedMarker => {
25391
- * // ...
25392
- * })
25393
- * ```
25394
- *
25395
- * Fired when an {@link Amity.UserFeedMarker} has been updated
25396
- *
25397
- * @param callback The function to call when the event was fired
25398
- * @returns an {@link Amity.Unsubscriber} function to stop listening
25399
- *
25400
- * @category FeedMarker Events
25401
- */
25402
- const onUserFeedMarkerUpdated = (callback) => {
25403
- const client = getActiveClient();
25404
- const filter = (payload) => {
25405
- persistUnreadCountInfo(payload);
25406
- payload.feedMarkers.forEach(feedMarker => {
25407
- callback(feedMarker);
25408
- });
25409
- };
25410
- return createEventSubscriber(client, 'feedMarker/onUserFeedMarkerUpdated', 'marker.userFeed-updated', filter);
25411
- };
25412
-
25413
25606
  const SYNC_TRIGGER_INTERVAL_TIME = 2000;
25414
- const ON_SUB_CHANNEL_DELETE_SYNC_TRIGGER_DELAY = 2000;
25415
25607
  let isSyncRunning = false;
25416
25608
  let disposers$1 = [];
25417
25609
  let isWaitingForResponse = false;
25418
- let isConsistentMode = true;
25610
+ const isConsistentMode = true;
25419
25611
  let deviceLastSyncAt = null;
25420
25612
  const getDeviceLastSyncAt = () => {
25421
25613
  if (deviceLastSyncAt == null) {
@@ -25430,12 +25622,6 @@ const saveDeviceLastSyncAt = (lastSyncAt) => {
25430
25622
  deviceLastSyncAt = lastSyncAt;
25431
25623
  }
25432
25624
  };
25433
- const fetchDeviceLastSyncAt = async () => {
25434
- const { data: userMarker } = await getUserMarker();
25435
- if (userMarker == null)
25436
- return;
25437
- saveDeviceLastSyncAt(new Date(userMarker.lastSyncAt));
25438
- };
25439
25625
  /**
25440
25626
  * list of conditions that make timer still trigger the syncing process.
25441
25627
  * If it's empty, it means sync is stopped.
@@ -25470,375 +25656,74 @@ const markerSyncTrigger = async () => {
25470
25656
  }
25471
25657
  if (events.length === 0) {
25472
25658
  // no event that require to call marker sync API
25473
- return;
25474
- }
25475
- try {
25476
- isWaitingForResponse = true;
25477
- // any past events are considered processed here.
25478
- // however during waiting for the response, RTE could add the new message event;
25479
- // which will make the engine trigger another call next round.
25480
- events = [];
25481
- const response = await markerSync(getDeviceLastSyncAt().toISOString());
25482
- const latestLastSyncAt = response.data.userMarkers.reduce((maxLastSyncAt, userMarker) => {
25483
- if (maxLastSyncAt == null ||
25484
- maxLastSyncAt.getTime() < new Date(userMarker.lastSyncAt).getTime()) {
25485
- return new Date(userMarker.lastSyncAt);
25486
- }
25487
- return maxLastSyncAt;
25488
- }, null);
25489
- saveDeviceLastSyncAt(latestLastSyncAt);
25490
- if (response.hasMore) {
25491
- events.push("has_more" /* Amity.MarkerSyncEvent.HAS_MORE */);
25492
- }
25493
- }
25494
- catch (_a) {
25495
- // prevent sync from stopping
25496
- }
25497
- finally {
25498
- if (isWaitingForResponse) {
25499
- isWaitingForResponse = false;
25500
- }
25501
- }
25502
- };
25503
- const registerEventListeners = () => {
25504
- if (disposers$1.length > 0) {
25505
- return;
25506
- }
25507
- // based on the tech spec design, we designed a fetch marker in case of these events
25508
- // - new message
25509
- // - create channel
25510
- // - remove channel
25511
- // - app going to online again after offline
25512
- disposers$1.push(onOnline(() => {
25513
- // should add RESUME to the event to trigger marker syncing again
25514
- events.push("resume" /* Amity.MarkerSyncEvent.RESUME */);
25515
- }), onMessageCreatedMqtt(message => {
25516
- // only conversation, community and broadcast types can sync
25517
- const client = getActiveClient();
25518
- if (isUnreadCountSupport$1(message) && message.creatorId !== client.userId)
25519
- events.push("new message" /* Amity.MarkerSyncEvent.NEW_MESSAGE */);
25520
- }), onChannelCreated(() => events.push("subchannel is created" /* Amity.MarkerSyncEvent.CHANNEL_CREATED */)), onChannelDeleted(() => events.push("subchannel is deleted" /* Amity.MarkerSyncEvent.CHANNEL_DELETED */)), onChannelJoined(() => events.push("subchannel is joined" /* Amity.MarkerSyncEvent.CHANNEL_JOINED */)), onChannelLeft(() => events.push("subchannel is left" /* Amity.MarkerSyncEvent.CHANNEL_LEFT */)), onSubChannelCreated(() => events.push("subchannel is created" /* Amity.MarkerSyncEvent.SUB_CHANNEL_CREATED */)), onSubChannelDeleted(() =>
25521
- /*
25522
- workaround: when receiving the event for sub-channel deletion,
25523
- before triggering marker update, the SDK will have to add a 2-second delay.
25524
- so that the unread count is calculated correctly.
25525
- */
25526
- setTimeout(() => events.push("subchannel is deleted" /* Amity.MarkerSyncEvent.SUBCHANNEL_IS_DELETED */), ON_SUB_CHANNEL_DELETE_SYNC_TRIGGER_DELAY)), onFeedMarkerUpdated(() => events.push("feed marker updated" /* Amity.MarkerSyncEvent.MARKER_UPDATED */)), onUserMarkerSync(() => events.push("feed marker updated" /* Amity.MarkerSyncEvent.MARKER_UPDATED */)), onUserFeedMarkerUpdated(() => events.push("feed marker updated" /* Amity.MarkerSyncEvent.MARKER_UPDATED */)));
25527
- };
25528
- const unRegisterEventListeners = () => {
25529
- disposers$1.forEach(fn => fn());
25530
- disposers$1 = [];
25531
- };
25532
- const startMarkerSync = async () => {
25533
- await fetchDeviceLastSyncAt();
25534
- pushMarkerSyncEvent("start syncing" /* Amity.MarkerSyncEvent.START_SYNCING */);
25535
- isConsistentMode = true;
25536
- isSyncRunning = true;
25537
- registerEventListeners();
25538
- return unRegisterEventListeners;
25539
- };
25540
- /**
25541
- ```js
25542
- * import { startUnreadSync } from '@amityco/ts-sdk-react-native'
25543
- * startUnreadSync()
25544
- * ```
25545
- *
25546
- * start syncing to keep feed markers, channel markers and user makers cache
25547
- * update to the server.
25548
- *
25549
- * @category Marker API
25550
- */
25551
- const startUnreadSync = async () => {
25552
- await fetchDeviceLastSyncAt();
25553
- pushMarkerSyncEvent("start syncing" /* Amity.MarkerSyncEvent.START_SYNCING */);
25554
- enableUnreadCount();
25555
- isConsistentMode = false;
25556
- isSyncRunning = true;
25557
- registerEventListeners();
25558
- };
25559
- /**
25560
- ```js
25561
- * import { stopUnreadSync } from '@amityco/ts-sdk-react-native'
25562
- * stopUnreadSync()
25563
- * ```
25564
- *
25565
- * stop unread syncing
25566
- *
25567
- * @category Marker API
25568
- */
25569
- const stopUnreadSync = () => {
25570
- isSyncRunning = false;
25571
- setMarkerSyncEvents([]);
25572
- unRegisterEventListeners();
25573
- };
25574
- setIntervalTask(async () => {
25575
- if (!isSyncRunning)
25576
- return;
25577
- await markerSyncTrigger();
25578
- }, SYNC_TRIGGER_INTERVAL_TIME);
25579
- const getMarkerSyncConsistentMode = () => isConsistentMode;
25580
-
25581
- /* eslint-disable no-param-reassign */
25582
- /*
25583
- * declared earlier to accomodate case when logging in with a different user
25584
- * than the one already connected, in which case the existing subscriptions need
25585
- * to be cleared
25586
- */
25587
- let subscriptions = [];
25588
- async function runMqtt() {
25589
- await modifyMqttConnection();
25590
- }
25591
- /* begin_public_function
25592
- id: client.login
25593
- */
25594
- /**
25595
- * ```js
25596
- * import { login } from '@amityco/ts-sdk-react-native/client/api'
25597
- * const success = await login({
25598
- * userId: 'XYZ123456789',
25599
- * })
25600
- * ```
25601
- *
25602
- * Connects an {@link Amity.Client} instance to ASC servers
25603
- *
25604
- * @param params the connect parameters
25605
- * @param params.userId the user ID for the current session
25606
- * @param params.displayName the user's displayName for the current session
25607
- * @param params.deviceId Manual override of the user's device id (for device management)
25608
- * @param params.authToken The authentication token - necessary when network option is set to secure
25609
- * @returns a success boolean if connected
25610
- *
25611
- * @category Client API
25612
- * @async
25613
- */
25614
- const login = async (params, sessionHandler, config) => {
25615
- var _a;
25616
- const client = getActiveClient();
25617
- let unsubWatcher;
25618
- client.log('client/api/connectClient', Object.assign({ apiKey: client.apiKey, sessionState: client.sessionState }, params));
25619
- // if connecting to a different userId than the one that is connected currently
25620
- if (client.userId && client.userId !== params.userId) {
25621
- await logout();
25622
- // Remove subscription to ban and delete
25623
- subscriptions.forEach(fn => fn());
25624
- subscriptions = [];
25659
+ return;
25625
25660
  }
25626
- // default values
25627
- const defaultDeviceId = await getDeviceId();
25628
25661
  try {
25629
- const { users } = await setClientToken({
25630
- params: Object.assign(Object.assign({}, params), { displayName: params === null || params === void 0 ? void 0 : params.displayName, deviceId: (params === null || params === void 0 ? void 0 : params.deviceId) || defaultDeviceId }),
25631
- options: {
25632
- setAccessTokenCookie: true,
25633
- },
25634
- });
25635
- const user = users.find(u => u.userId === params.userId);
25636
- if (user == null) {
25637
- throw new ASCError(`${params.userId} has not been founded`, 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "error" /* Amity.ErrorLevel.ERROR */);
25638
- }
25639
- if (user.isDeleted) {
25640
- terminateClient("userDeleted" /* Amity.TokenTerminationReason.USER_DELETED */);
25641
- return false;
25642
- }
25643
- if (user.isGlobalBanned) {
25644
- terminateClient("globalBan" /* Amity.TokenTerminationReason.GLOBAL_BAN */);
25645
- return false;
25662
+ isWaitingForResponse = true;
25663
+ // any past events are considered processed here.
25664
+ // however during waiting for the response, RTE could add the new message event;
25665
+ // which will make the engine trigger another call next round.
25666
+ events = [];
25667
+ const response = await markerSync(getDeviceLastSyncAt().toISOString());
25668
+ const latestLastSyncAt = response.data.userMarkers.reduce((maxLastSyncAt, userMarker) => {
25669
+ if (maxLastSyncAt == null ||
25670
+ maxLastSyncAt.getTime() < new Date(userMarker.lastSyncAt).getTime()) {
25671
+ return new Date(userMarker.lastSyncAt);
25672
+ }
25673
+ return maxLastSyncAt;
25674
+ }, null);
25675
+ saveDeviceLastSyncAt(latestLastSyncAt);
25676
+ if (response.hasMore) {
25677
+ events.push("has_more" /* Amity.MarkerSyncEvent.HAS_MORE */);
25646
25678
  }
25647
- // FIXME: events are duplicated if connectClient is called few times without disconnectClient
25648
- // wire websocket events to our event emitter
25649
- proxyWebsocketEvents(client.ws, client.emitter);
25650
- (_a = client.ws) === null || _a === void 0 ? void 0 : _a.open();
25651
- client.userId = user.userId;
25652
- client.sessionHandler = sessionHandler;
25653
- /*
25654
- * Cannot push to subscriptions as watcher needs to continue working even if
25655
- * token expires
25656
- */
25657
- unsubWatcher = client.accessTokenExpiryWatcher(sessionHandler);
25658
- setActiveUser(user);
25659
25679
  }
25660
- catch (error) {
25661
- /*
25662
- * if getting token failed session state reverts to initial state when app
25663
- * is first launched
25664
- */
25665
- SessionWatcher$1.getInstance().setSessionState("notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */);
25666
- // pass error down tree so the calling function handle it
25667
- throw error;
25668
- }
25669
- if ((config === null || config === void 0 ? void 0 : config.disableRTE) !== true) {
25670
- runMqtt();
25680
+ catch (_a) {
25681
+ // prevent sync from stopping
25671
25682
  }
25672
- await initializeMessagePreviewSetting();
25673
- if (subscriptions.length === 0) {
25674
- subscriptions.push(
25675
- // GLOBAL_BAN
25676
- onClientBanned((_) => {
25677
- terminateClient("globalBan" /* Amity.TokenTerminationReason.GLOBAL_BAN */);
25678
- subscriptions.forEach(fn => fn());
25679
- unsubWatcher();
25680
- }), onTokenTerminated(_ => {
25681
- terminateClient();
25682
- subscriptions.forEach(fn => fn());
25683
- unsubWatcher();
25684
- }), onUserDeleted$2((user) => {
25685
- if (user.userId === client.userId) {
25686
- terminateClient("userDeleted" /* Amity.TokenTerminationReason.USER_DELETED */);
25687
- subscriptions.forEach(fn => fn());
25688
- unsubWatcher();
25689
- }
25690
- }), onTokenExpired(state => {
25691
- SessionWatcher$1.getInstance().setSessionState(state);
25692
- logout();
25693
- subscriptions.forEach(fn => fn());
25694
- }),
25695
- // NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
25696
- // the channel because currently backend can't handle this, so every time a user is banned from
25697
- // a channel or the channel is deleted the channel's unread count will not be reset to zero
25698
- onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler());
25699
- if (client.useLegacyUnreadCount) {
25700
- subscriptions.push(readReceiptSyncEngineOnLoginHandler());
25683
+ finally {
25684
+ if (isWaitingForResponse) {
25685
+ isWaitingForResponse = false;
25701
25686
  }
25702
- else
25703
- subscriptions.push(legacyReadReceiptSyncEngineOnLoginHandler());
25704
- const markerSyncUnsubscriber = await startMarkerSync();
25705
- subscriptions.push(markerSyncUnsubscriber);
25706
25687
  }
25707
- return true;
25708
25688
  };
25709
- /* end_public_function */
25710
-
25711
- /* begin_public_function
25712
- id: client.renew_access_token
25713
- */
25714
- /*
25715
- * Renewal defintion accepted by SessionHandler interface
25716
- *
25717
- * Tech Spec:
25718
- * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#Session-Handler
25719
- *
25720
- * @category private
25721
- */
25722
- const renewal = () => {
25723
- let tokenRenewed = false;
25724
- let renewTimeoutId;
25725
- const client = getActiveClient();
25726
- client.log('initiating access token renewal');
25727
- /*
25728
- * Renews a token if it is hasn't been renewed before. Also marks token as
25729
- * renewed once done
25730
- * Per instance of Renewal, only one renewal is allowed
25731
- */
25732
- const renewToken = async (authToken) => {
25733
- const { userId, displayName } = getActiveUser();
25734
- const deviceId = await getDeviceId();
25735
- const params = { userId, displayName, authToken, deviceId };
25736
- if (client.sessionState === "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */ && client.sessionHandler) {
25737
- await login(params, client.sessionHandler);
25738
- }
25739
- else {
25740
- // about to expire
25741
- await setClientToken({
25742
- params,
25743
- options: {
25744
- setAccessTokenCookie: true,
25745
- },
25746
- });
25747
- }
25748
- tokenRenewed = true;
25749
- if (renewTimeoutId)
25750
- clearTimeout(renewTimeoutId);
25751
- };
25752
- return {
25753
- renew: () => {
25754
- if (tokenRenewed) {
25755
- console.log("'renew' method can be called only once per renewal instance");
25756
- return;
25757
- }
25758
- renewToken();
25759
- },
25760
- renewWithAuthToken: (authToken) => {
25761
- if (tokenRenewed) {
25762
- console.log("'renewWithAuthToken' method can be called only once per renewal instance");
25763
- return;
25764
- }
25765
- renewToken(authToken);
25766
- },
25767
- unableToRetrieveAuthToken: () => {
25768
- renewTimeoutId = setTimeout(() => {
25769
- var _a;
25770
- (_a = client.sessionHandler) === null || _a === void 0 ? void 0 : _a.sessionWillRenewAccessToken(renewal());
25771
- }, ACCESS_TOKEN_WATCHER_INTERVAL);
25772
- },
25773
- };
25689
+ const unRegisterEventListeners = () => {
25690
+ disposers$1.forEach(fn => fn());
25691
+ disposers$1 = [];
25774
25692
  };
25775
- /* end_public_function */
25776
-
25777
- const ABOUT_TO_EXPIRE_THRESHOLD = 80 / 100;
25778
- const COMPENSATED_DELAY = 5 * MINUTE;
25779
- /*
25780
- * a helper function to check if the token has expires
25693
+ const startMarkerSync = async () => Promise.resolve();
25694
+ /**
25695
+ ```js
25696
+ * import { startUnreadSync } from '@amityco/ts-sdk-react-native'
25697
+ * startUnreadSync()
25698
+ * ```
25781
25699
  *
25782
- * @param token to be checked
25783
- * @returns boolean indicating if token expires
25700
+ * start syncing to keep feed markers, channel markers and user makers cache
25701
+ * update to the server.
25784
25702
  *
25785
- * @category private
25703
+ * @category Marker API
25786
25704
  */
25787
- const isExpired = (expiresAt) => Date.now() > Date.parse(expiresAt) - COMPENSATED_DELAY;
25788
- /*
25789
- * a helper function to check if the token is about to expire
25705
+ const startUnreadSync = async () => Promise.resolve();
25706
+ /**
25707
+ ```js
25708
+ * import { stopUnreadSync } from '@amityco/ts-sdk-react-native'
25709
+ * stopUnreadSync()
25710
+ * ```
25790
25711
  *
25791
- * @param token to be checked
25792
- * @returns boolean indicating if token is aboutToExpire
25712
+ * stop unread syncing
25793
25713
  *
25794
- * @category private
25714
+ * @category Marker API
25795
25715
  */
25796
- const isAboutToExpire = (params) => {
25797
- const { expiresAt, issuedAt } = params;
25798
- const expires = Date.parse(expiresAt);
25799
- const issued = Date.parse(issuedAt);
25800
- const now = Date.now();
25801
- const duration = expires - issued - COMPENSATED_DELAY;
25802
- const aboutToExpireAt = issued + duration * ABOUT_TO_EXPIRE_THRESHOLD;
25803
- return now > aboutToExpireAt && now < expires;
25716
+ const stopUnreadSync = () => {
25717
+ isSyncRunning = false;
25718
+ setMarkerSyncEvents([]);
25719
+ unRegisterEventListeners();
25804
25720
  };
25805
- /*
25806
- * Monitors time to expire of token and updates session state to aboutToExpire
25807
- *
25808
- * @returns intervalId to be cleared after trigger
25809
- *
25810
- * @category private
25811
- */
25812
- const accessTokenExpiryWatcher = (sessionHandler) => {
25813
- const interval = setInterval(() => {
25814
- const client = getActiveClient();
25815
- if (!client.token)
25816
- return;
25817
- const { issuedAt, expiresAt } = client.token;
25818
- if (isExpired(expiresAt)) {
25819
- /*
25820
- * the event handler will take care of updating session state
25821
- * Note, this will also clear the interval id, so this event will only be
25822
- * fired once
25823
- */
25824
- fireEvent('tokenExpired', "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */);
25825
- /*
25826
- * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#Automatically-initiate-renewal-flow
25827
- *
25828
- * Why sechduled task?
25829
- * Since fireEvent is scheduled, it will be called
25830
- * after sessionHandler leading to an invalid state change from
25831
- * establishing to tokenExpired
25832
- */
25833
- scheduleTask(() => sessionHandler.sessionWillRenewAccessToken(renewal()));
25834
- return;
25835
- }
25836
- if (isAboutToExpire({ expiresAt, issuedAt })) {
25837
- sessionHandler.sessionWillRenewAccessToken(renewal());
25838
- }
25839
- }, ACCESS_TOKEN_WATCHER_INTERVAL);
25840
- return () => clearInterval(interval);
25841
- };
25721
+ setIntervalTask(async () => {
25722
+ if (!isSyncRunning)
25723
+ return;
25724
+ await markerSyncTrigger();
25725
+ }, SYNC_TRIGGER_INTERVAL_TIME);
25726
+ const getMarkerSyncConsistentMode = () => isConsistentMode;
25842
25727
 
25843
25728
  /**
25844
25729
  * ```js
@@ -26006,6 +25891,10 @@ const isConnected = () => {
26006
25891
  isWsConnected);
26007
25892
  };
26008
25893
 
25894
+ const enableUnreadCount = () => {
25895
+ return true;
25896
+ };
25897
+
26009
25898
  var _GlobalFileAccessType_fileAccessType;
26010
25899
  class GlobalFileAccessType {
26011
25900
  constructor() {
@@ -29449,7 +29338,7 @@ const prepareCommentFromFlaggedEvent = (payload) => {
29449
29338
  * @async
29450
29339
  * */
29451
29340
  const addReaction = async (referenceType, referenceId, reactionName) => {
29452
- var _a, _b;
29341
+ var _a, _b, _c;
29453
29342
  const client = getActiveClient();
29454
29343
  client.log('reaction/createReaction', {
29455
29344
  referenceId,
@@ -29470,9 +29359,9 @@ const addReaction = async (referenceType, referenceId, reactionName) => {
29470
29359
  'get',
29471
29360
  referenceId,
29472
29361
  ]);
29473
- if (!model)
29362
+ if (!model || ((_a = model.data.myReactions) === null || _a === void 0 ? void 0 : _a.includes(reactionName)))
29474
29363
  return true;
29475
- const updatedModel = Object.assign(Object.assign({}, model.data), { reactionsCount: model.data.reactionsCount + 1, myReactions: [...((_a = model.data.myReactions) !== null && _a !== void 0 ? _a : []), reactionName], reactions: Object.assign(Object.assign({}, model.data.reactions), { [reactionName]: ((_b = model.data.reactions[reactionName]) !== null && _b !== void 0 ? _b : 0) + 1 }) });
29364
+ const updatedModel = Object.assign(Object.assign({}, model.data), { reactionsCount: model.data.reactionsCount + 1, myReactions: [...((_b = model.data.myReactions) !== null && _b !== void 0 ? _b : []), reactionName], reactions: Object.assign(Object.assign({}, model.data.reactions), { [reactionName]: ((_c = model.data.reactions[reactionName]) !== null && _c !== void 0 ? _c : 0) + 1 }) });
29476
29365
  if (referenceType === 'comment') {
29477
29366
  fireEvent('local.comment.addReaction', {
29478
29367
  comment: updatedModel,