@amityco/ts-sdk 7.5.3-d52ed38.0 → 7.5.3-d6ee46d.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -25987,266 +25987,6 @@ const removeChannelMarkerCache = (channel) => {
25987
25987
  dropFromCache(['channelMarker', 'get', id], true);
25988
25988
  };
25989
25989
 
25990
- /* eslint-disable no-param-reassign */
25991
- /*
25992
- * declared earlier to accomodate case when logging in with a different user
25993
- * than the one already connected, in which case the existing subscriptions need
25994
- * to be cleared
25995
- */
25996
- let subscriptions = [];
25997
- async function runMqtt() {
25998
- await modifyMqttConnection();
25999
- }
26000
- /* begin_public_function
26001
- id: client.login
26002
- */
26003
- /**
26004
- * ```js
26005
- * import { login } from '@amityco/ts-sdk/client/api'
26006
- * const success = await login({
26007
- * userId: 'XYZ123456789',
26008
- * })
26009
- * ```
26010
- *
26011
- * Connects an {@link Amity.Client} instance to ASC servers
26012
- *
26013
- * @param params the connect parameters
26014
- * @param params.userId the user ID for the current session
26015
- * @param params.displayName the user's displayName for the current session
26016
- * @param params.deviceId Manual override of the user's device id (for device management)
26017
- * @param params.authToken The authentication token - necessary when network option is set to secure
26018
- * @returns a success boolean if connected
26019
- *
26020
- * @category Client API
26021
- * @async
26022
- */
26023
- const login = async (params, sessionHandler, config) => {
26024
- var _a;
26025
- const client = getActiveClient();
26026
- let unsubWatcher;
26027
- client.log('client/api/connectClient', Object.assign({ apiKey: client.apiKey, sessionState: client.sessionState }, params));
26028
- // if connecting to a different userId than the one that is connected currently
26029
- if (client.userId && client.userId !== params.userId) {
26030
- await logout();
26031
- // Remove subscription to ban and delete
26032
- subscriptions.forEach(fn => fn());
26033
- subscriptions = [];
26034
- }
26035
- // default values
26036
- const defaultDeviceId = await getDeviceId();
26037
- try {
26038
- const { users } = await setClientToken({
26039
- 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 }),
26040
- options: {
26041
- setAccessTokenCookie: true,
26042
- },
26043
- });
26044
- const user = users.find(u => u.userId === params.userId);
26045
- if (user == null) {
26046
- throw new ASCError(`${params.userId} has not been founded`, 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "error" /* Amity.ErrorLevel.ERROR */);
26047
- }
26048
- if (user.isDeleted) {
26049
- terminateClient("userDeleted" /* Amity.TokenTerminationReason.USER_DELETED */);
26050
- return false;
26051
- }
26052
- if (user.isGlobalBanned) {
26053
- terminateClient("globalBan" /* Amity.TokenTerminationReason.GLOBAL_BAN */);
26054
- return false;
26055
- }
26056
- // FIXME: events are duplicated if connectClient is called few times without disconnectClient
26057
- // wire websocket events to our event emitter
26058
- proxyWebsocketEvents(client.ws, client.emitter);
26059
- (_a = client.ws) === null || _a === void 0 ? void 0 : _a.open();
26060
- client.userId = user.userId;
26061
- client.sessionHandler = sessionHandler;
26062
- /*
26063
- * Cannot push to subscriptions as watcher needs to continue working even if
26064
- * token expires
26065
- */
26066
- unsubWatcher = client.accessTokenExpiryWatcher(sessionHandler);
26067
- setActiveUser(user);
26068
- }
26069
- catch (error) {
26070
- /*
26071
- * if getting token failed session state reverts to initial state when app
26072
- * is first launched
26073
- */
26074
- SessionWatcher$1.getInstance().setSessionState("notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */);
26075
- // pass error down tree so the calling function handle it
26076
- throw error;
26077
- }
26078
- if ((config === null || config === void 0 ? void 0 : config.disableRTE) !== true) {
26079
- runMqtt();
26080
- }
26081
- await initializeMessagePreviewSetting();
26082
- if (subscriptions.length === 0) {
26083
- subscriptions.push(
26084
- // GLOBAL_BAN
26085
- onClientBanned((_) => {
26086
- terminateClient("globalBan" /* Amity.TokenTerminationReason.GLOBAL_BAN */);
26087
- subscriptions.forEach(fn => fn());
26088
- unsubWatcher();
26089
- }), onTokenTerminated(_ => {
26090
- terminateClient();
26091
- subscriptions.forEach(fn => fn());
26092
- unsubWatcher();
26093
- }), onUserDeleted$2((user) => {
26094
- if (user.userId === client.userId) {
26095
- terminateClient("userDeleted" /* Amity.TokenTerminationReason.USER_DELETED */);
26096
- subscriptions.forEach(fn => fn());
26097
- unsubWatcher();
26098
- }
26099
- }), onTokenExpired(state => {
26100
- SessionWatcher$1.getInstance().setSessionState(state);
26101
- logout();
26102
- subscriptions.forEach(fn => fn());
26103
- }),
26104
- // NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
26105
- // the channel because currently backend can't handle this, so every time a user is banned from
26106
- // a channel or the channel is deleted the channel's unread count will not be reset to zero
26107
- onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler());
26108
- if (client.useLegacyUnreadCount) {
26109
- subscriptions.push(readReceiptSyncEngineOnLoginHandler());
26110
- }
26111
- else
26112
- subscriptions.push(legacyReadReceiptSyncEngineOnLoginHandler());
26113
- }
26114
- return true;
26115
- };
26116
- /* end_public_function */
26117
-
26118
- /* begin_public_function
26119
- id: client.renew_access_token
26120
- */
26121
- /*
26122
- * Renewal defintion accepted by SessionHandler interface
26123
- *
26124
- * Tech Spec:
26125
- * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#Session-Handler
26126
- *
26127
- * @category private
26128
- */
26129
- const renewal = () => {
26130
- let tokenRenewed = false;
26131
- let renewTimeoutId;
26132
- const client = getActiveClient();
26133
- client.log('initiating access token renewal');
26134
- /*
26135
- * Renews a token if it is hasn't been renewed before. Also marks token as
26136
- * renewed once done
26137
- * Per instance of Renewal, only one renewal is allowed
26138
- */
26139
- const renewToken = async (authToken) => {
26140
- const { userId, displayName } = getActiveUser();
26141
- const deviceId = await getDeviceId();
26142
- const params = { userId, displayName, authToken, deviceId };
26143
- if (client.sessionState === "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */ && client.sessionHandler) {
26144
- await login(params, client.sessionHandler);
26145
- }
26146
- else {
26147
- // about to expire
26148
- await setClientToken({
26149
- params,
26150
- options: {
26151
- setAccessTokenCookie: true,
26152
- },
26153
- });
26154
- }
26155
- tokenRenewed = true;
26156
- if (renewTimeoutId)
26157
- clearTimeout(renewTimeoutId);
26158
- };
26159
- return {
26160
- renew: () => {
26161
- if (tokenRenewed) {
26162
- console.log("'renew' method can be called only once per renewal instance");
26163
- return;
26164
- }
26165
- renewToken();
26166
- },
26167
- renewWithAuthToken: (authToken) => {
26168
- if (tokenRenewed) {
26169
- console.log("'renewWithAuthToken' method can be called only once per renewal instance");
26170
- return;
26171
- }
26172
- renewToken(authToken);
26173
- },
26174
- unableToRetrieveAuthToken: () => {
26175
- renewTimeoutId = setTimeout(() => {
26176
- var _a;
26177
- (_a = client.sessionHandler) === null || _a === void 0 ? void 0 : _a.sessionWillRenewAccessToken(renewal());
26178
- }, ACCESS_TOKEN_WATCHER_INTERVAL);
26179
- },
26180
- };
26181
- };
26182
- /* end_public_function */
26183
-
26184
- const ABOUT_TO_EXPIRE_THRESHOLD = 80 / 100;
26185
- const COMPENSATED_DELAY = 5 * MINUTE;
26186
- /*
26187
- * a helper function to check if the token has expires
26188
- *
26189
- * @param token to be checked
26190
- * @returns boolean indicating if token expires
26191
- *
26192
- * @category private
26193
- */
26194
- const isExpired = (expiresAt) => Date.now() > Date.parse(expiresAt) - COMPENSATED_DELAY;
26195
- /*
26196
- * a helper function to check if the token is about to expire
26197
- *
26198
- * @param token to be checked
26199
- * @returns boolean indicating if token is aboutToExpire
26200
- *
26201
- * @category private
26202
- */
26203
- const isAboutToExpire = (params) => {
26204
- const { expiresAt, issuedAt } = params;
26205
- const expires = Date.parse(expiresAt);
26206
- const issued = Date.parse(issuedAt);
26207
- const now = Date.now();
26208
- const duration = expires - issued - COMPENSATED_DELAY;
26209
- const aboutToExpireAt = issued + duration * ABOUT_TO_EXPIRE_THRESHOLD;
26210
- return now > aboutToExpireAt && now < expires;
26211
- };
26212
- /*
26213
- * Monitors time to expire of token and updates session state to aboutToExpire
26214
- *
26215
- * @returns intervalId to be cleared after trigger
26216
- *
26217
- * @category private
26218
- */
26219
- const accessTokenExpiryWatcher = (sessionHandler) => {
26220
- const interval = setInterval(() => {
26221
- const client = getActiveClient();
26222
- if (!client.token)
26223
- return;
26224
- const { issuedAt, expiresAt } = client.token;
26225
- if (isExpired(expiresAt)) {
26226
- /*
26227
- * the event handler will take care of updating session state
26228
- * Note, this will also clear the interval id, so this event will only be
26229
- * fired once
26230
- */
26231
- fireEvent('tokenExpired', "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */);
26232
- /*
26233
- * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#Automatically-initiate-renewal-flow
26234
- *
26235
- * Why sechduled task?
26236
- * Since fireEvent is scheduled, it will be called
26237
- * after sessionHandler leading to an invalid state change from
26238
- * establishing to tokenExpired
26239
- */
26240
- scheduleTask(() => sessionHandler.sessionWillRenewAccessToken(renewal()));
26241
- return;
26242
- }
26243
- if (isAboutToExpire({ expiresAt, issuedAt })) {
26244
- sessionHandler.sessionWillRenewAccessToken(renewal());
26245
- }
26246
- }, ACCESS_TOKEN_WATCHER_INTERVAL);
26247
- return () => clearInterval(interval);
26248
- };
26249
-
26250
25990
  const callbacks$9 = [];
26251
25991
  let mainDisposer$8 = null;
26252
25992
  const dispose$9 = (cb) => {
@@ -26519,6 +26259,33 @@ const onChannelMemberRoleRemoved = (callback) => {
26519
26259
  return () => dispose(callback);
26520
26260
  };
26521
26261
 
26262
+ /**
26263
+ * ```js
26264
+ * import { onUserMarkerSync } from '@amityco/ts-sdk'
26265
+ * const dispose = onUserMarkerSync(UserMarker => {
26266
+ * // ...
26267
+ * })
26268
+ * ```
26269
+ *
26270
+ * Fired when an {@link Amity.UserMarker} has been sync
26271
+ *
26272
+ * @param callback The function to call when the event was fired
26273
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
26274
+ *
26275
+ * @category UserMarker Events
26276
+ */
26277
+ const onUserMarkerSync = (callback) => {
26278
+ const client = getActiveClient();
26279
+ const filter = (payload) => {
26280
+ const { userMarkers, userEntityMarkers: userEntityMarkersPayload, userFeedMarkers: userFeedMarkersPayload } = payload, rest = __rest(payload, ["userMarkers", "userEntityMarkers", "userFeedMarkers"]);
26281
+ const userEntityMarkers = convertChannelMarkerResponse(userEntityMarkersPayload);
26282
+ const userFeedMarker = convertSubChannelMarkerResponse(userFeedMarkersPayload);
26283
+ ingestInCache(Object.assign({ userMarkers, userEntityMarkers, userFeedMarker }, rest));
26284
+ callback(userMarkers[0]);
26285
+ };
26286
+ return createEventSubscriber(client, 'UserMarker/onUserMarkerSync', 'marker.user-sync', filter);
26287
+ };
26288
+
26522
26289
  /**
26523
26290
  * ```js
26524
26291
  * import { onFeedMarkerUpdated } from '@amityco/ts-sdk'
@@ -26790,11 +26557,52 @@ const markerSync = async (deviceLastSyncAt) => {
26790
26557
  };
26791
26558
  };
26792
26559
 
26560
+ const enableUnreadCount = () => {
26561
+ const client = getActiveClient();
26562
+ client.log('client/api/isUnreadCountEnabled', client.isUnreadCountEnabled);
26563
+ if (!client) {
26564
+ throw new ASCError('There is no active client', 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "fatal" /* Amity.ErrorLevel.FATAL */);
26565
+ }
26566
+ if (client.isUnreadCountEnabled)
26567
+ return false;
26568
+ client.isUnreadCountEnabled = true;
26569
+ client.useLegacyUnreadCount = false;
26570
+ client.emitter.emit('unreadCountEnabled', true);
26571
+ return true;
26572
+ };
26573
+
26574
+ /**
26575
+ * ```js
26576
+ * import { onFeedMarkerUpdated } from '@amityco/ts-sdk'
26577
+ * const dispose = onFeedMarkerUpdated(feedMarker => {
26578
+ * // ...
26579
+ * })
26580
+ * ```
26581
+ *
26582
+ * Fired when an {@link Amity.UserFeedMarker} has been updated
26583
+ *
26584
+ * @param callback The function to call when the event was fired
26585
+ * @returns an {@link Amity.Unsubscriber} function to stop listening
26586
+ *
26587
+ * @category FeedMarker Events
26588
+ */
26589
+ const onUserFeedMarkerUpdated = (callback) => {
26590
+ const client = getActiveClient();
26591
+ const filter = (payload) => {
26592
+ persistUnreadCountInfo(payload);
26593
+ payload.feedMarkers.forEach(feedMarker => {
26594
+ callback(feedMarker);
26595
+ });
26596
+ };
26597
+ return createEventSubscriber(client, 'feedMarker/onUserFeedMarkerUpdated', 'marker.userFeed-updated', filter);
26598
+ };
26599
+
26793
26600
  const SYNC_TRIGGER_INTERVAL_TIME = 2000;
