stream-chat 9.2.0-offline-support-beta.2 → 9.2.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/cjs/index.browser.cjs +378 -198
- package/dist/cjs/index.browser.cjs.map +3 -3
- package/dist/cjs/index.node.cjs +378 -198
- package/dist/cjs/index.node.cjs.map +3 -3
- package/dist/esm/index.js +378 -198
- package/dist/esm/index.js.map +3 -3
- package/dist/types/channel_manager.d.ts +2 -2
- package/dist/types/channel_state.d.ts +1 -1
- package/dist/types/client.d.ts +2 -2
- package/dist/types/constants.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/messageComposer/attachmentManager.d.ts +2 -0
- package/dist/types/offline-support/index.d.ts +3 -0
- package/dist/types/offline-support/offline_support_api.d.ts +558 -0
- package/dist/types/offline-support/offline_sync_manager.d.ts +63 -0
- package/dist/types/offline-support/types.d.ts +327 -0
- package/dist/types/types.d.ts +1 -5
- package/dist/types/utils.d.ts +2 -3
- package/package.json +8 -10
- package/src/channel.ts +9 -5
- package/src/channel_manager.ts +12 -10
- package/src/channel_state.ts +20 -9
- package/src/client.ts +8 -10
- package/src/constants.ts +1 -1
- package/src/index.ts +1 -1
- package/src/messageComposer/attachmentManager.ts +45 -25
- package/src/messageComposer/fileUtils.ts +1 -1
- package/src/messageComposer/messageComposer.ts +1 -1
- package/src/offline-support/index.ts +3 -0
- package/src/offline-support/offline_support_api.ts +1104 -0
- package/src/offline-support/offline_sync_manager.ts +190 -0
- package/src/offline-support/types.ts +403 -0
- package/src/types.ts +1 -5
- package/src/utils.ts +10 -8
- package/dist/types/offline_support_api.d.ts +0 -351
- package/src/offline_support_api.ts +0 -1078
|
@@ -2550,7 +2550,7 @@ var RESERVED_UPDATED_MESSAGE_FIELDS = {
|
|
|
2550
2550
|
};
|
|
2551
2551
|
var LOCAL_MESSAGE_FIELDS = { error: true };
|
|
2552
2552
|
var DEFAULT_QUERY_CHANNELS_RETRY_COUNT = 3;
|
|
2553
|
-
var
|
|
2553
|
+
var DEFAULT_QUERY_CHANNELS_MS_BETWEEN_RETRIES = 1e3;
|
|
2554
2554
|
|
|
2555
2555
|
// src/utils.ts
|
|
2556
2556
|
function logChatPromiseExecution(promise, name) {
|
|
@@ -3254,16 +3254,17 @@ var promoteChannel = ({
|
|
|
3254
3254
|
};
|
|
3255
3255
|
var isDate2 = (value) => !!value.getTime;
|
|
3256
3256
|
var isLocalMessage = (message) => isDate2(message.created_at);
|
|
3257
|
-
var waitSeconds = (seconds) => new Promise((resolve) => {
|
|
3258
|
-
setTimeout(resolve, seconds * 1e3);
|
|
3259
|
-
});
|
|
3260
3257
|
var runDetached = (callback, options) => {
|
|
3261
3258
|
const { context, onSuccessCallback = () => void 0, onErrorCallback } = options ?? {};
|
|
3262
3259
|
const defaultOnError = (error) => {
|
|
3263
3260
|
console.log(`An error has occurred in context ${context}: ${error}`);
|
|
3264
3261
|
};
|
|
3265
3262
|
const onError = onErrorCallback ?? defaultOnError;
|
|
3266
|
-
callback
|
|
3263
|
+
let promise = callback;
|
|
3264
|
+
if (onSuccessCallback) {
|
|
3265
|
+
promise = promise.then(onSuccessCallback);
|
|
3266
|
+
}
|
|
3267
|
+
promise.catch(onError);
|
|
3267
3268
|
};
|
|
3268
3269
|
|
|
3269
3270
|
// src/channel_state.ts
|
|
@@ -3539,12 +3540,14 @@ var ChannelState = class {
|
|
|
3539
3540
|
};
|
|
3540
3541
|
this._updateMessage(updateData, (msg) => {
|
|
3541
3542
|
if (messageWithReaction) {
|
|
3543
|
+
const updatedMessage = { ...messageWithReaction };
|
|
3542
3544
|
messageWithReaction.own_reactions = this._addOwnReactionToMessage(
|
|
3543
3545
|
msg.own_reactions,
|
|
3544
3546
|
reaction,
|
|
3545
3547
|
enforce_unique
|
|
3546
3548
|
);
|
|
3547
|
-
|
|
3549
|
+
updatedMessage.own_reactions = this._channel.getClient().userID === reaction.user_id ? messageWithReaction.own_reactions : msg.own_reactions;
|
|
3550
|
+
return this.formatMessage(updatedMessage);
|
|
3548
3551
|
}
|
|
3549
3552
|
if (messageFromState) {
|
|
3550
3553
|
return this._addReactionToState(messageFromState, reaction, enforce_unique);
|
|
@@ -3599,16 +3602,15 @@ var ChannelState = class {
|
|
|
3599
3602
|
return messageFromState;
|
|
3600
3603
|
}
|
|
3601
3604
|
_addOwnReactionToMessage(ownReactions, reaction, enforce_unique) {
|
|
3602
|
-
if (this._channel.getClient().userID !== reaction.user_id) {
|
|
3603
|
-
return ownReactions;
|
|
3604
|
-
}
|
|
3605
3605
|
if (enforce_unique) {
|
|
3606
3606
|
ownReactions = [];
|
|
3607
3607
|
} else {
|
|
3608
3608
|
ownReactions = this._removeOwnReactionFromMessage(ownReactions, reaction);
|
|
3609
3609
|
}
|
|
3610
3610
|
ownReactions = ownReactions || [];
|
|
3611
|
-
|
|
3611
|
+
if (this._channel.getClient().userID === reaction.user_id) {
|
|
3612
|
+
ownReactions.push(reaction);
|
|
3613
|
+
}
|
|
3612
3614
|
return ownReactions;
|
|
3613
3615
|
}
|
|
3614
3616
|
_removeOwnReactionFromMessage(ownReactions, reaction) {
|
|
@@ -3727,9 +3729,8 @@ var ChannelState = class {
|
|
|
3727
3729
|
(msg) => msg.id === message.id
|
|
3728
3730
|
);
|
|
3729
3731
|
if (msgIndex !== -1) {
|
|
3730
|
-
this.messageSets[messageSetIndex].messages[msgIndex]
|
|
3731
|
-
|
|
3732
|
-
);
|
|
3732
|
+
const upMsg = updateFunc(this.messageSets[messageSetIndex].messages[msgIndex]);
|
|
3733
|
+
this.messageSets[messageSetIndex].messages[msgIndex] = upMsg;
|
|
3733
3734
|
}
|
|
3734
3735
|
}
|
|
3735
3736
|
}
|
|
@@ -3989,7 +3990,7 @@ var isFile2 = (fileLike) => !!fileLike.lastModified && !("uri" in fileLike);
|
|
|
3989
3990
|
var isFileList2 = (obj) => {
|
|
3990
3991
|
if (obj === null || obj === void 0) return false;
|
|
3991
3992
|
if (typeof obj !== "object") return false;
|
|
3992
|
-
return obj instanceof FileList || "item" in obj && "length" in obj && !Array.isArray(obj);
|
|
3993
|
+
return typeof FileList !== "undefined" && obj instanceof FileList || "item" in obj && "length" in obj && !Array.isArray(obj);
|
|
3993
3994
|
};
|
|
3994
3995
|
var isBlobButNotFile = (obj) => obj instanceof Blob && !(obj instanceof File);
|
|
3995
3996
|
var isFileReference = (obj) => obj !== null && typeof obj === "object" && !isFile2(obj) && !isBlobButNotFile(obj) && typeof obj.name === "string" && typeof obj.uri === "string" && typeof obj.size === "number" && typeof obj.type === "string";
|
|
@@ -4476,29 +4477,51 @@ var AttachmentManager = class {
|
|
|
4476
4477
|
const attachmentsById = this.attachmentsById;
|
|
4477
4478
|
return this.attachments.indexOf(attachmentsById[localId]);
|
|
4478
4479
|
};
|
|
4480
|
+
this.prepareAttachmentUpdate = (attachmentToUpdate) => {
|
|
4481
|
+
const stateAttachments = this.attachments;
|
|
4482
|
+
const attachments = [...this.attachments];
|
|
4483
|
+
const attachmentIndex = this.getAttachmentIndex(attachmentToUpdate.localMetadata.id);
|
|
4484
|
+
if (attachmentIndex === -1) return null;
|
|
4485
|
+
const merged = mergeWithDiff(
|
|
4486
|
+
stateAttachments[attachmentIndex],
|
|
4487
|
+
attachmentToUpdate
|
|
4488
|
+
);
|
|
4489
|
+
const updatesOnMerge = merged.diff && Object.keys(merged.diff.children).length;
|
|
4490
|
+
if (updatesOnMerge) {
|
|
4491
|
+
const localAttachment = ensureIsLocalAttachment(merged.result);
|
|
4492
|
+
if (localAttachment) {
|
|
4493
|
+
attachments.splice(attachmentIndex, 1, localAttachment);
|
|
4494
|
+
return attachments;
|
|
4495
|
+
}
|
|
4496
|
+
}
|
|
4497
|
+
return null;
|
|
4498
|
+
};
|
|
4499
|
+
this.updateAttachment = (attachmentToUpdate) => {
|
|
4500
|
+
const updatedAttachments = this.prepareAttachmentUpdate(attachmentToUpdate);
|
|
4501
|
+
if (updatedAttachments) {
|
|
4502
|
+
this.state.partialNext({ attachments: updatedAttachments });
|
|
4503
|
+
}
|
|
4504
|
+
};
|
|
4479
4505
|
this.upsertAttachments = (attachmentsToUpsert) => {
|
|
4480
4506
|
if (!attachmentsToUpsert.length) return;
|
|
4481
|
-
|
|
4482
|
-
|
|
4507
|
+
let attachments = [...this.attachments];
|
|
4508
|
+
let hasUpdates = false;
|
|
4483
4509
|
attachmentsToUpsert.forEach((attachment) => {
|
|
4484
|
-
const
|
|
4485
|
-
if (
|
|
4486
|
-
|
|
4487
|
-
|
|
4510
|
+
const updatedAttachments = this.prepareAttachmentUpdate(attachment);
|
|
4511
|
+
if (updatedAttachments) {
|
|
4512
|
+
attachments = updatedAttachments;
|
|
4513
|
+
hasUpdates = true;
|
|
4488
4514
|
} else {
|
|
4489
|
-
const
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
const updatesOnMerge = merged.diff && Object.keys(merged.diff.children).length;
|
|
4494
|
-
if (updatesOnMerge) {
|
|
4495
|
-
const localAttachment = ensureIsLocalAttachment(merged.result);
|
|
4496
|
-
if (localAttachment)
|
|
4497
|
-
newAttachments.splice(targetAttachmentIndex, 1, localAttachment);
|
|
4515
|
+
const localAttachment = ensureIsLocalAttachment(attachment);
|
|
4516
|
+
if (localAttachment) {
|
|
4517
|
+
attachments.push(localAttachment);
|
|
4518
|
+
hasUpdates = true;
|
|
4498
4519
|
}
|
|
4499
4520
|
}
|
|
4500
4521
|
});
|
|
4501
|
-
|
|
4522
|
+
if (hasUpdates) {
|
|
4523
|
+
this.state.partialNext({ attachments });
|
|
4524
|
+
}
|
|
4502
4525
|
};
|
|
4503
4526
|
this.removeAttachments = (localAttachmentIds) => {
|
|
4504
4527
|
this.state.partialNext({
|
|
@@ -4526,7 +4549,7 @@ var AttachmentManager = class {
|
|
|
4526
4549
|
} = uploadConfig;
|
|
4527
4550
|
const sizeLimit = size_limit || DEFAULT_UPLOAD_SIZE_LIMIT_BYTES;
|
|
4528
4551
|
const mimeType = fileLike.type;
|
|
4529
|
-
if (isFile2(fileLike)) {
|
|
4552
|
+
if (isFile2(fileLike) || isFileReference(fileLike)) {
|
|
4530
4553
|
if (allowed_file_extensions?.length && !allowed_file_extensions.some(
|
|
4531
4554
|
(ext) => fileLike.name.toLowerCase().endsWith(ext.toLowerCase())
|
|
4532
4555
|
)) {
|
|
@@ -4672,7 +4695,7 @@ var AttachmentManager = class {
|
|
|
4672
4695
|
uploadState: "failed"
|
|
4673
4696
|
}
|
|
4674
4697
|
};
|
|
4675
|
-
this.
|
|
4698
|
+
this.updateAttachment(failedAttachment);
|
|
4676
4699
|
return failedAttachment;
|
|
4677
4700
|
}
|
|
4678
4701
|
if (!response) {
|
|
@@ -4698,7 +4721,7 @@ var AttachmentManager = class {
|
|
|
4698
4721
|
if (response.thumb_url) {
|
|
4699
4722
|
uploadedAttachment.thumb_url = response.thumb_url;
|
|
4700
4723
|
}
|
|
4701
|
-
this.
|
|
4724
|
+
this.updateAttachment(uploadedAttachment);
|
|
4702
4725
|
return uploadedAttachment;
|
|
4703
4726
|
};
|
|
4704
4727
|
this.uploadFiles = async (files) => {
|
|
@@ -7648,7 +7671,7 @@ var _MessageComposer = class _MessageComposer extends WithSubscriptions {
|
|
|
7648
7671
|
this.addUnsubscribeFunction(this.subscribeCustomDataManagerStateChanged());
|
|
7649
7672
|
this.addUnsubscribeFunction(this.subscribeMessageComposerStateChanged());
|
|
7650
7673
|
this.addUnsubscribeFunction(this.subscribeMessageComposerConfigStateChanged());
|
|
7651
|
-
return this.unregisterSubscriptions;
|
|
7674
|
+
return this.unregisterSubscriptions.bind(this);
|
|
7652
7675
|
};
|
|
7653
7676
|
this.subscribeMessageUpdated = () => {
|
|
7654
7677
|
const eventTypes = [
|
|
@@ -8372,10 +8395,12 @@ var Channel = class {
|
|
|
8372
8395
|
type: reactionType,
|
|
8373
8396
|
user_id: this.getClient().userID ?? user_id
|
|
8374
8397
|
};
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8398
|
+
if (message) {
|
|
8399
|
+
await offlineDb.deleteReaction({
|
|
8400
|
+
message,
|
|
8401
|
+
reaction
|
|
8402
|
+
});
|
|
8403
|
+
}
|
|
8379
8404
|
return await offlineDb.queueTask({
|
|
8380
8405
|
task: {
|
|
8381
8406
|
channelId: this.id,
|
|
@@ -8847,7 +8872,7 @@ var Channel = class {
|
|
|
8847
8872
|
});
|
|
8848
8873
|
}
|
|
8849
8874
|
_isTypingIndicatorsEnabled() {
|
|
8850
|
-
if (!this.getConfig()?.typing_events) {
|
|
8875
|
+
if (!this.getConfig()?.typing_events || !this.getClient().wsConnection?.isHealthy) {
|
|
8851
8876
|
return false;
|
|
8852
8877
|
}
|
|
8853
8878
|
return this.getClient().user?.privacy_settings?.typing_indicators?.enabled ?? true;
|
|
@@ -11726,7 +11751,7 @@ var ChannelManager = class extends WithSubscriptions {
|
|
|
11726
11751
|
this.setOptions = (options = {}) => {
|
|
11727
11752
|
this.options = { ...DEFAULT_CHANNEL_MANAGER_OPTIONS, ...options };
|
|
11728
11753
|
};
|
|
11729
|
-
this.
|
|
11754
|
+
this.executeChannelsQuery = async (payload, retryCount = 0) => {
|
|
11730
11755
|
const { filters, sort, options, stateOptions } = payload;
|
|
11731
11756
|
const { offset, limit } = {
|
|
11732
11757
|
...DEFAULT_CHANNEL_MANAGER_PAGINATION_OPTIONS,
|
|
@@ -11770,8 +11795,8 @@ var ChannelManager = class extends WithSubscriptions {
|
|
|
11770
11795
|
this.state.partialNext({ error: wrappedError });
|
|
11771
11796
|
return;
|
|
11772
11797
|
}
|
|
11773
|
-
await
|
|
11774
|
-
return this.
|
|
11798
|
+
await sleep(DEFAULT_QUERY_CHANNELS_MS_BETWEEN_RETRIES);
|
|
11799
|
+
return this.executeChannelsQuery(payload, retryCount + 1);
|
|
11775
11800
|
}
|
|
11776
11801
|
};
|
|
11777
11802
|
this.queryChannels = async (filters, sort = [], options = {}, stateOptions = {}) => {
|
|
@@ -11779,10 +11804,12 @@ var ChannelManager = class extends WithSubscriptions {
|
|
|
11779
11804
|
pagination: { isLoading, filters: filtersFromState },
|
|
11780
11805
|
initialized
|
|
11781
11806
|
} = this.state.getLatestValue();
|
|
11782
|
-
if (isLoading && !this.options.abortInFlightQuery &&
|
|
11807
|
+
if (isLoading && !this.options.abortInFlightQuery && // TODO: Figure a proper way to either deeply compare these or
|
|
11808
|
+
// create hashes from each.
|
|
11809
|
+
JSON.stringify(filtersFromState) === JSON.stringify(filters)) {
|
|
11783
11810
|
return;
|
|
11784
11811
|
}
|
|
11785
|
-
const
|
|
11812
|
+
const executeChannelsQueryPayload = { filters, sort, options, stateOptions };
|
|
11786
11813
|
try {
|
|
11787
11814
|
this.stateOptions = stateOptions;
|
|
11788
11815
|
this.state.next((currentState) => ({
|
|
@@ -11817,13 +11844,13 @@ var ChannelManager = class extends WithSubscriptions {
|
|
|
11817
11844
|
this.client.offlineDb.syncManager.scheduleSyncStatusChangeCallback(
|
|
11818
11845
|
this.id,
|
|
11819
11846
|
async () => {
|
|
11820
|
-
await this.
|
|
11847
|
+
await this.executeChannelsQuery(executeChannelsQueryPayload);
|
|
11821
11848
|
}
|
|
11822
11849
|
);
|
|
11823
11850
|
return;
|
|
11824
11851
|
}
|
|
11825
11852
|
}
|
|
11826
|
-
await this.
|
|
11853
|
+
await this.executeChannelsQuery(executeChannelsQueryPayload);
|
|
11827
11854
|
} catch (error) {
|
|
11828
11855
|
this.client.logger("error", error.message);
|
|
11829
11856
|
this.state.next((currentState) => ({
|
|
@@ -13166,13 +13193,11 @@ var StreamChat = class _StreamChat {
|
|
|
13166
13193
|
if (event.type === "notification.mutes_updated" && event.me?.mutes) {
|
|
13167
13194
|
this.mutedUsers = event.me.mutes;
|
|
13168
13195
|
}
|
|
13169
|
-
if (event.type === "notification.mark_read") {
|
|
13170
|
-
|
|
13171
|
-
|
|
13172
|
-
|
|
13173
|
-
|
|
13174
|
-
);
|
|
13175
|
-
}
|
|
13196
|
+
if (event.type === "notification.mark_read" && event.unread_channels === 0) {
|
|
13197
|
+
const activeChannelKeys = Object.keys(this.activeChannels);
|
|
13198
|
+
activeChannelKeys.forEach(
|
|
13199
|
+
(activeChannelKey) => this.activeChannels[activeChannelKey].state.unreadCount = 0
|
|
13200
|
+
);
|
|
13176
13201
|
}
|
|
13177
13202
|
if ((event.type === "channel.deleted" || event.type === "notification.channel_deleted") && event.cid) {
|
|
13178
13203
|
const { cid } = event;
|
|
@@ -14050,7 +14075,7 @@ var StreamChat = class _StreamChat {
|
|
|
14050
14075
|
data
|
|
14051
14076
|
);
|
|
14052
14077
|
}
|
|
14053
|
-
|
|
14078
|
+
DBDeleteChannelType(channelType) {
|
|
14054
14079
|
return this.delete(
|
|
14055
14080
|
this.baseURL + `/channeltypes/${encodeURIComponent(channelType)}`
|
|
14056
14081
|
);
|
|
@@ -14391,7 +14416,7 @@ var StreamChat = class _StreamChat {
|
|
|
14391
14416
|
if (this.userAgent) {
|
|
14392
14417
|
return this.userAgent;
|
|
14393
14418
|
}
|
|
14394
|
-
const version = "9.2.0
|
|
14419
|
+
const version = "9.2.0";
|
|
14395
14420
|
const clientBundle = "browser-cjs";
|
|
14396
14421
|
let userAgentString = "";
|
|
14397
14422
|
if (this.sdkIdentifier) {
|
|
@@ -15567,9 +15592,154 @@ var BuiltinPermissions = {
|
|
|
15567
15592
|
UseFrozenChannel: "Send messages and reactions to frozen channels"
|
|
15568
15593
|
};
|
|
15569
15594
|
|
|
15570
|
-
// src/
|
|
15595
|
+
// src/offline-support/offline_sync_manager.ts
|
|
15596
|
+
var OfflineDBSyncManager = class {
|
|
15597
|
+
constructor({
|
|
15598
|
+
client,
|
|
15599
|
+
offlineDb
|
|
15600
|
+
}) {
|
|
15601
|
+
this.syncStatus = false;
|
|
15602
|
+
this.connectionChangedListener = null;
|
|
15603
|
+
this.syncStatusListeners = [];
|
|
15604
|
+
this.scheduledSyncStatusCallbacks = /* @__PURE__ */ new Map();
|
|
15605
|
+
/**
|
|
15606
|
+
* Initializes the sync manager. Should only be called once per session.
|
|
15607
|
+
*
|
|
15608
|
+
* Cleans up old listeners if re-initialized to avoid memory leaks.
|
|
15609
|
+
* Starts syncing immediately if already connected, otherwise waits for reconnection.
|
|
15610
|
+
*/
|
|
15611
|
+
this.init = async () => {
|
|
15612
|
+
try {
|
|
15613
|
+
if (this.client.user?.id && this.client.wsConnection?.isHealthy) {
|
|
15614
|
+
await this.syncAndExecutePendingTasks();
|
|
15615
|
+
await this.invokeSyncStatusListeners(true);
|
|
15616
|
+
}
|
|
15617
|
+
if (this.connectionChangedListener) {
|
|
15618
|
+
this.connectionChangedListener.unsubscribe();
|
|
15619
|
+
}
|
|
15620
|
+
this.connectionChangedListener = this.client.on(
|
|
15621
|
+
"connection.changed",
|
|
15622
|
+
async (event) => {
|
|
15623
|
+
if (event.online) {
|
|
15624
|
+
await this.syncAndExecutePendingTasks();
|
|
15625
|
+
await this.invokeSyncStatusListeners(true);
|
|
15626
|
+
} else {
|
|
15627
|
+
await this.invokeSyncStatusListeners(false);
|
|
15628
|
+
}
|
|
15629
|
+
}
|
|
15630
|
+
);
|
|
15631
|
+
} catch (error) {
|
|
15632
|
+
console.log("Error in DBSyncManager.init: ", error);
|
|
15633
|
+
}
|
|
15634
|
+
};
|
|
15635
|
+
/**
|
|
15636
|
+
* Registers a listener that is called whenever the sync status changes.
|
|
15637
|
+
*
|
|
15638
|
+
* @param listener - A callback invoked with the new sync status (`true` or `false`).
|
|
15639
|
+
* @returns An object with an `unsubscribe` function to remove the listener.
|
|
15640
|
+
*/
|
|
15641
|
+
this.onSyncStatusChange = (listener) => {
|
|
15642
|
+
this.syncStatusListeners.push(listener);
|
|
15643
|
+
return {
|
|
15644
|
+
unsubscribe: () => {
|
|
15645
|
+
this.syncStatusListeners = this.syncStatusListeners.filter(
|
|
15646
|
+
(el) => el !== listener
|
|
15647
|
+
);
|
|
15648
|
+
}
|
|
15649
|
+
};
|
|
15650
|
+
};
|
|
15651
|
+
/**
|
|
15652
|
+
* Schedules a one-time callback to be invoked after the next successful sync.
|
|
15653
|
+
*
|
|
15654
|
+
* @param tag - A unique key to identify and manage the callback.
|
|
15655
|
+
* @param callback - An async function to run after sync.
|
|
15656
|
+
*/
|
|
15657
|
+
this.scheduleSyncStatusChangeCallback = (tag, callback) => {
|
|
15658
|
+
this.scheduledSyncStatusCallbacks.set(tag, callback);
|
|
15659
|
+
};
|
|
15660
|
+
/**
|
|
15661
|
+
* Invokes all registered sync status listeners and executes any scheduled sync callbacks.
|
|
15662
|
+
*
|
|
15663
|
+
* @param status - The new sync status (`true` or `false`).
|
|
15664
|
+
*/
|
|
15665
|
+
this.invokeSyncStatusListeners = async (status) => {
|
|
15666
|
+
this.syncStatus = status;
|
|
15667
|
+
this.syncStatusListeners.forEach((l) => l(status));
|
|
15668
|
+
if (status) {
|
|
15669
|
+
const promises = Array.from(this.scheduledSyncStatusCallbacks.values()).map(
|
|
15670
|
+
(cb) => cb()
|
|
15671
|
+
);
|
|
15672
|
+
await Promise.all(promises);
|
|
15673
|
+
this.scheduledSyncStatusCallbacks.clear();
|
|
15674
|
+
}
|
|
15675
|
+
};
|
|
15676
|
+
/**
|
|
15677
|
+
* Performs synchronization with the Stream backend.
|
|
15678
|
+
*
|
|
15679
|
+
* This includes downloading events since the last sync, updating the local DB,
|
|
15680
|
+
* and handling sync failures (e.g., if syncing beyond the allowed retention window).
|
|
15681
|
+
*/
|
|
15682
|
+
this.sync = async () => {
|
|
15683
|
+
if (!this.client?.user) {
|
|
15684
|
+
return;
|
|
15685
|
+
}
|
|
15686
|
+
try {
|
|
15687
|
+
const cids = await this.offlineDb.getAllChannelCids();
|
|
15688
|
+
if (cids.length === 0) {
|
|
15689
|
+
return;
|
|
15690
|
+
}
|
|
15691
|
+
const lastSyncedAt = await this.offlineDb.getLastSyncedAt({
|
|
15692
|
+
userId: this.client.user.id
|
|
15693
|
+
});
|
|
15694
|
+
if (lastSyncedAt) {
|
|
15695
|
+
const lastSyncedAtDate = new Date(lastSyncedAt);
|
|
15696
|
+
const nowDate = /* @__PURE__ */ new Date();
|
|
15697
|
+
const diff = Math.floor(
|
|
15698
|
+
(nowDate.getTime() - lastSyncedAtDate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
15699
|
+
);
|
|
15700
|
+
if (diff > 30) {
|
|
15701
|
+
await this.offlineDb.resetDB();
|
|
15702
|
+
} else {
|
|
15703
|
+
const result = await this.client.sync(cids, lastSyncedAtDate.toISOString());
|
|
15704
|
+
const queryPromises = result.events.map(
|
|
15705
|
+
(event) => this.offlineDb.handleEvent({ event, execute: false })
|
|
15706
|
+
);
|
|
15707
|
+
const queriesArray = await Promise.all(queryPromises);
|
|
15708
|
+
const queries = queriesArray.flat();
|
|
15709
|
+
if (queries.length) {
|
|
15710
|
+
await this.offlineDb.executeSqlBatch(queries);
|
|
15711
|
+
}
|
|
15712
|
+
}
|
|
15713
|
+
}
|
|
15714
|
+
await this.offlineDb.upsertUserSyncStatus({
|
|
15715
|
+
userId: this.client.user.id,
|
|
15716
|
+
lastSyncedAt: (/* @__PURE__ */ new Date()).toString()
|
|
15717
|
+
});
|
|
15718
|
+
} catch (e) {
|
|
15719
|
+
console.log("An error has occurred while syncing the DB.", e);
|
|
15720
|
+
await this.offlineDb.resetDB();
|
|
15721
|
+
}
|
|
15722
|
+
};
|
|
15723
|
+
/**
|
|
15724
|
+
* Executes any tasks that were queued while offline and then performs a sync.
|
|
15725
|
+
*/
|
|
15726
|
+
this.syncAndExecutePendingTasks = async () => {
|
|
15727
|
+
await this.offlineDb.executePendingTasks();
|
|
15728
|
+
await this.sync();
|
|
15729
|
+
};
|
|
15730
|
+
this.client = client;
|
|
15731
|
+
this.offlineDb = offlineDb;
|
|
15732
|
+
}
|
|
15733
|
+
};
|
|
15734
|
+
|
|
15735
|
+
// src/offline-support/offline_support_api.ts
|
|
15571
15736
|
var AbstractOfflineDB = class {
|
|
15572
15737
|
constructor({ client }) {
|
|
15738
|
+
/**
|
|
15739
|
+
* Initializes the DB as well as its syncManager for a given userId.
|
|
15740
|
+
* It will update the DBs reactive state with initialization values.
|
|
15741
|
+
* @param userId - the user ID for which we want to initialize
|
|
15742
|
+
*/
|
|
15573
15743
|
this.init = async (userId) => {
|
|
15574
15744
|
try {
|
|
15575
15745
|
if (!this.shouldInitialize(userId)) {
|
|
@@ -15586,6 +15756,15 @@ var AbstractOfflineDB = class {
|
|
|
15586
15756
|
console.log("Error Initializing DB:", error);
|
|
15587
15757
|
}
|
|
15588
15758
|
};
|
|
15759
|
+
/**
|
|
15760
|
+
* A utility method used to execute a query in a detached manner. The callback
|
|
15761
|
+
* passed uses a reference to the DB itself and will handle errors gracefully
|
|
15762
|
+
* and silently. Only really meant to be used for write queries that need to
|
|
15763
|
+
* be run in synchronous functions.
|
|
15764
|
+
* @param queryCallback - a callback wrapping all query logic that is to be executed
|
|
15765
|
+
* @param method - a utility parameter used for proper logging (will make sure the method
|
|
15766
|
+
* is logged on failure)
|
|
15767
|
+
*/
|
|
15589
15768
|
this.executeQuerySafely = (queryCallback, { method }) => {
|
|
15590
15769
|
const { initialized } = this.state.getLatestValue();
|
|
15591
15770
|
if (!initialized) {
|
|
@@ -15593,6 +15772,24 @@ var AbstractOfflineDB = class {
|
|
|
15593
15772
|
}
|
|
15594
15773
|
runDetached(queryCallback(this), { context: `OfflineDB(${method})` });
|
|
15595
15774
|
};
|
|
15775
|
+
/**
|
|
15776
|
+
* A utility method used to guard a certain DB query with the possible non-existance
|
|
15777
|
+
* of a channel inside of the DB. If the channel we want to guard against does not exist
|
|
15778
|
+
* in the DB yet, it will try to:
|
|
15779
|
+
*
|
|
15780
|
+
* 1. Use the channel from the WS event
|
|
15781
|
+
* 2. Use the channel from state
|
|
15782
|
+
*
|
|
15783
|
+
* and upsert the channels in the DB.
|
|
15784
|
+
*
|
|
15785
|
+
* If both fail, it will not execute the query as it would result in a foreign key constraint
|
|
15786
|
+
* error.
|
|
15787
|
+
*
|
|
15788
|
+
* @param event - the WS event we are trying to process
|
|
15789
|
+
* @param execute - whether to immediately execute the operation.
|
|
15790
|
+
* @param forceUpdate - whether to upsert the channel data anyway
|
|
15791
|
+
* @param createQueries - a callback function to creation of the queries that we want to execute
|
|
15792
|
+
*/
|
|
15596
15793
|
this.queriesWithChannelGuard = async ({
|
|
15597
15794
|
event,
|
|
15598
15795
|
execute = true,
|
|
@@ -15630,7 +15827,7 @@ var AbstractOfflineDB = class {
|
|
|
15630
15827
|
return newQueries;
|
|
15631
15828
|
} else {
|
|
15632
15829
|
console.warn(
|
|
15633
|
-
`
|
|
15830
|
+
`Couldn't create channel queries on ${type} event for an initialized channel that is not in DB, skipping event`,
|
|
15634
15831
|
{ event }
|
|
15635
15832
|
);
|
|
15636
15833
|
return [];
|
|
@@ -15645,8 +15842,14 @@ var AbstractOfflineDB = class {
|
|
|
15645
15842
|
}
|
|
15646
15843
|
return await createQueries(execute);
|
|
15647
15844
|
};
|
|
15648
|
-
|
|
15649
|
-
|
|
15845
|
+
/**
|
|
15846
|
+
* Handles a message.new event. Will always use a channel guard for the inner queries
|
|
15847
|
+
* and it is going to make sure that both messages and reads are upserted. It will not
|
|
15848
|
+
* try to fetch the reads from the DB first and it will rely on channel.state to handle
|
|
15849
|
+
* the number of unreads.
|
|
15850
|
+
* @param event - the WS event we are trying to process
|
|
15851
|
+
* @param execute - whether to immediately execute the operation.
|
|
15852
|
+
*/
|
|
15650
15853
|
this.handleNewMessage = async ({
|
|
15651
15854
|
event,
|
|
15652
15855
|
execute = true
|
|
@@ -15692,6 +15895,12 @@ var AbstractOfflineDB = class {
|
|
|
15692
15895
|
}
|
|
15693
15896
|
return finalQueries;
|
|
15694
15897
|
};
|
|
15898
|
+
/**
|
|
15899
|
+
* A handler for message deletion. It provides a channel guard and determines whether
|
|
15900
|
+
* it should hard delete or soft delete the message.
|
|
15901
|
+
* @param event - the WS event we are trying to process
|
|
15902
|
+
* @param execute - whether to immediately execute the operation.
|
|
15903
|
+
*/
|
|
15695
15904
|
this.handleDeleteMessage = async ({
|
|
15696
15905
|
event,
|
|
15697
15906
|
execute = true
|
|
@@ -15707,12 +15916,11 @@ var AbstractOfflineDB = class {
|
|
|
15707
15916
|
return [];
|
|
15708
15917
|
};
|
|
15709
15918
|
/**
|
|
15710
|
-
*
|
|
15711
|
-
*
|
|
15712
|
-
*
|
|
15713
|
-
*
|
|
15714
|
-
* @param
|
|
15715
|
-
* @param execute
|
|
15919
|
+
* A utility method used for removing a message that has already failed from the
|
|
15920
|
+
* state as well as the DB. We want to drop all pending tasks and finally hard
|
|
15921
|
+
* delete the message from the DB.
|
|
15922
|
+
* @param messageId - the message id of the message we want to remove
|
|
15923
|
+
* @param execute - whether to immediately execute the operation.
|
|
15716
15924
|
*/
|
|
15717
15925
|
this.handleRemoveMessage = async ({
|
|
15718
15926
|
messageId,
|
|
@@ -15732,6 +15940,16 @@ var AbstractOfflineDB = class {
|
|
|
15732
15940
|
}
|
|
15733
15941
|
return queries;
|
|
15734
15942
|
};
|
|
15943
|
+
/**
|
|
15944
|
+
* A utility method to handle read events. It will calculate the state of the reads if
|
|
15945
|
+
* present in the event, or optionally rely on the hard override in unreadMessages.
|
|
15946
|
+
* The unreadMessages argument is useful for cases where we know the exact number of unreads
|
|
15947
|
+
* (for example reading an entire channel), but `unread_messages` might not necessarily exist
|
|
15948
|
+
* in the event (or it exists with a stale value if we know what we want to ultimately update to).
|
|
15949
|
+
* @param event - the WS event we are trying to process
|
|
15950
|
+
* @param unreadMessages - an override of unread_messages that will be preferred when upserting reads
|
|
15951
|
+
* @param execute - whether to immediately execute the operation.
|
|
15952
|
+
*/
|
|
15735
15953
|
this.handleRead = async ({
|
|
15736
15954
|
event,
|
|
15737
15955
|
unreadMessages,
|
|
@@ -15764,6 +15982,13 @@ var AbstractOfflineDB = class {
|
|
|
15764
15982
|
}
|
|
15765
15983
|
return [];
|
|
15766
15984
|
};
|
|
15985
|
+
/**
|
|
15986
|
+
* A utility method used to handle member events. It guards the processing
|
|
15987
|
+
* of each event with a channel guard and also forces an update of member_count
|
|
15988
|
+
* for the respective channel if applicable.
|
|
15989
|
+
* @param event - the WS event we are trying to process
|
|
15990
|
+
* @param execute - whether to immediately execute the operation.
|
|
15991
|
+
*/
|
|
15767
15992
|
this.handleMemberEvent = async ({
|
|
15768
15993
|
event,
|
|
15769
15994
|
execute = true
|
|
@@ -15786,6 +16011,12 @@ var AbstractOfflineDB = class {
|
|
|
15786
16011
|
}
|
|
15787
16012
|
return [];
|
|
15788
16013
|
};
|
|
16014
|
+
/**
|
|
16015
|
+
* A utility method used to handle message.updated events. It guards each
|
|
16016
|
+
* event handler within a channel guard.
|
|
16017
|
+
* @param event - the WS event we are trying to process
|
|
16018
|
+
* @param execute - whether to immediately execute the operation.
|
|
16019
|
+
*/
|
|
15789
16020
|
this.handleMessageUpdatedEvent = async ({
|
|
15790
16021
|
event,
|
|
15791
16022
|
execute = true
|
|
@@ -15803,8 +16034,10 @@ var AbstractOfflineDB = class {
|
|
|
15803
16034
|
* An event handler for channel.visible and channel.hidden events. We need a separate
|
|
15804
16035
|
* handler because event.channel.hidden does not arrive with the baseline event, so a
|
|
15805
16036
|
* simple upsertion is not enough.
|
|
15806
|
-
*
|
|
15807
|
-
*
|
|
16037
|
+
* It will update the hidden property of a channel to true if handling the `channel.hidden`
|
|
16038
|
+
* event and to false if handling `channel.visible`.
|
|
16039
|
+
* @param event - the WS event we are trying to process
|
|
16040
|
+
* @param execute - whether to immediately execute the operation.
|
|
15808
16041
|
*/
|
|
15809
16042
|
this.handleChannelVisibilityEvent = async ({
|
|
15810
16043
|
event,
|
|
@@ -15820,6 +16053,13 @@ var AbstractOfflineDB = class {
|
|
|
15820
16053
|
}
|
|
15821
16054
|
return [];
|
|
15822
16055
|
};
|
|
16056
|
+
/**
|
|
16057
|
+
* A utility handler used to handle channel.truncated events. It handles both
|
|
16058
|
+
* removing all messages and relying on truncated_at as well. It will also upsert
|
|
16059
|
+
* reads adequately (and calculate the correct unread messages when truncating).
|
|
16060
|
+
* @param event - the WS event we are trying to process
|
|
16061
|
+
* @param execute - whether to immediately execute the operation.
|
|
16062
|
+
*/
|
|
15823
16063
|
this.handleChannelTruncatedEvent = async ({
|
|
15824
16064
|
event,
|
|
15825
16065
|
execute = true
|
|
@@ -15861,6 +16101,15 @@ var AbstractOfflineDB = class {
|
|
|
15861
16101
|
}
|
|
15862
16102
|
return [];
|
|
15863
16103
|
};
|
|
16104
|
+
/**
|
|
16105
|
+
* A utility handler for all reaction events. It wraps the inner queries
|
|
16106
|
+
* within a channel guard and maps them like so:
|
|
16107
|
+
* - reaction.new -> insertReaction
|
|
16108
|
+
* - reaction.updated -> updateReaction
|
|
16109
|
+
* - reaction.deleted -> deleteReaction
|
|
16110
|
+
* @param event - the WS event we are trying to process
|
|
16111
|
+
* @param execute - whether to immediately execute the operation.
|
|
16112
|
+
*/
|
|
15864
16113
|
this.handleReactionEvent = async ({
|
|
15865
16114
|
event,
|
|
15866
16115
|
execute = true
|
|
@@ -15889,6 +16138,13 @@ var AbstractOfflineDB = class {
|
|
|
15889
16138
|
(executeOverride) => reactionMethod({ message, reaction, execute: executeOverride })
|
|
15890
16139
|
);
|
|
15891
16140
|
};
|
|
16141
|
+
/**
|
|
16142
|
+
* A generic event handler that decides which DB API to invoke based on
|
|
16143
|
+
* event.type for all events we are currently handling. It is used to both
|
|
16144
|
+
* react on WS events as well as process the sync API events.
|
|
16145
|
+
* @param event - the WS event we are trying to process
|
|
16146
|
+
* @param execute - whether to immediately execute the operation.
|
|
16147
|
+
*/
|
|
15892
16148
|
this.handleEvent = async ({
|
|
15893
16149
|
event,
|
|
15894
16150
|
execute = true
|
|
@@ -15929,9 +16185,24 @@ var AbstractOfflineDB = class {
|
|
|
15929
16185
|
}
|
|
15930
16186
|
return [];
|
|
15931
16187
|
};
|
|
16188
|
+
/**
|
|
16189
|
+
* A method used to enqueue a pending task if the execution of it fails.
|
|
16190
|
+
* It will try to do the following:
|
|
16191
|
+
*
|
|
16192
|
+
* 1. Execute the task immediately
|
|
16193
|
+
* 2. If this fails, checks if the failure was due to something valid for a pending task
|
|
16194
|
+
* 3. If it is, it will insert the task in the pending tasks table
|
|
16195
|
+
*
|
|
16196
|
+
* It will return the response from the execution if it succeeded.
|
|
16197
|
+
* @param task - the pending task we want to execute
|
|
16198
|
+
*/
|
|
15932
16199
|
this.queueTask = async ({ task }) => {
|
|
15933
16200
|
let response;
|
|
15934
16201
|
try {
|
|
16202
|
+
if (!this.client.wsConnection?.isHealthy) {
|
|
16203
|
+
await this.addPendingTask(task);
|
|
16204
|
+
return;
|
|
16205
|
+
}
|
|
15935
16206
|
response = await this.executeTask({ task });
|
|
15936
16207
|
} catch (e) {
|
|
15937
16208
|
if (!this.shouldSkipQueueingTask(e)) {
|
|
@@ -15941,9 +16212,26 @@ var AbstractOfflineDB = class {
|
|
|
15941
16212
|
}
|
|
15942
16213
|
return response;
|
|
15943
16214
|
};
|
|
15944
|
-
|
|
15945
|
-
|
|
16215
|
+
/**
|
|
16216
|
+
* A utility method that determines if a failed task should be added to the
|
|
16217
|
+
* queue based on its error.
|
|
16218
|
+
* Error code 4 - bad request data
|
|
16219
|
+
* Error code 17 - missing own_capabilities to execute the task
|
|
16220
|
+
* @param error
|
|
16221
|
+
*/
|
|
15946
16222
|
this.shouldSkipQueueingTask = (error) => error?.response?.data?.code === 4 || error?.response?.data?.code === 17;
|
|
16223
|
+
/**
|
|
16224
|
+
* Executes a task from the list of supported pending tasks. Currently supported pending tasks
|
|
16225
|
+
* are:
|
|
16226
|
+
* - Deleting a message
|
|
16227
|
+
* - Sending a reaction
|
|
16228
|
+
* - Removing a reaction
|
|
16229
|
+
* - Sending a message
|
|
16230
|
+
* It will throw if we try to execute a pending task that is not supported.
|
|
16231
|
+
* @param task - The task we want to execute
|
|
16232
|
+
* @param isPendingTask - a control value telling us if it's an actual pending task being executed
|
|
16233
|
+
* or delayed execution
|
|
16234
|
+
*/
|
|
15947
16235
|
this.executeTask = async ({ task }, isPendingTask = false) => {
|
|
15948
16236
|
if (task.type === "delete-message") {
|
|
15949
16237
|
return await this.client._deleteMessage(...task.payload);
|
|
@@ -15951,9 +16239,6 @@ var AbstractOfflineDB = class {
|
|
|
15951
16239
|
const { channelType, channelId } = task;
|
|
15952
16240
|
if (channelType && channelId) {
|
|
15953
16241
|
const channel = this.client.channel(channelType, channelId);
|
|
15954
|
-
if (!channel.initialized) {
|
|
15955
|
-
await channel.watch();
|
|
15956
|
-
}
|
|
15957
16242
|
if (task.type === "send-reaction") {
|
|
15958
16243
|
return await channel._sendReaction(...task.payload);
|
|
15959
16244
|
}
|
|
@@ -15961,17 +16246,30 @@ var AbstractOfflineDB = class {
|
|
|
15961
16246
|
return await channel._deleteReaction(...task.payload);
|
|
15962
16247
|
}
|
|
15963
16248
|
if (task.type === "send-message") {
|
|
15964
|
-
const
|
|
15965
|
-
|
|
15966
|
-
|
|
16249
|
+
const newMessageResponse = await channel._sendMessage(...task.payload);
|
|
16250
|
+
const newMessage = newMessageResponse?.message;
|
|
16251
|
+
if (isPendingTask && newMessage) {
|
|
16252
|
+
if (newMessage?.parent_id) {
|
|
16253
|
+
this.client.threads.threadsById[newMessage.parent_id]?.upsertReplyLocally({
|
|
16254
|
+
message: newMessage,
|
|
16255
|
+
timestampChanged: true
|
|
16256
|
+
});
|
|
16257
|
+
}
|
|
16258
|
+
channel.state.addMessageSorted(newMessage, true);
|
|
15967
16259
|
}
|
|
15968
|
-
return
|
|
16260
|
+
return newMessageResponse;
|
|
15969
16261
|
}
|
|
15970
16262
|
}
|
|
15971
16263
|
throw new Error(
|
|
15972
16264
|
`Tried to execute invalid pending task type (${task.type}) while synchronizing the database.`
|
|
15973
16265
|
);
|
|
15974
16266
|
};
|
|
16267
|
+
/**
|
|
16268
|
+
* A utility method used to execute all pending tasks. As each task succeeds execution,
|
|
16269
|
+
* it is going to be removed from the DB. If the execution failed due to a valid reason
|
|
16270
|
+
* it is going to remove the pending task from the DB even if execution fails, otherwise
|
|
16271
|
+
* it will keep it for the next time we try to execute all pending taks.
|
|
16272
|
+
*/
|
|
15975
16273
|
this.executePendingTasks = async () => {
|
|
15976
16274
|
const queue = await this.getPendingTasks();
|
|
15977
16275
|
for (const task of queue) {
|
|
@@ -16003,133 +16301,15 @@ var AbstractOfflineDB = class {
|
|
|
16003
16301
|
userId: this.client.userID
|
|
16004
16302
|
});
|
|
16005
16303
|
}
|
|
16304
|
+
/**
|
|
16305
|
+
* Checks whether the DB should be initialized or if it has been initialized already.
|
|
16306
|
+
* @param {string} userId - the user ID for which we want to check initialization
|
|
16307
|
+
*/
|
|
16006
16308
|
shouldInitialize(userId) {
|
|
16007
16309
|
const { userId: userIdFromState, initialized } = this.state.getLatestValue();
|
|
16008
16310
|
return userId === userIdFromState && initialized;
|
|
16009
16311
|
}
|
|
16010
16312
|
};
|
|
16011
|
-
var OfflineDBSyncManager = class {
|
|
16012
|
-
constructor({
|
|
16013
|
-
client,
|
|
16014
|
-
offlineDb
|
|
16015
|
-
}) {
|
|
16016
|
-
this.syncStatus = false;
|
|
16017
|
-
this.connectionChangedListener = null;
|
|
16018
|
-
this.syncStatusListeners = [];
|
|
16019
|
-
this.scheduledSyncStatusCallbacks = /* @__PURE__ */ new Map();
|
|
16020
|
-
/**
|
|
16021
|
-
* Initializes the OfflineDBSyncManager. This function should be called only once
|
|
16022
|
-
* throughout the lifetime of SDK. If it is performed more than once for whatever
|
|
16023
|
-
* reason, it will run cleanup on its listeners to prevent memory leaks.
|
|
16024
|
-
*/
|
|
16025
|
-
this.init = async () => {
|
|
16026
|
-
try {
|
|
16027
|
-
if (this.client.user?.id && this.client.wsConnection?.isHealthy) {
|
|
16028
|
-
await this.syncAndExecutePendingTasks();
|
|
16029
|
-
await this.invokeSyncStatusListeners(true);
|
|
16030
|
-
}
|
|
16031
|
-
if (this.connectionChangedListener) {
|
|
16032
|
-
this.connectionChangedListener.unsubscribe();
|
|
16033
|
-
}
|
|
16034
|
-
this.connectionChangedListener = this.client.on(
|
|
16035
|
-
"connection.changed",
|
|
16036
|
-
async (event) => {
|
|
16037
|
-
if (event.online) {
|
|
16038
|
-
await this.syncAndExecutePendingTasks();
|
|
16039
|
-
await this.invokeSyncStatusListeners(true);
|
|
16040
|
-
} else {
|
|
16041
|
-
await this.invokeSyncStatusListeners(false);
|
|
16042
|
-
}
|
|
16043
|
-
}
|
|
16044
|
-
);
|
|
16045
|
-
} catch (error) {
|
|
16046
|
-
console.log("Error in DBSyncManager.init: ", error);
|
|
16047
|
-
}
|
|
16048
|
-
};
|
|
16049
|
-
/**
|
|
16050
|
-
* Subscribes a listener for sync status change.
|
|
16051
|
-
*
|
|
16052
|
-
* @param listener {function}
|
|
16053
|
-
* @returns {function} to unsubscribe the listener.
|
|
16054
|
-
*/
|
|
16055
|
-
this.onSyncStatusChange = (listener) => {
|
|
16056
|
-
this.syncStatusListeners.push(listener);
|
|
16057
|
-
return {
|
|
16058
|
-
unsubscribe: () => {
|
|
16059
|
-
this.syncStatusListeners = this.syncStatusListeners.filter(
|
|
16060
|
-
(el) => el !== listener
|
|
16061
|
-
);
|
|
16062
|
-
}
|
|
16063
|
-
};
|
|
16064
|
-
};
|
|
16065
|
-
this.scheduleSyncStatusChangeCallback = (tag, callback) => {
|
|
16066
|
-
this.scheduledSyncStatusCallbacks.set(tag, callback);
|
|
16067
|
-
};
|
|
16068
|
-
this.invokeSyncStatusListeners = async (status) => {
|
|
16069
|
-
this.syncStatus = status;
|
|
16070
|
-
this.syncStatusListeners.forEach((l) => l(status));
|
|
16071
|
-
if (status) {
|
|
16072
|
-
const promises = Array.from(this.scheduledSyncStatusCallbacks.values()).map(
|
|
16073
|
-
(cb) => cb()
|
|
16074
|
-
);
|
|
16075
|
-
await Promise.all(promises);
|
|
16076
|
-
this.scheduledSyncStatusCallbacks.clear();
|
|
16077
|
-
}
|
|
16078
|
-
};
|
|
16079
|
-
this.sync = async () => {
|
|
16080
|
-
if (!this.client?.user) {
|
|
16081
|
-
return;
|
|
16082
|
-
}
|
|
16083
|
-
const cids = await this.offlineDb.getAllChannelCids();
|
|
16084
|
-
if (cids.length === 0) {
|
|
16085
|
-
return;
|
|
16086
|
-
}
|
|
16087
|
-
console.log("MIDWAY MARK :D");
|
|
16088
|
-
const lastSyncedAt = await this.offlineDb.getLastSyncedAt({
|
|
16089
|
-
userId: this.client.user.id
|
|
16090
|
-
});
|
|
16091
|
-
console.log("LAST SYNC AT: ", lastSyncedAt);
|
|
16092
|
-
if (lastSyncedAt) {
|
|
16093
|
-
const lastSyncedAtDate = new Date(lastSyncedAt);
|
|
16094
|
-
const nowDate = /* @__PURE__ */ new Date();
|
|
16095
|
-
const diff = Math.floor(
|
|
16096
|
-
(nowDate.getTime() - lastSyncedAtDate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
16097
|
-
);
|
|
16098
|
-
console.log("DIFF: ", diff);
|
|
16099
|
-
if (diff > 30) {
|
|
16100
|
-
await this.offlineDb.resetDB();
|
|
16101
|
-
} else {
|
|
16102
|
-
try {
|
|
16103
|
-
console.log("ABOUT TO CALL SYNC API");
|
|
16104
|
-
const result = await this.client.sync(cids, lastSyncedAtDate.toISOString());
|
|
16105
|
-
console.log("CALLED SYNC API", result.events);
|
|
16106
|
-
const queryPromises = result.events.map(
|
|
16107
|
-
(event) => this.offlineDb.handleEvent({ event, execute: false })
|
|
16108
|
-
);
|
|
16109
|
-
const queriesArray = await Promise.all(queryPromises);
|
|
16110
|
-
const queries = queriesArray.flat();
|
|
16111
|
-
if (queries.length) {
|
|
16112
|
-
await this.offlineDb.executeSqlBatch(queries);
|
|
16113
|
-
}
|
|
16114
|
-
} catch (e) {
|
|
16115
|
-
console.log("An error has occurred while syncing the DB.", e);
|
|
16116
|
-
await this.offlineDb.resetDB();
|
|
16117
|
-
}
|
|
16118
|
-
}
|
|
16119
|
-
}
|
|
16120
|
-
await this.offlineDb.upsertUserSyncStatus({
|
|
16121
|
-
userId: this.client.user.id,
|
|
16122
|
-
lastSyncedAt: (/* @__PURE__ */ new Date()).toString()
|
|
16123
|
-
});
|
|
16124
|
-
};
|
|
16125
|
-
this.syncAndExecutePendingTasks = async () => {
|
|
16126
|
-
await this.offlineDb.executePendingTasks();
|
|
16127
|
-
await this.sync();
|
|
16128
|
-
};
|
|
16129
|
-
this.client = client;
|
|
16130
|
-
this.offlineDb = offlineDb;
|
|
16131
|
-
}
|
|
16132
|
-
};
|
|
16133
16313
|
|
|
16134
16314
|
// src/utils/FixedSizeQueueCache.ts
|
|
16135
16315
|
var FixedSizeQueueCache = class {
|