@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.cjs.js CHANGED
@@ -135,8 +135,8 @@ const PostContentType = Object.freeze({
135
135
 
136
136
  function getVersion() {
137
137
  try {
138
- // the string ''v7.5.2-cjs'' should be replaced by actual value by @rollup/plugin-replace
139
- return 'v7.5.2-cjs';
138
+ // the string ''v7.5.3-cjs'' should be replaced by actual value by @rollup/plugin-replace
139
+ return 'v7.5.3-cjs';
140
140
  }
141
141
  catch (error) {
142
142
  return '__dev__';
@@ -8727,6 +8727,266 @@ const removeChannelMarkerCache = (channel) => {
8727
8727
  dropFromCache(['channelMarker', 'get', id], true);
8728
8728
  };
8729
8729
 
8730
+ /* eslint-disable no-param-reassign */
8731
+ /*
8732
+ * declared earlier to accomodate case when logging in with a different user
8733
+ * than the one already connected, in which case the existing subscriptions need
8734
+ * to be cleared
8735
+ */
8736
+ let subscriptions = [];
8737
+ async function runMqtt() {
8738
+ await modifyMqttConnection();
8739
+ }
8740
+ /* begin_public_function
8741
+ id: client.login
8742
+ */
8743
+ /**
8744
+ * ```js
8745
+ * import { login } from '@amityco/ts-sdk-react-native/client/api'
8746
+ * const success = await login({
8747
+ * userId: 'XYZ123456789',
8748
+ * })
8749
+ * ```
8750
+ *
8751
+ * Connects an {@link Amity.Client} instance to ASC servers
8752
+ *
8753
+ * @param params the connect parameters
8754
+ * @param params.userId the user ID for the current session
8755
+ * @param params.displayName the user's displayName for the current session
8756
+ * @param params.deviceId Manual override of the user's device id (for device management)
8757
+ * @param params.authToken The authentication token - necessary when network option is set to secure
8758
+ * @returns a success boolean if connected
8759
+ *
8760
+ * @category Client API
8761
+ * @async
8762
+ */
8763
+ const login = async (params, sessionHandler, config) => {
8764
+ var _a;
8765
+ const client = getActiveClient();
8766
+ let unsubWatcher;
8767
+ client.log('client/api/connectClient', Object.assign({ apiKey: client.apiKey, sessionState: client.sessionState }, params));
8768
+ // if connecting to a different userId than the one that is connected currently
8769
+ if (client.userId && client.userId !== params.userId) {
8770
+ await logout();
8771
+ // Remove subscription to ban and delete
8772
+ subscriptions.forEach(fn => fn());
8773
+ subscriptions = [];
8774
+ }
8775
+ // default values
8776
+ const defaultDeviceId = await getDeviceId();
8777
+ try {
8778
+ const { users } = await setClientToken({
8779
+ 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 }),
8780
+ options: {
8781
+ setAccessTokenCookie: true,
8782
+ },
8783
+ });
8784
+ const user = users.find(u => u.userId === params.userId);
8785
+ if (user == null) {
8786
+ throw new ASCError(`${params.userId} has not been founded`, 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "error" /* Amity.ErrorLevel.ERROR */);
8787
+ }
8788
+ if (user.isDeleted) {
8789
+ terminateClient("userDeleted" /* Amity.TokenTerminationReason.USER_DELETED */);
8790
+ return false;
8791
+ }
8792
+ if (user.isGlobalBanned) {
8793
+ terminateClient("globalBan" /* Amity.TokenTerminationReason.GLOBAL_BAN */);
8794
+ return false;
8795
+ }
8796
+ // FIXME: events are duplicated if connectClient is called few times without disconnectClient
8797
+ // wire websocket events to our event emitter
8798
+ proxyWebsocketEvents(client.ws, client.emitter);
8799
+ (_a = client.ws) === null || _a === void 0 ? void 0 : _a.open();
8800
+ client.userId = user.userId;
8801
+ client.sessionHandler = sessionHandler;
8802
+ /*
8803
+ * Cannot push to subscriptions as watcher needs to continue working even if
8804
+ * token expires
8805
+ */
8806
+ unsubWatcher = client.accessTokenExpiryWatcher(sessionHandler);
8807
+ setActiveUser(user);
8808
+ }
8809
+ catch (error) {
8810
+ /*
8811
+ * if getting token failed session state reverts to initial state when app
8812
+ * is first launched
8813
+ */
8814
+ SessionWatcher$1.getInstance().setSessionState("notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */);
8815
+ // pass error down tree so the calling function handle it
8816
+ throw error;
8817
+ }
8818
+ if ((config === null || config === void 0 ? void 0 : config.disableRTE) !== true) {
8819
+ runMqtt();
8820
+ }
8821
+ await initializeMessagePreviewSetting();
8822
+ if (subscriptions.length === 0) {
8823
+ subscriptions.push(
8824
+ // GLOBAL_BAN
8825
+ onClientBanned((_) => {
8826
+ terminateClient("globalBan" /* Amity.TokenTerminationReason.GLOBAL_BAN */);
8827
+ subscriptions.forEach(fn => fn());
8828
+ unsubWatcher();
8829
+ }), onTokenTerminated(_ => {
8830
+ terminateClient();
8831
+ subscriptions.forEach(fn => fn());
8832
+ unsubWatcher();
8833
+ }), onUserDeleted$2((user) => {
8834
+ if (user.userId === client.userId) {
8835
+ terminateClient("userDeleted" /* Amity.TokenTerminationReason.USER_DELETED */);
8836
+ subscriptions.forEach(fn => fn());
8837
+ unsubWatcher();
8838
+ }
8839
+ }), onTokenExpired(state => {
8840
+ SessionWatcher$1.getInstance().setSessionState(state);
8841
+ logout();
8842
+ subscriptions.forEach(fn => fn());
8843
+ }),
8844
+ // NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
8845
+ // the channel because currently backend can't handle this, so every time a user is banned from
8846
+ // a channel or the channel is deleted the channel's unread count will not be reset to zero
8847
+ onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler());
8848
+ if (client.useLegacyUnreadCount) {
8849
+ subscriptions.push(readReceiptSyncEngineOnLoginHandler());
8850
+ }
8851
+ else
8852
+ subscriptions.push(legacyReadReceiptSyncEngineOnLoginHandler());
8853
+ }
8854
+ return true;
8855
+ };
8856
+ /* end_public_function */
8857
+
8858
+ /* begin_public_function
8859
+ id: client.renew_access_token
8860
+ */
8861
+ /*
8862
+ * Renewal defintion accepted by SessionHandler interface
8863
+ *
8864
+ * Tech Spec:
8865
+ * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#Session-Handler
8866
+ *
8867
+ * @category private
8868
+ */
8869
+ const renewal = () => {
8870
+ let tokenRenewed = false;
8871
+ let renewTimeoutId;
8872
+ const client = getActiveClient();
8873
+ client.log('initiating access token renewal');
8874
+ /*
8875
+ * Renews a token if it is hasn't been renewed before. Also marks token as
8876
+ * renewed once done
8877
+ * Per instance of Renewal, only one renewal is allowed
8878
+ */
8879
+ const renewToken = async (authToken) => {
8880
+ const { userId, displayName } = getActiveUser();
8881
+ const deviceId = await getDeviceId();
8882
+ const params = { userId, displayName, authToken, deviceId };
8883
+ if (client.sessionState === "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */ && client.sessionHandler) {
8884
+ await login(params, client.sessionHandler);
8885
+ }
8886
+ else {
8887
+ // about to expire
8888
+ await setClientToken({
8889
+ params,
8890
+ options: {
8891
+ setAccessTokenCookie: true,
8892
+ },
8893
+ });
8894
+ }
8895
+ tokenRenewed = true;
8896
+ if (renewTimeoutId)
8897
+ clearTimeout(renewTimeoutId);
8898
+ };
8899
+ return {
8900
+ renew: () => {
8901
+ if (tokenRenewed) {
8902
+ console.log("'renew' method can be called only once per renewal instance");
8903
+ return;
8904
+ }
8905
+ renewToken();
8906
+ },
8907
+ renewWithAuthToken: (authToken) => {
8908
+ if (tokenRenewed) {
8909
+ console.log("'renewWithAuthToken' method can be called only once per renewal instance");
8910
+ return;
8911
+ }
8912
+ renewToken(authToken);
8913
+ },
8914
+ unableToRetrieveAuthToken: () => {
8915
+ renewTimeoutId = setTimeout(() => {
8916
+ var _a;
8917
+ (_a = client.sessionHandler) === null || _a === void 0 ? void 0 : _a.sessionWillRenewAccessToken(renewal());
8918
+ }, ACCESS_TOKEN_WATCHER_INTERVAL);
8919
+ },
8920
+ };
8921
+ };
8922
+ /* end_public_function */
8923
+
8924
+ const ABOUT_TO_EXPIRE_THRESHOLD = 80 / 100;
8925
+ const COMPENSATED_DELAY = 5 * MINUTE;
8926
+ /*
8927
+ * a helper function to check if the token has expires
8928
+ *
8929
+ * @param token to be checked
8930
+ * @returns boolean indicating if token expires
8931
+ *
8932
+ * @category private
8933
+ */
8934
+ const isExpired = (expiresAt) => Date.now() > Date.parse(expiresAt) - COMPENSATED_DELAY;
8935
+ /*
8936
+ * a helper function to check if the token is about to expire
8937
+ *
8938
+ * @param token to be checked
8939
+ * @returns boolean indicating if token is aboutToExpire
8940
+ *
8941
+ * @category private
8942
+ */
8943
+ const isAboutToExpire = (params) => {
8944
+ const { expiresAt, issuedAt } = params;
8945
+ const expires = Date.parse(expiresAt);
8946
+ const issued = Date.parse(issuedAt);
8947
+ const now = Date.now();
8948
+ const duration = expires - issued - COMPENSATED_DELAY;
8949
+ const aboutToExpireAt = issued + duration * ABOUT_TO_EXPIRE_THRESHOLD;
8950
+ return now > aboutToExpireAt && now < expires;
8951
+ };
8952
+ /*
8953
+ * Monitors time to expire of token and updates session state to aboutToExpire
8954
+ *
8955
+ * @returns intervalId to be cleared after trigger
8956
+ *
8957
+ * @category private
8958
+ */
8959
+ const accessTokenExpiryWatcher = (sessionHandler) => {
8960
+ const interval = setInterval(() => {
8961
+ const client = getActiveClient();
8962
+ if (!client.token)
8963
+ return;
8964
+ const { issuedAt, expiresAt } = client.token;
8965
+ if (isExpired(expiresAt)) {
8966
+ /*
8967
+ * the event handler will take care of updating session state
8968
+ * Note, this will also clear the interval id, so this event will only be
8969
+ * fired once
8970
+ */
8971
+ fireEvent('tokenExpired', "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */);
8972
+ /*
8973
+ * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#Automatically-initiate-renewal-flow
8974
+ *
8975
+ * Why sechduled task?
8976
+ * Since fireEvent is scheduled, it will be called
8977
+ * after sessionHandler leading to an invalid state change from
8978
+ * establishing to tokenExpired
8979
+ */
8980
+ scheduleTask(() => sessionHandler.sessionWillRenewAccessToken(renewal()));
8981
+ return;
8982
+ }
8983
+ if (isAboutToExpire({ expiresAt, issuedAt })) {
8984
+ sessionHandler.sessionWillRenewAccessToken(renewal());
8985
+ }
8986
+ }, ACCESS_TOKEN_WATCHER_INTERVAL);
8987
+ return () => clearInterval(interval);
8988
+ };
8989
+
8730
8990
  const callbacks$9 = [];
8731
8991
  let mainDisposer$8 = null;
8732
8992
  const dispose$9 = (cb) => {
@@ -8999,33 +9259,6 @@ const onChannelMemberRoleRemoved = (callback) => {
8999
9259
  return () => dispose(callback);
9000
9260
  };
9001
9261
 
9002
- /**
9003
- * ```js
9004
- * import { onUserMarkerSync } from '@amityco/ts-sdk-react-native'
9005
- * const dispose = onUserMarkerSync(UserMarker => {
9006
- * // ...
9007
- * })
9008
- * ```
9009
- *
9010
- * Fired when an {@link Amity.UserMarker} has been sync
9011
- *
9012
- * @param callback The function to call when the event was fired
9013
- * @returns an {@link Amity.Unsubscriber} function to stop listening
9014
- *
9015
- * @category UserMarker Events
9016
- */
9017
- const onUserMarkerSync = (callback) => {
9018
- const client = getActiveClient();
9019
- const filter = (payload) => {
9020
- const { userMarkers, userEntityMarkers: userEntityMarkersPayload, userFeedMarkers: userFeedMarkersPayload } = payload, rest = __rest(payload, ["userMarkers", "userEntityMarkers", "userFeedMarkers"]);
9021
- const userEntityMarkers = convertChannelMarkerResponse(userEntityMarkersPayload);
9022
- const userFeedMarker = convertSubChannelMarkerResponse(userFeedMarkersPayload);
9023
- ingestInCache(Object.assign({ userMarkers, userEntityMarkers, userFeedMarker }, rest));
9024
- callback(userMarkers[0]);
9025
- };
9026
- return createEventSubscriber(client, 'UserMarker/onUserMarkerSync', 'marker.user-sync', filter);
9027
- };
9028
-
9029
9262
  /**
9030
9263
  * ```js
9031
9264
  * import { onFeedMarkerUpdated } from '@amityco/ts-sdk-react-native'
@@ -9297,52 +9530,11 @@ const markerSync = async (deviceLastSyncAt) => {
9297
9530
  };
9298
9531
  };
9299
9532
 
9300
- const enableUnreadCount = () => {
9301
- const client = getActiveClient();
9302
- client.log('client/api/isUnreadCountEnabled', client.isUnreadCountEnabled);
9303
- if (!client) {
9304
- throw new ASCError('There is no active client', 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "fatal" /* Amity.ErrorLevel.FATAL */);
9305
- }
9306
- if (client.isUnreadCountEnabled)
9307
- return false;
9308
- client.isUnreadCountEnabled = true;
9309
- client.useLegacyUnreadCount = false;
9310
- client.emitter.emit('unreadCountEnabled', true);
9311
- return true;
9312
- };
9313
-
9314
- /**
9315
- * ```js
9316
- * import { onFeedMarkerUpdated } from '@amityco/ts-sdk-react-native'
9317
- * const dispose = onFeedMarkerUpdated(feedMarker => {
9318
- * // ...
9319
- * })
9320
- * ```
9321
- *
9322
- * Fired when an {@link Amity.UserFeedMarker} has been updated
9323
- *
9324
- * @param callback The function to call when the event was fired
9325
- * @returns an {@link Amity.Unsubscriber} function to stop listening
9326
- *
9327
- * @category FeedMarker Events
9328
- */
9329
- const onUserFeedMarkerUpdated = (callback) => {
9330
- const client = getActiveClient();
9331
- const filter = (payload) => {
9332
- persistUnreadCountInfo(payload);
9333
- payload.feedMarkers.forEach(feedMarker => {
9334
- callback(feedMarker);
9335
- });
9336
- };
9337
- return createEventSubscriber(client, 'feedMarker/onUserFeedMarkerUpdated', 'marker.userFeed-updated', filter);
9338
- };
9339
-
9340
9533
  const SYNC_TRIGGER_INTERVAL_TIME = 2000;
9341
- const ON_SUB_CHANNEL_DELETE_SYNC_TRIGGER_DELAY = 2000;
9342
9534
  let isSyncRunning = false;
9343
9535
  let disposers$1 = [];
9344
9536
  let isWaitingForResponse = false;
9345
- let isConsistentMode = true;
9537
+ const isConsistentMode = true;
9346
9538
  let deviceLastSyncAt = null;
9347
9539
  const getDeviceLastSyncAt = () => {
9348
9540
  if (deviceLastSyncAt == null) {
@@ -9357,12 +9549,6 @@ const saveDeviceLastSyncAt = (lastSyncAt) => {
9357
9549
  deviceLastSyncAt = lastSyncAt;
9358
9550
  }
9359
9551
  };
9360
- const fetchDeviceLastSyncAt = async () => {
9361
- const { data: userMarker } = await getUserMarker();
9362
- if (userMarker == null)
9363
- return;
9364
- saveDeviceLastSyncAt(new Date(userMarker.lastSyncAt));
9365
- };
9366
9552
  /**
9367
9553
  * list of conditions that make timer still trigger the syncing process.
9368
9554
  * If it's empty, it means sync is stopped.
@@ -9397,375 +9583,74 @@ const markerSyncTrigger = async () => {
9397
9583
  }
9398
9584
  if (events.length === 0) {
9399
9585
  // no event that require to call marker sync API
9400
- return;
9401
- }
9402
- try {
9403
- isWaitingForResponse = true;
9404
- // any past events are considered processed here.
9405
- // however during waiting for the response, RTE could add the new message event;
9406
- // which will make the engine trigger another call next round.
9407
- events = [];
9408
- const response = await markerSync(getDeviceLastSyncAt().toISOString());
9409
- const latestLastSyncAt = response.data.userMarkers.reduce((maxLastSyncAt, userMarker) => {
9410
- if (maxLastSyncAt == null ||
9411
- maxLastSyncAt.getTime() < new Date(userMarker.lastSyncAt).getTime()) {
9412
- return new Date(userMarker.lastSyncAt);
9413
- }
9414
- return maxLastSyncAt;
9415
- }, null);
9416
- saveDeviceLastSyncAt(latestLastSyncAt);
9417
- if (response.hasMore) {
9418
- events.push("has_more" /* Amity.MarkerSyncEvent.HAS_MORE */);
9419
- }
9420
- }
9421
- catch (_a) {
9422
- // prevent sync from stopping
9423
- }
9424
- finally {
9425
- if (isWaitingForResponse) {
9426
- isWaitingForResponse = false;
9427
- }
9428
- }
9429
- };
9430
- const registerEventListeners = () => {
9431
- if (disposers$1.length > 0) {
9432
- return;
9433
- }
9434
- // based on the tech spec design, we designed a fetch marker in case of these events
9435
- // - new message
9436
- // - create channel
9437
- // - remove channel
9438
- // - app going to online again after offline
9439
- disposers$1.push(onOnline(() => {
9440
- // should add RESUME to the event to trigger marker syncing again
9441
- events.push("resume" /* Amity.MarkerSyncEvent.RESUME */);
9442
- }), onMessageCreatedMqtt(message => {
9443
- // only conversation, community and broadcast types can sync
9444
- const client = getActiveClient();
9445
- if (isUnreadCountSupport$1(message) && message.creatorId !== client.userId)
9446
- events.push("new message" /* Amity.MarkerSyncEvent.NEW_MESSAGE */);
9447
- }), 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(() =>
9448
- /*
9449
- workaround: when receiving the event for sub-channel deletion,
9450
- before triggering marker update, the SDK will have to add a 2-second delay.
9451
- so that the unread count is calculated correctly.
9452
- */
9453
- 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 */)));
9454
- };
9455
- const unRegisterEventListeners = () => {
9456
- disposers$1.forEach(fn => fn());
9457
- disposers$1 = [];
9458
- };
9459
- const startMarkerSync = async () => {
9460
- await fetchDeviceLastSyncAt();
9461
- pushMarkerSyncEvent("start syncing" /* Amity.MarkerSyncEvent.START_SYNCING */);
9462
- isConsistentMode = true;
9463
- isSyncRunning = true;
9464
- registerEventListeners();
9465
- return unRegisterEventListeners;
9466
- };
9467
- /**
9468
- ```js
9469
- * import { startUnreadSync } from '@amityco/ts-sdk-react-native'
9470
- * startUnreadSync()
9471
- * ```
9472
- *
9473
- * start syncing to keep feed markers, channel markers and user makers cache
9474
- * update to the server.
9475
- *
9476
- * @category Marker API
9477
- */
9478
- const startUnreadSync = async () => {
9479
- await fetchDeviceLastSyncAt();
9480
- pushMarkerSyncEvent("start syncing" /* Amity.MarkerSyncEvent.START_SYNCING */);
9481
- enableUnreadCount();
9482
- isConsistentMode = false;
9483
- isSyncRunning = true;
9484
- registerEventListeners();
9485
- };
9486
- /**
9487
- ```js
9488
- * import { stopUnreadSync } from '@amityco/ts-sdk-react-native'
9489
- * stopUnreadSync()
9490
- * ```
9491
- *
9492
- * stop unread syncing
9493
- *
9494
- * @category Marker API
9495
- */
9496
- const stopUnreadSync = () => {
9497
- isSyncRunning = false;
9498
- setMarkerSyncEvents([]);
9499
- unRegisterEventListeners();
9500
- };
9501
- setIntervalTask(async () => {
9502
- if (!isSyncRunning)
9503
- return;
9504
- await markerSyncTrigger();
9505
- }, SYNC_TRIGGER_INTERVAL_TIME);
9506
- const getMarkerSyncConsistentMode = () => isConsistentMode;
9507
-
9508
- /* eslint-disable no-param-reassign */
9509
- /*
9510
- * declared earlier to accomodate case when logging in with a different user
9511
- * than the one already connected, in which case the existing subscriptions need
9512
- * to be cleared
9513
- */
9514
- let subscriptions = [];
9515
- async function runMqtt() {
9516
- await modifyMqttConnection();
9517
- }
9518
- /* begin_public_function
9519
- id: client.login
9520
- */
9521
- /**
9522
- * ```js
9523
- * import { login } from '@amityco/ts-sdk-react-native/client/api'
9524
- * const success = await login({
9525
- * userId: 'XYZ123456789',
9526
- * })
9527
- * ```
9528
- *
9529
- * Connects an {@link Amity.Client} instance to ASC servers
9530
- *
9531
- * @param params the connect parameters
9532
- * @param params.userId the user ID for the current session
9533
- * @param params.displayName the user's displayName for the current session
9534
- * @param params.deviceId Manual override of the user's device id (for device management)
9535
- * @param params.authToken The authentication token - necessary when network option is set to secure
9536
- * @returns a success boolean if connected
9537
- *
9538
- * @category Client API
9539
- * @async
9540
- */
9541
- const login = async (params, sessionHandler, config) => {
9542
- var _a;
9543
- const client = getActiveClient();
9544
- let unsubWatcher;
9545
- client.log('client/api/connectClient', Object.assign({ apiKey: client.apiKey, sessionState: client.sessionState }, params));
9546
- // if connecting to a different userId than the one that is connected currently
9547
- if (client.userId && client.userId !== params.userId) {
9548
- await logout();
9549
- // Remove subscription to ban and delete
9550
- subscriptions.forEach(fn => fn());
9551
- subscriptions = [];
9586
+ return;
9552
9587
  }
9553
- // default values
9554
- const defaultDeviceId = await getDeviceId();
9555
9588
  try {
9556
- const { users } = await setClientToken({
9557
- 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 }),
9558
- options: {
9559
- setAccessTokenCookie: true,
9560
- },
9561
- });
9562
- const user = users.find(u => u.userId === params.userId);
9563
- if (user == null) {
9564
- throw new ASCError(`${params.userId} has not been founded`, 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "error" /* Amity.ErrorLevel.ERROR */);
9565
- }
9566
- if (user.isDeleted) {
9567
- terminateClient("userDeleted" /* Amity.TokenTerminationReason.USER_DELETED */);
9568
- return false;
9569
- }
9570
- if (user.isGlobalBanned) {
9571
- terminateClient("globalBan" /* Amity.TokenTerminationReason.GLOBAL_BAN */);
9572
- return false;
9589
+ isWaitingForResponse = true;
9590
+ // any past events are considered processed here.
9591
+ // however during waiting for the response, RTE could add the new message event;
9592
+ // which will make the engine trigger another call next round.
9593
+ events = [];
9594
+ const response = await markerSync(getDeviceLastSyncAt().toISOString());
9595
+ const latestLastSyncAt = response.data.userMarkers.reduce((maxLastSyncAt, userMarker) => {
9596
+ if (maxLastSyncAt == null ||
9597
+ maxLastSyncAt.getTime() < new Date(userMarker.lastSyncAt).getTime()) {
9598
+ return new Date(userMarker.lastSyncAt);
9599
+ }
9600
+ return maxLastSyncAt;
9601
+ }, null);
9602
+ saveDeviceLastSyncAt(latestLastSyncAt);
9603
+ if (response.hasMore) {
9604
+ events.push("has_more" /* Amity.MarkerSyncEvent.HAS_MORE */);
9573
9605
  }
9574
- // FIXME: events are duplicated if connectClient is called few times without disconnectClient
9575
- // wire websocket events to our event emitter
9576
- proxyWebsocketEvents(client.ws, client.emitter);
9577
- (_a = client.ws) === null || _a === void 0 ? void 0 : _a.open();
9578
- client.userId = user.userId;
9579
- client.sessionHandler = sessionHandler;
9580
- /*
9581
- * Cannot push to subscriptions as watcher needs to continue working even if
9582
- * token expires
9583
- */
9584
- unsubWatcher = client.accessTokenExpiryWatcher(sessionHandler);
9585
- setActiveUser(user);
9586
9606
  }