26601
+ const ON_SUB_CHANNEL_DELETE_SYNC_TRIGGER_DELAY = 2000;
26794
26602
  let isSyncRunning = false;
26795
26603
  let disposers$1 = [];
26796
26604
  let isWaitingForResponse = false;
26797
- const isConsistentMode = true;
26605
+ let isConsistentMode = true;
26798
26606
  let deviceLastSyncAt = null;
26799
26607
  const getDeviceLastSyncAt = () => {
26800
26608
  if (deviceLastSyncAt == null) {
@@ -26809,6 +26617,12 @@ const saveDeviceLastSyncAt = (lastSyncAt) => {
26809
26617
  deviceLastSyncAt = lastSyncAt;
26810
26618
  }
26811
26619
  };
26620
+ const fetchDeviceLastSyncAt = async () => {
26621
+ const { data: userMarker } = await getUserMarker();
26622
+ if (userMarker == null)
26623
+ return;
26624
+ saveDeviceLastSyncAt(new Date(userMarker.lastSyncAt));
26625
+ };
26812
26626
  /**
26813
26627
  * list of conditions that make timer still trigger the syncing process.
26814
26628
  * If it's empty, it means sync is stopped.
@@ -26846,71 +26660,372 @@ const markerSyncTrigger = async () => {
26846
26660
  return;
26847
26661
  }
26848
26662
  try {
26849
- isWaitingForResponse = true;
26850
- // any past events are considered processed here.
26851
- // however during waiting for the response, RTE could add the new message event;
26852
- // which will make the engine trigger another call next round.
26853
- events = [];
26854
- const response = await markerSync(getDeviceLastSyncAt().toISOString());
26855
- const latestLastSyncAt = response.data.userMarkers.reduce((maxLastSyncAt, userMarker) => {
26856
- if (maxLastSyncAt == null ||
26857
- maxLastSyncAt.getTime() < new Date(userMarker.lastSyncAt).getTime()) {
26858
- return new Date(userMarker.lastSyncAt);
26859
- }
26860
- return maxLastSyncAt;
26861
- }, null);
26862
- saveDeviceLastSyncAt(latestLastSyncAt);
26863
- if (response.hasMore) {
26864
- events.push("has_more" /* Amity.MarkerSyncEvent.HAS_MORE */);
26663
+ isWaitingForResponse = true;
26664
+ // any past events are considered processed here.
26665
+ // however during waiting for the response, RTE could add the new message event;
26666
+ // which will make the engine trigger another call next round.
26667
+ events = [];
26668
+ const response = await markerSync(getDeviceLastSyncAt().toISOString());
26669
+ const latestLastSyncAt = response.data.userMarkers.reduce((maxLastSyncAt, userMarker) => {
26670
+ if (maxLastSyncAt == null ||
26671
+ maxLastSyncAt.getTime() < new Date(userMarker.lastSyncAt).getTime()) {
26672
+ return new Date(userMarker.lastSyncAt);
26673
+ }
26674
+ return maxLastSyncAt;
26675
+ }, null);
26676
+ saveDeviceLastSyncAt(latestLastSyncAt);
26677
+ if (response.hasMore) {
26678
+ events.push("has_more" /* Amity.MarkerSyncEvent.HAS_MORE */);
26679
+ }
26680
+ }
26681
+ catch (_a) {
26682
+ // prevent sync from stopping
26683
+ }
26684
+ finally {
26685
+ if (isWaitingForResponse) {
26686
+ isWaitingForResponse = false;
26687
+ }
26688
+ }
26689
+ };
26690
+ const registerEventListeners = () => {
26691
+ if (disposers$1.length > 0) {
26692
+ return;
26693
+ }
26694
+ // based on the tech spec design, we designed a fetch marker in case of these events
26695
+ // - new message
26696
+ // - create channel
26697
+ // - remove channel
26698
+ // - app going to online again after offline
26699
+ disposers$1.push(onOnline(() => {
26700
+ // should add RESUME to the event to trigger marker syncing again
26701
+ events.push("resume" /* Amity.MarkerSyncEvent.RESUME */);
26702
+ }), onMessageCreatedMqtt(message => {
26703
+ // only conversation, community and broadcast types can sync
26704
+ const client = getActiveClient();
26705
+ if (isUnreadCountSupport$1(message) && message.creatorId !== client.userId)
26706
+ events.push("new message" /* Amity.MarkerSyncEvent.NEW_MESSAGE */);
26707
+ }), 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(() =>
26708
+ /*
26709
+ workaround: when receiving the event for sub-channel deletion,
26710
+ before triggering marker update, the SDK will have to add a 2-second delay.
26711
+ so that the unread count is calculated correctly.
26712
+ */
26713
+ 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 */)));
26714
+ };
26715
+ const unRegisterEventListeners = () => {
26716
+ disposers$1.forEach(fn => fn());
26717
+ disposers$1 = [];
26718
+ };
26719
+ const startMarkerSync = async () => {
26720
+ await fetchDeviceLastSyncAt();
26721
+ pushMarkerSyncEvent("start syncing" /* Amity.MarkerSyncEvent.START_SYNCING */);
26722
+ isConsistentMode = true;
26723
+ isSyncRunning = true;
26724
+ registerEventListeners();
26725
+ return unRegisterEventListeners;
26726
+ };
26727
+ /**
26728
+ ```js
26729
+ * import { startUnreadSync } from '@amityco/ts-sdk'
26730
+ * startUnreadSync()
26731
+ * ```
26732
+ *
26733
+ * start syncing to keep feed markers, channel markers and user makers cache
26734
+ * update to the server.
26735
+ *
26736
+ * @category Marker API
26737
+ */
26738
+ const startUnreadSync = async () => {
26739
+ await fetchDeviceLastSyncAt();
26740
+ pushMarkerSyncEvent("start syncing" /* Amity.MarkerSyncEvent.START_SYNCING */);
26741
+ enableUnreadCount();
26742
+ isConsistentMode = false;
26743
+ isSyncRunning = true;
26744
+ registerEventListeners();
26745
+ };
26746
+ /**
26747
+ ```js
26748
+ * import { stopUnreadSync } from '@amityco/ts-sdk'
26749
+ * stopUnreadSync()
26750
+ * ```
26751
+ *
26752
+ * stop unread syncing
26753
+ *
26754
+ * @category Marker API
26755
+ */
26756
+ const stopUnreadSync = () => {
26757
+ isSyncRunning = false;
26758
+ setMarkerSyncEvents([]);
26759
+ unRegisterEventListeners();
26760
+ };
26761
+ setIntervalTask(async () => {
26762
+ if (!isSyncRunning)
26763
+ return;
26764
+ await markerSyncTrigger();
26765
+ }, SYNC_TRIGGER_INTERVAL_TIME);
26766
+ const getMarkerSyncConsistentMode = () => isConsistentMode;
26767
+
26768
+ /* eslint-disable no-param-reassign */
26769
+ /*
26770
+ * declared earlier to accomodate case when logging in with a different user
26771
+ * than the one already connected, in which case the existing subscriptions need
26772
+ * to be cleared
26773
+ */
26774
+ let subscriptions = [];
26775
+ async function runMqtt() {
26776
+ await modifyMqttConnection();
26777
+ }
26778
+ /* begin_public_function
26779
+ id: client.login
26780
+ */
26781
+ /**
26782
+ * ```js
26783
+ * import { login } from '@amityco/ts-sdk/client/api'
26784
+ * const success = await login({
26785
+ * userId: 'XYZ123456789',
26786
+ * })
26787
+ * ```
26788
+ *
26789
+ * Connects an {@link Amity.Client} instance to ASC servers
26790
+ *
26791
+ * @param params the connect parameters
26792
+ * @param params.userId the user ID for the current session
26793
+ * @param params.displayName the user's displayName for the current session
26794
+ * @param params.deviceId Manual override of the user's device id (for device management)
26795
+ * @param params.authToken The authentication token - necessary when network option is set to secure
26796
+ * @returns a success boolean if connected
26797
+ *
26798
+ * @category Client API
26799
+ * @async
26800
+ */
26801
+ const login = async (params, sessionHandler, config) => {
26802
+ var _a;
26803
+ const client = getActiveClient();
26804
+ let unsubWatcher;
26805
+ client.log('client/api/connectClient', Object.assign({ apiKey: client.apiKey, sessionState: client.sessionState }, params));
26806
+ // if connecting to a different userId than the one that is connected currently
26807
+ if (client.userId && client.userId !== params.userId) {
26808
+ await logout();
26809
+ // Remove subscription to ban and delete
26810
+ subscriptions.forEach(fn => fn());
26811
+ subscriptions = [];
26812
+ }
26813
+ // default values
26814
+ const defaultDeviceId = await getDeviceId();
26815
+ try {
26816
+ const { users } = await setClientToken({
26817
+ 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 }),
26818
+ options: {
26819
+ setAccessTokenCookie: true,
26820
+ },
26821
+ });
26822
+ const user = users.find(u => u.userId === params.userId);
26823
+ if (user == null) {
26824
+ throw new ASCError(`${params.userId} has not been founded`, 800000 /* Amity.ClientError.UNKNOWN_ERROR */, "error" /* Amity.ErrorLevel.ERROR */);
26825
+ }
26826
+ if (user.isDeleted) {
26827
+ terminateClient("userDeleted" /* Amity.TokenTerminationReason.USER_DELETED */);
26828
+ return false;
26829
+ }
26830
+ if (user.isGlobalBanned) {
26831
+ terminateClient("globalBan" /* Amity.TokenTerminationReason.GLOBAL_BAN */);
26832
+ return false;
26865
26833
  }
26834
+ // FIXME: events are duplicated if connectClient is called few times without disconnectClient
26835
+ // wire websocket events to our event emitter
26836
+ proxyWebsocketEvents(client.ws, client.emitter);
26837
+ (_a = client.ws) === null || _a === void 0 ? void 0 : _a.open();
26838
+ client.userId = user.userId;
26839
+ client.sessionHandler = sessionHandler;
26840
+ /*
26841
+ * Cannot push to subscriptions as watcher needs to continue working even if
26842
+ * token expires
26843
+ */
26844
+ unsubWatcher = client.accessTokenExpiryWatcher(sessionHandler);
26845
+ setActiveUser(user);
26866
26846
  }
26867
- catch (_a) {
26868
- // prevent sync from stopping
26847
+ catch (error) {
26848
+ /*
26849
+ * if getting token failed session state reverts to initial state when app
26850
+ * is first launched
26851
+ */
26852
+ SessionWatcher$1.getInstance().setSessionState("notLoggedIn" /* Amity.SessionStates.NOT_LOGGED_IN */);
26853
+ // pass error down tree so the calling function handle it
26854
+ throw error;
26869
26855
  }
26870
- finally {
26871
- if (isWaitingForResponse) {
26872
- isWaitingForResponse = false;
26856
+ if ((config === null || config === void 0 ? void 0 : config.disableRTE) !== true) {
26857
+ runMqtt();
26858
+ }
26859
+ await initializeMessagePreviewSetting();
26860
+ if (subscriptions.length === 0) {
26861
+ subscriptions.push(
26862
+ // GLOBAL_BAN
26863
+ onClientBanned((_) => {
26864
+ terminateClient("globalBan" /* Amity.TokenTerminationReason.GLOBAL_BAN */);
26865
+ subscriptions.forEach(fn => fn());
26866
+ unsubWatcher();
26867
+ }), onTokenTerminated(_ => {
26868
+ terminateClient();
26869
+ subscriptions.forEach(fn => fn());
26870
+ unsubWatcher();
26871
+ }), onUserDeleted$2((user) => {
26872
+ if (user.userId === client.userId) {
26873
+ terminateClient("userDeleted" /* Amity.TokenTerminationReason.USER_DELETED */);
26874
+ subscriptions.forEach(fn => fn());
26875
+ unsubWatcher();
26876
+ }
26877
+ }), onTokenExpired(state => {
26878
+ SessionWatcher$1.getInstance().setSessionState(state);
26879
+ logout();
26880
+ subscriptions.forEach(fn => fn());
26881
+ }),
26882
+ // NOTE: This is a temporary solution to handle the channel marker when the user is forced to leave
26883
+ // the channel because currently backend can't handle this, so every time a user is banned from
26884
+ // a channel or the channel is deleted the channel's unread count will not be reset to zero
26885
+ onChannelDeleted(removeChannelMarkerCache), onChannelMemberBanned(removeChannelMarkerCache), markReadEngineOnLoginHandler(), analyticsEngineOnLoginHandler(), objectResolverEngineOnLoginHandler());
26886
+ if (client.useLegacyUnreadCount) {
26887
+ subscriptions.push(readReceiptSyncEngineOnLoginHandler());
26873
26888
  }
26889
+ else
26890
+ subscriptions.push(legacyReadReceiptSyncEngineOnLoginHandler());
26891
+ const markerSyncUnsubscriber = await startMarkerSync();
26892
+ subscriptions.push(markerSyncUnsubscriber);
26874
26893
  }
26894
+ return true;
26875
26895
  };
26876
- const unRegisterEventListeners = () => {
26877
- disposers$1.forEach(fn => fn());
26878
- disposers$1 = [];
26896
+ /* end_public_function */
26897
+
26898
+ /* begin_public_function
26899
+ id: client.renew_access_token
26900
+ */
26901
+ /*
26902
+ * Renewal defintion accepted by SessionHandler interface
26903
+ *
26904
+ * Tech Spec:
26905
+ * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#Session-Handler
26906
+ *
26907
+ * @category private
26908
+ */
26909
+ const renewal = () => {
26910
+ let tokenRenewed = false;
26911
+ let renewTimeoutId;
26912
+ const client = getActiveClient();
26913
+ client.log('initiating access token renewal');
26914
+ /*
26915
+ * Renews a token if it is hasn't been renewed before. Also marks token as
26916
+ * renewed once done
26917
+ * Per instance of Renewal, only one renewal is allowed
26918
+ */
26919
+ const renewToken = async (authToken) => {
26920
+ const { userId, displayName } = getActiveUser();
26921
+ const deviceId = await getDeviceId();
26922
+ const params = { userId, displayName, authToken, deviceId };
26923
+ if (client.sessionState === "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */ && client.sessionHandler) {
26924
+ await login(params, client.sessionHandler);
26925
+ }
26926
+ else {
26927
+ // about to expire
26928
+ await setClientToken({
26929
+ params,
26930
+ options: {
26931
+ setAccessTokenCookie: true,
26932
+ },
26933
+ });
26934
+ }
26935
+ tokenRenewed = true;
26936
+ if (renewTimeoutId)
26937
+ clearTimeout(renewTimeoutId);
26938
+ };
26939
+ return {
26940
+ renew: () => {
26941
+ if (tokenRenewed) {
26942
+ console.log("'renew' method can be called only once per renewal instance");
26943
+ return;
26944
+ }
26945
+ renewToken();
26946
+ },
26947
+ renewWithAuthToken: (authToken) => {
26948
+ if (tokenRenewed) {
26949
+ console.log("'renewWithAuthToken' method can be called only once per renewal instance");
26950
+ return;
26951
+ }
26952
+ renewToken(authToken);
26953
+ },
26954
+ unableToRetrieveAuthToken: () => {
26955
+ renewTimeoutId = setTimeout(() => {
26956
+ var _a;
26957
+ (_a = client.sessionHandler) === null || _a === void 0 ? void 0 : _a.sessionWillRenewAccessToken(renewal());
26958
+ }, ACCESS_TOKEN_WATCHER_INTERVAL);
26959
+ },
26960
+ };
26879
26961
  };
26880
- const startMarkerSync = async () => Promise.resolve();
26881
- /**
26882
- ```js
26883
- * import { startUnreadSync } from '@amityco/ts-sdk'
26884
- * startUnreadSync()
26885
- * ```
26962
+ /* end_public_function */
26963
+
26964
+ const ABOUT_TO_EXPIRE_THRESHOLD = 80 / 100;
26965
+ const COMPENSATED_DELAY = 5 * MINUTE;
26966
+ /*
26967
+ * a helper function to check if the token has expires
26886
26968
  *
26887
- * start syncing to keep feed markers, channel markers and user makers cache
26888
- * update to the server.
26969
+ * @param token to be checked
26970
+ * @returns boolean indicating if token expires
26889
26971
  *
26890
- * @category Marker API
26972
+ * @category private
26891
26973
  */
26892
- const startUnreadSync = async () => Promise.resolve();
26893
- /**
26894
- ```js
26895
- * import { stopUnreadSync } from '@amityco/ts-sdk'
26896
- * stopUnreadSync()
26897
- * ```
26974
+ const isExpired = (expiresAt) => Date.now() > Date.parse(expiresAt) - COMPENSATED_DELAY;
26975
+ /*
26976
+ * a helper function to check if the token is about to expire
26898
26977
  *
26899
- * stop unread syncing
26978
+ * @param token to be checked
26979
+ * @returns boolean indicating if token is aboutToExpire
26900
26980
  *
26901
- * @category Marker API
26981
+ * @category private
26902
26982
  */
26903
- const stopUnreadSync = () => {
26904
- isSyncRunning = false;
26905
- setMarkerSyncEvents([]);
26906
- unRegisterEventListeners();
26983
+ const isAboutToExpire = (params) => {
26984
+ const { expiresAt, issuedAt } = params;
26985
+ const expires = Date.parse(expiresAt);
26986
+ const issued = Date.parse(issuedAt);
26987
+ const now = Date.now();
26988
+ const duration = expires - issued - COMPENSATED_DELAY;
26989
+ const aboutToExpireAt = issued + duration * ABOUT_TO_EXPIRE_THRESHOLD;
26990
+ return now > aboutToExpireAt && now < expires;
26907
26991
  };
26908
- setIntervalTask(async () => {
26909
- if (!isSyncRunning)
26910
- return;
26911
- await markerSyncTrigger();
26912
- }, SYNC_TRIGGER_INTERVAL_TIME);
26913
- const getMarkerSyncConsistentMode = () => isConsistentMode;
26992
+ /*
26993
+ * Monitors time to expire of token and updates session state to aboutToExpire
26994
+ *
26995
+ * @returns intervalId to be cleared after trigger
26996
+ *
26997
+ * @category private
26998
+ */
26999
+ const accessTokenExpiryWatcher = (sessionHandler) => {
27000
+ const interval = setInterval(() => {
27001
+ const client = getActiveClient();
27002
+ if (!client.token)
27003
+ return;
27004
+ const { issuedAt, expiresAt } = client.token;
27005
+ if (isExpired(expiresAt)) {
27006
+ /*
27007
+ * the event handler will take care of updating session state
27008
+ * Note, this will also clear the interval id, so this event will only be
27009
+ * fired once
27010
+ */
27011
+ fireEvent('tokenExpired', "tokenExpired" /* Amity.SessionStates.TOKEN_EXPIRED */);
27012
+ /*
27013
+ * https://ekoapp.atlassian.net/wiki/spaces/UP/pages/2082537485/ASC+Core+-+Session+Management+3.0#Automatically-initiate-renewal-flow
27014
+ *
27015
+ * Why sechduled task?
27016
+ * Since fireEvent is scheduled, it will be called
27017
+ * after sessionHandler leading to an invalid state change from
27018
+ * establishing to tokenExpired
27019
+ */
27020
+ scheduleTask(() => sessionHandler.sessionWillRenewAccessToken(renewal()));
27021
+ return;
27022
+ }
27023
+ if (isAboutToExpire({ expiresAt, issuedAt })) {
27024
+ sessionHandler.sessionWillRenewAccessToken(renewal());
27025
+ }
27026
+ }, ACCESS_TOKEN_WATCHER_INTERVAL);
27027
+ return () => clearInterval(interval);
27028
+ };
26914
27029
 
26915
27030
  const DEFAULT_DEBUG_SESSION = 'amity';
26916
27031
  /**
@@ -27057,10 +27172,6 @@ const isConnected = () => {
27057
27172
  isWsConnected);
27058
27173
  };
27059
27174
 
27060
- const enableUnreadCount = () => {
27061
- return true;
27062
- };
27063
-
27064
27175
  var _GlobalFileAccessType_fileAccessType;
27065
27176
  class GlobalFileAccessType {
27066
27177
  constructor() {