9587
- catch (error) {
9588
- /*
9589
- * if getting token failed session state reverts to initial state when app
9590
- * is first launched
9591
- */
9592
- SessionWatcher$1.getInstance().setSessionState("notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */);
9593
- // pass error down tree so the calling function handle it
9594
- throw error;
9595
- }
9596
- if ((config === null || config === void 0 ? void 0 : config.disableRTE) !== true) {
9597
- runMqtt();
9607
+ catch (_a) {
9608
+ // prevent sync from stopping
9598
9609
  }
9599
- await initializeMessagePreviewSetting();
9600
- if (subscriptions.length === 0) {
9601
- subscriptions.push(
9602
- // GLOBAL_BAN
9603
- onClientBanned((_) => {
9604
- terminateClient("globalBan" /* Amity.TokenTerminationReason.GLOBAL_BAN */);
9605
- subscriptions.forEach(fn => fn());
9606
- unsubWatcher();
9607
- }), onTokenTerminated(_ => {
9608
- terminateClient();
9609
- subscriptions.forEach(fn => fn());
9610
- unsubWatcher();
9611
- }), onUserDeleted$2((user) => {
9612
- if (user.userId === client.userId) {
9613
- terminateClient("userDeleted" /* Amity.TokenTerminationReason.USER_DELETED */);
9614
- subscriptions.forEach(fn => fn());
9615
- unsubWatcher();
9616
- }
9617
- }), onTokenExpired(state => {
9618
- SessionWatcher$1.getInstance().setSessionState(state);
9619
- logout();
9620
- subscriptions.forEach(fn => fn());
9621
- }),
9622
- // NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
9623
- // the channel because currently backend can't handle this, so every time a user is banned from
9624
- // a channel or the channel is deleted the channel's unread count will not be reset to zero
9625
- onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler());
9626
- if (client.useLegacyUnreadCount) {
9627
- subscriptions.push(readReceiptSyncEngineOnLoginHandler());
9610
+ finally {
9611
+ if (isWaitingForResponse) {
9612
+ isWaitingForResponse = false;
9628
9613
  }
9629
- else
9630
- subscriptions.push(legacyReadReceiptSyncEngineOnLoginHandler());
9631
- const markerSyncUnsubscriber = await startMarkerSync();
9632
- subscriptions.push(markerSyncUnsubscriber);
9633
9614
  }
9634
- return true;
9635
9615
  };
9636
- /* end_public_function */
9637
-
9638
- /* begin_public_function
9639
- id: client.renew_access_token
9640
- */
9641
- /*
9642
- * Renewal defintion accepted by SessionHandler interface
9643
- *
9644
- * Tech Spec:
9645
- * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#Session-Handler
9646
- *
9647
- * @category private
9648
- */
9649
- const renewal = () => {
9650
- let tokenRenewed = false;
9651
- let renewTimeoutId;
9652
- const client = getActiveClient();
9653
- client.log('initiating access token renewal');
9654
- /*
9655
- * Renews a token if it is hasn't been renewed before. Also marks token as
9656
- * renewed once done
9657
- * Per instance of Renewal, only one renewal is allowed
9658
- */
9659
- const renewToken = async (authToken) => {
9660
- const { userId, displayName } = getActiveUser();
9661
- const deviceId = await getDeviceId();
9662
- const params = { userId, displayName, authToken, deviceId };
9663
- if (client.sessionState === "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */ && client.sessionHandler) {
9664
- await login(params, client.sessionHandler);
9665
- }
9666
- else {
9667
- // about to expire
9668
- await setClientToken({
9669
- params,
9670
- options: {
9671
- setAccessTokenCookie: true,
9672
- },
9673
- });
9674
- }
9675
- tokenRenewed = true;
9676
- if (renewTimeoutId)
9677
- clearTimeout(renewTimeoutId);
9678
- };
9679
- return {
9680
- renew: () => {
9681
- if (tokenRenewed) {
9682
- console.log("'renew' method can be called only once per renewal instance");
9683
- return;
9684
- }
9685
- renewToken();
9686
- },
9687
- renewWithAuthToken: (authToken) => {
9688
- if (tokenRenewed) {
9689
- console.log("'renewWithAuthToken' method can be called only once per renewal instance");
9690
- return;
9691
- }
9692
- renewToken(authToken);
9693
- },
9694
- unableToRetrieveAuthToken: () => {
9695
- renewTimeoutId = setTimeout(() => {
9696
- var _a;
9697
- (_a = client.sessionHandler) === null || _a === void 0 ? void 0 : _a.sessionWillRenewAccessToken(renewal());
9698
- }, ACCESS_TOKEN_WATCHER_INTERVAL);
9699
- },
9700
- };
9616
+ const unRegisterEventListeners = () => {
9617
+ disposers$1.forEach(fn => fn());
9618
+ disposers$1 = [];
9701
9619
  };
9702
- /* end_public_function */
9703
-
9704
- const ABOUT_TO_EXPIRE_THRESHOLD = 80 / 100;
9705
- const COMPENSATED_DELAY = 5 * MINUTE;
9706
- /*
9707
- * a helper function to check if the token has expires
9620
+ const startMarkerSync = async () => Promise.resolve();
9621
+ /**
9622
+ ```js
9623
+ * import { startUnreadSync } from '@amityco/ts-sdk-react-native'
9624
+ * startUnreadSync()
9625
+ * ```
9708
9626
  *
9709
- * @param token to be checked
9710
- * @returns boolean indicating if token expires
9627
+ * start syncing to keep feed markers, channel markers and user makers cache
9628
+ * update to the server.
9711
9629
  *
9712
- * @category private
9630
+ * @category Marker API
9713
9631
  */
9714
- const isExpired = (expiresAt) => Date.now() > Date.parse(expiresAt) - COMPENSATED_DELAY;
9715
- /*
9716
- * a helper function to check if the token is about to expire
9632
+ const startUnreadSync = async () => Promise.resolve();
9633
+ /**
9634
+ ```js
9635
+ * import { stopUnreadSync } from '@amityco/ts-sdk-react-native'
9636
+ * stopUnreadSync()
9637
+ * ```
9717
9638
  *
9718
- * @param token to be checked
9719
- * @returns boolean indicating if token is aboutToExpire
9639
+ * stop unread syncing
9720
9640
  *
9721
- * @category private
9641
+ * @category Marker API
9722
9642
  */
9723
- const isAboutToExpire = (params) => {
9724
- const { expiresAt, issuedAt } = params;
9725
- const expires = Date.parse(expiresAt);
9726
- const issued = Date.parse(issuedAt);
9727
- const now = Date.now();
9728
- const duration = expires - issued - COMPENSATED_DELAY;
9729
- const aboutToExpireAt = issued + duration * ABOUT_TO_EXPIRE_THRESHOLD;
9730
- return now > aboutToExpireAt && now < expires;
9643
+ const stopUnreadSync = () => {
9644
+ isSyncRunning = false;
9645
+ setMarkerSyncEvents([]);
9646
+ unRegisterEventListeners();
9731
9647
  };
9732
- /*
9733
- * Monitors time to expire of token and updates session state to aboutToExpire
9734
- *
9735
- * @returns intervalId to be cleared after trigger
9736
- *
9737
- * @category private
9738
- */
9739
- const accessTokenExpiryWatcher = (sessionHandler) => {
9740
- const interval = setInterval(() => {
9741
- const client = getActiveClient();
9742
- if (!client.token)
9743
- return;
9744
- const { issuedAt, expiresAt } = client.token;
9745
- if (isExpired(expiresAt)) {
9746
- /*
9747
- * the event handler will take care of updating session state
9748
- * Note, this will also clear the interval id, so this event will only be
9749
- * fired once
9750
- */
9751
- fireEvent('tokenExpired', "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */);
9752
- /*
9753
- * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#Automatically-initiate-renewal-flow
9754
- *
9755
- * Why sechduled task?
9756
- * Since fireEvent is scheduled, it will be called
9757
- * after sessionHandler leading to an invalid state change from
9758
- * establishing to tokenExpired
9759
- */
9760
- scheduleTask(() => sessionHandler.sessionWillRenewAccessToken(renewal()));
9761
- return;
9762
- }
9763
- if (isAboutToExpire({ expiresAt, issuedAt })) {
9764
- sessionHandler.sessionWillRenewAccessToken(renewal());
9765
- }
9766
- }, ACCESS_TOKEN_WATCHER_INTERVAL);
9767
- return () => clearInterval(interval);
9768
- };
9648
+ setIntervalTask(async () => {
9649
+ if (!isSyncRunning)
9650
+ return;
9651
+ await markerSyncTrigger();
9652
+ }, SYNC_TRIGGER_INTERVAL_TIME);
9653
+ const getMarkerSyncConsistentMode = () => isConsistentMode;
9769
9654
 
9770
9655
  /**
9771
9656
  * ```js
@@ -9933,6 +9818,10 @@ const isConnected = () => {
9933
9818
  isWsConnected);
9934
9819
  };
9935
9820
 
9821
+ const enableUnreadCount = () => {
9822
+ return true;
9823
+ };
9824
+
9936
9825
  var _GlobalFileAccessType_fileAccessType;
9937
9826
  class GlobalFileAccessType {
9938
9827
  constructor() {
@@ -13376,7 +13265,7 @@ const prepareCommentFromFlaggedEvent = (payload) => {
13376
13265
  * @async
13377
13266
  * */
13378
13267
  const addReaction = async (referenceType, referenceId, reactionName) => {
13379
- var _a, _b;
13268
+ var _a, _b, _c;
13380
13269
  const client = getActiveClient();
13381
13270
  client.log('reaction/createReaction', {
13382
13271
  referenceId,
@@ -13397,9 +13286,9 @@ const addReaction = async (referenceType, referenceId, reactionName) => {
13397
13286
  'get',
13398
13287
  referenceId,
13399
13288
  ]);
13400
- if (!model)
13289
+ if (!model || ((_a = model.data.myReactions) === null || _a === void 0 ? void 0 : _a.includes(reactionName)))
13401
13290
  return true;
13402
- 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 }) });
13291
+ 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 }) });
13403
13292
  if (referenceType === 'comment') {
13404
13293
  fireEvent('local.comment.addReaction', {
13405
13294
  comment: updatedModel,