fix: merge the confSyncDumpJob into the confSyncJob

pull/2620/head
Audric Ackermann 2 years ago
parent 6fe6544d6c
commit 051c4bb262

@ -24,7 +24,6 @@ import { SessionWrapperModal } from '../SessionWrapperModal';
import { SessionButton, SessionButtonType } from '../basic/SessionButton';
import { SessionSpinner } from '../basic/SessionSpinner';
import { SessionIconButton } from '../icon';
import { ConfigurationDumpSync } from '../../session/utils/job_runners/jobs/ConfigurationSyncDumpJob';
const handleSaveQRCode = (event: MouseEvent) => {
event.preventDefault();
@ -361,7 +360,6 @@ async function commitProfileEdits(newName: string, scaledAvatarUrl: string | nul
if (window.sessionFeatureFlags.useSharedUtilForUserConfig) {
await ConfigurationSync.queueNewJobIfNeeded();
await ConfigurationDumpSync.queueNewJobIfNeeded();
await setLastProfileUpdateTimestamp(Date.now());
} else {
await setLastProfileUpdateTimestamp(Date.now());

@ -43,7 +43,6 @@ import { SessionUtilUserGroups } from '../session/utils/libsession/libsession_ut
import { leaveClosedGroup } from '../session/group/closed-group';
import { SessionUtilContact } from '../session/utils/libsession/libsession_utils_contacts';
import { SettingsKey } from '../data/settings-key';
import { ConfigurationDumpSync } from '../session/utils/job_runners/jobs/ConfigurationSyncDumpJob';
export function copyPublicKeyByConvoId(convoId: string) {
if (OpenGroupUtils.isOpenGroupV2(convoId)) {
@ -465,7 +464,6 @@ export async function uploadOurAvatar(newAvatarDecrypted?: ArrayBuffer) {
await setLastProfileUpdateTimestamp(Date.now());
if (window.sessionFeatureFlags.useSharedUtilForUserConfig) {
await ConfigurationSync.queueNewJobIfNeeded();
await ConfigurationDumpSync.queueNewJobIfNeeded();
} else {
await SyncUtils.forceSyncConfigurationNowIfNeeded(true);
}

@ -116,8 +116,6 @@ async function startJobRunners() {
runners.avatarDownloadRunner.startProcessing();
await runners.configurationSyncRunner.loadJobsFromDb();
runners.configurationSyncRunner.startProcessing();
await runners.configurationSyncDumpRunner.loadJobsFromDb();
runners.configurationSyncDumpRunner.startProcessing();
}
// We need this 'first' check because we don't want to start the app up any other time

@ -72,7 +72,6 @@ import {
MessageRequestResponseParams,
} from '../session/messages/outgoing/controlMessage/MessageRequestResponse';
import { ed25519Str } from '../session/onions/onionPath';
import { ConfigurationDumpSync } from '../session/utils/job_runners/jobs/ConfigurationSyncDumpJob';
import { ConfigurationSync } from '../session/utils/job_runners/jobs/ConfigurationSyncJob';
import { SessionUtilContact } from '../session/utils/libsession/libsession_utils_contacts';
import { SessionUtilConvoInfoVolatile } from '../session/utils/libsession/libsession_utils_convo_info_volatile';
@ -2284,7 +2283,6 @@ export async function commitConversationAndRefreshWrapper(id: string) {
if (Registration.isDone()) {
// save the new dump if needed to the DB asap
// this call throttled so we do not run this too often (and not for every .commit())
await ConfigurationDumpSync.queueNewJobIfNeeded();
await ConfigurationSync.queueNewJobIfNeeded();
}
convo.triggerUIRefresh();

@ -13,7 +13,6 @@ import { CONVERSATION_PRIORITIES, ConversationTypeEnum } from '../../models/conv
import { assertUnreachable } from '../../types/sqlSharedTypes';
import { UserGroupsWrapperActions } from '../../webworker/workers/browser/libsession_worker_interface';
import { leaveClosedGroup } from '../group/closed-group';
import { ConfigurationDumpSync } from '../utils/job_runners/jobs/ConfigurationSyncDumpJob';
import { ConfigurationSync } from '../utils/job_runners/jobs/ConfigurationSyncJob';
import { LibSessionUtil } from '../utils/libsession/libsession_utils';
import { SessionUtilContact } from '../utils/libsession/libsession_utils_contacts';
@ -299,7 +298,6 @@ export class ConversationController {
if (!fromSyncMessage) {
await ConfigurationSync.queueNewJobIfNeeded();
await ConfigurationDumpSync.queueNewJobIfNeeded();
}
}

@ -4,7 +4,6 @@ import {
FakeSleepForMultiJob,
} from '../../../test/session/unit/utils/job_runner/FakeSleepForJob';
import { AvatarDownload } from './jobs/AvatarDownloadJob';
import { ConfigurationDumpSync } from './jobs/ConfigurationSyncDumpJob';
import { ConfigurationSync } from './jobs/ConfigurationSyncJob';
import { PersistedJob, TypeOfPersistedData } from './PersistedJob';
@ -18,11 +17,6 @@ export function persistedJobFromData<T extends TypeOfPersistedData>(
switch (data.jobType) {
case 'ConfigurationSyncJobType':
return (new ConfigurationSync.ConfigurationSyncJob(data) as unknown) as PersistedJob<T>;
case 'ConfigurationSyncDumpJobType':
return (new ConfigurationDumpSync.ConfigurationSyncDumpJob(data) as unknown) as PersistedJob<
T
>;
case 'AvatarDownloadJobType':
return (new AvatarDownload.AvatarDownloadJob(data) as unknown) as PersistedJob<T>;
case 'FakeSleepForJobType':

@ -5,7 +5,6 @@ import { persistedJobFromData } from './JobDeserialization';
import { JobRunnerType } from './jobs/JobRunnerType';
import {
AvatarDownloadPersistedData,
ConfigurationSyncDumpPersistedData,
ConfigurationSyncPersistedData,
PersistedJob,
RunJobResult,
@ -369,13 +368,7 @@ const avatarDownloadRunner = new PersistedJobRunner<AvatarDownloadPersistedData>
null
);
const configurationSyncDumpRunner = new PersistedJobRunner<ConfigurationSyncDumpPersistedData>(
'ConfigurationSyncDumpJob',
null
);
export const runners = {
configurationSyncRunner,
configurationSyncDumpRunner,
avatarDownloadRunner,
};

@ -4,8 +4,7 @@ export type PersistedJobType =
| 'ConfigurationSyncJobType'
| 'AvatarDownloadJobType'
| 'FakeSleepForJobType'
| 'FakeSleepForJobMultiType'
| 'ConfigurationSyncDumpJobType';
| 'FakeSleepForJobMultiType';
interface PersistedJobData {
jobType: PersistedJobType;
@ -38,16 +37,11 @@ export interface ConfigurationSyncPersistedData extends PersistedJobData {
jobType: 'ConfigurationSyncJobType';
}
export interface ConfigurationSyncDumpPersistedData extends PersistedJobData {
jobType: 'ConfigurationSyncDumpJobType';
}
export type TypeOfPersistedData =
| ConfigurationSyncPersistedData
| AvatarDownloadPersistedData
| FakeSleepJobData
| FakeSleepForMultiJobData
| ConfigurationSyncDumpPersistedData;
| FakeSleepForMultiJobData;
export type AddJobCheckReturn =
| 'skipAddSameJobPresent'

@ -1,195 +0,0 @@
import { isNumber } from 'lodash';
import { v4 } from 'uuid';
import { UserUtils } from '../..';
import { ConfigDumpData } from '../../../../data/configDump/configDump';
import { assertUnreachable } from '../../../../types/sqlSharedTypes';
import { GenericWrapperActions } from '../../../../webworker/workers/browser/libsession_worker_interface';
import { DURATION } from '../../../constants';
import { getConversationController } from '../../../conversations';
import { LibSessionUtil } from '../../libsession/libsession_utils';
import { runners } from '../JobRunner';
import {
AddJobCheckReturn,
ConfigurationSyncDumpPersistedData,
ConfigurationSyncPersistedData,
PersistedJob,
RunJobResult,
} from '../PersistedJob';
import { SessionUtilUserProfile } from '../../libsession/libsession_utils_user_profile';
import { SessionUtilContact } from '../../libsession/libsession_utils_contacts';
import { SessionUtilUserGroups } from '../../libsession/libsession_utils_user_groups';
import { SessionUtilConvoInfoVolatile } from '../../libsession/libsession_utils_convo_info_volatile';
const defaultMsBetweenRetries = DURATION.SECONDS * 5;
const defaultMaxAttempts = 2;
/**
* We want to run each of those jobs at least 3seconds apart.
* So every time one of that job finishes, update this timestamp, so we know when adding a new job, what is the next minimun date to run it.
*/
let lastRunConfigSyncJobDumpTimestamp: number | null = null;
async function saveDumpsNeededToDB(): Promise<boolean> {
let savedAtLeastOne = false;
for (let i = 0; i < LibSessionUtil.requiredUserVariants.length; i++) {
const variant = LibSessionUtil.requiredUserVariants[i];
const needsDump = await GenericWrapperActions.needsDump(variant);
if (!needsDump) {
continue;
}
const dump = await GenericWrapperActions.dump(variant);
await ConfigDumpData.saveConfigDump({
data: dump,
publicKey: UserUtils.getOurPubKeyStrFromCache(),
variant,
});
savedAtLeastOne = true;
}
return savedAtLeastOne;
}
/**
* We have the `ConfigurationSyncDumpJob` and the `ConfigurationSyncJob`.
* The `ConfigurationSyncDumpJob` involves network request which is slow and might not be available at all, hence it can run only from time to time.
* But when we do changes to the wrapper, we need to dump it to the database in the event of an app crash or whatever.
* To it less likely to loose data, we have a lightweight job which can be run a lot more frequently, the `ConfigurationSyncDumpJob`.
* It just grabs all the wrappers which needs to be stored in the DB, override the data in the wrapper with what is in the database and save the result (if `needsDump` is true) to the corresponding database wrapper.
*
*/
class ConfigurationSyncDumpJob extends PersistedJob<ConfigurationSyncDumpPersistedData> {
constructor({
identifier,
nextAttemptTimestamp,
maxAttempts,
currentRetry,
}: Partial<
Pick<
ConfigurationSyncPersistedData,
'identifier' | 'nextAttemptTimestamp' | 'currentRetry' | 'maxAttempts'
>
>) {
super({
jobType: 'ConfigurationSyncDumpJobType',
identifier: identifier || v4(),
delayBetweenRetries: defaultMsBetweenRetries,
maxAttempts: isNumber(maxAttempts) ? maxAttempts : defaultMaxAttempts,
currentRetry: isNumber(currentRetry) ? currentRetry : 0,
nextAttemptTimestamp: nextAttemptTimestamp || Date.now(),
});
}
public async run(): Promise<RunJobResult> {
const start = Date.now();
try {
if (!window.sessionFeatureFlags.useSharedUtilForUserConfig) {
return RunJobResult.Success;
}
window.log.debug(`ConfigurationSyncDumpJob starting ${this.persistedData.identifier}`);
const us = UserUtils.getOurPubKeyStrFromCache();
const ed25519Key = await UserUtils.getUserED25519KeyPairBytes();
const conversation = getConversationController().get(us);
if (!us || !conversation || !ed25519Key) {
// we check for ed25519Key because it is needed for authenticated requests
window.log.warn('did not find our own conversation');
return RunJobResult.PermanentFailure;
}
// refresh all the data stored by the wrappers we need to store.
// so when we call needsDump(), we know for sure that we are up to date
// TODOLATER we need to add the dump of the wrappers of other destination than ourself once we had the closed group handling of config sync job
// I think dumping should not fetch data from the DB again, but instead just use the data already in the wrapper.
for (let index = 0; index < LibSessionUtil.requiredUserVariants.length; index++) {
const variant = LibSessionUtil.requiredUserVariants[index];
switch (variant) {
case 'UserConfig':
await SessionUtilUserProfile.insertUserProfileIntoWrapper(us);
break;
case 'ContactsConfig':
await SessionUtilContact.insertAllContactsIntoContactsWrapper();
break;
case 'UserGroupsConfig':
await SessionUtilUserGroups.insertAllUserGroupsIntoWrapper();
break;
case 'ConvoInfoVolatileConfig':
await SessionUtilConvoInfoVolatile.insertAllConvoInfoVolatileIntoWrapper();
break;
default:
assertUnreachable(variant, `ConfigurationSyncDumpJob unhandled variant: "${variant}"`);
}
}
await saveDumpsNeededToDB();
return RunJobResult.Success;
} catch (e) {
throw e;
} finally {
// this is a simple way to make sure whatever happens here, we update the lastest timestamp.
// (a finally statement is always executed (no matter if exception or returns in other try/catch block)
this.updateLastTickTimestamp();
window.log.debug(`ConfigurationSyncDumpJob run() took ${Date.now() - start}ms`);
}
}
public serializeJob(): ConfigurationSyncDumpPersistedData {
const fromParent = super.serializeBase();
return fromParent;
}
public addJobCheck(jobs: Array<ConfigurationSyncDumpPersistedData>): AddJobCheckReturn {
return this.addJobCheckSameTypePresent(jobs);
}
/**
* For the SharedConfig job, we do not care about the jobs already in the list.
* We never want to add a new sync configuration job if there is already one in the queue.
* This is done by the `addJobCheck` method above
*/
public nonRunningJobsToRemove(_jobs: Array<ConfigurationSyncDumpPersistedData>) {
return [];
}
public getJobTimeoutMs(): number {
return DURATION.SECONDS * 2;
}
private updateLastTickTimestamp() {
lastRunConfigSyncJobDumpTimestamp = Date.now();
}
}
/**
* Queue a new Sync Configuration if needed job.
* A ConfigurationSyncJob can only be added if there is none of the same type queued already.
*/
async function queueNewJobIfNeeded() {
if (!window.sessionFeatureFlags.useSharedUtilForUserConfig) {
return;
}
if (
!lastRunConfigSyncJobDumpTimestamp ||
lastRunConfigSyncJobDumpTimestamp < Date.now() - defaultMsBetweenRetries
) {
window.log.debug('Scheduling ConfDumpJob: ASAP');
// this call will make sure that there is only one configuration sync job at all times
await runners.configurationSyncDumpRunner.addJob(
new ConfigurationSyncDumpJob({ nextAttemptTimestamp: Date.now() + 1000 }) // we postpone by 1000ms to make sure whoever is adding this job is done with what is needs to do first
);
} else {
// if we did run at 100, and it is currently 110, diff is 10
const diff = Math.max(Date.now() - lastRunConfigSyncJobDumpTimestamp, 0);
// but we want to run every 30, so what we need is actually `30-10` from now = 20
const leftBeforeNextTick = Math.max(defaultMsBetweenRetries - diff, 0);
window.log.debug(`Scheduling ConfDumpJob: in ${leftBeforeNextTick} ms`);
await runners.configurationSyncDumpRunner.addJob(
new ConfigurationSyncDumpJob({ nextAttemptTimestamp: Date.now() + leftBeforeNextTick })
);
}
}
export const ConfigurationDumpSync = {
ConfigurationSyncDumpJob,
queueNewJobIfNeeded,
};

@ -3,7 +3,6 @@ import { v4 } from 'uuid';
import { UserUtils } from '../..';
import { ConfigDumpData } from '../../../../data/configDump/configDump';
import { ConfigurationSyncJobDone } from '../../../../shims/events';
import { assertUnreachable } from '../../../../types/sqlSharedTypes';
import { GenericWrapperActions } from '../../../../webworker/workers/browser/libsession_worker_interface';
import { NotEmptyArrayOfBatchResults } from '../../../apis/snode_api/SnodeRequestTypes';
import { getConversationController } from '../../../conversations';
@ -17,10 +16,6 @@ import {
PersistedJob,
RunJobResult,
} from '../PersistedJob';
import { SessionUtilUserProfile } from '../../libsession/libsession_utils_user_profile';
import { SessionUtilContact } from '../../libsession/libsession_utils_contacts';
import { SessionUtilUserGroups } from '../../libsession/libsession_utils_user_groups';
import { SessionUtilConvoInfoVolatile } from '../../libsession/libsession_utils_convo_info_volatile';
const defaultMsBetweenRetries = 3000;
const defaultMaxAttempts = 3;
@ -128,6 +123,23 @@ async function buildAndSaveDumpsToDB(
}
}
async function saveDumpsNeededToDB(destination: string) {
for (let i = 0; i < LibSessionUtil.requiredUserVariants.length; i++) {
const variant = LibSessionUtil.requiredUserVariants[i];
const needsDump = await GenericWrapperActions.needsDump(variant);
if (!needsDump) {
continue;
}
const dump = await GenericWrapperActions.dump(variant);
await ConfigDumpData.saveConfigDump({
data: dump,
publicKey: destination,
variant,
});
}
}
class ConfigurationSyncJob extends PersistedJob<ConfigurationSyncPersistedData> {
constructor({
identifier,
@ -154,10 +166,6 @@ class ConfigurationSyncJob extends PersistedJob<ConfigurationSyncPersistedData>
const start = Date.now();
try {
if (!window.sessionFeatureFlags.useSharedUtilForUserConfig) {
this.triggerConfSyncJobDone();
return RunJobResult.Success;
}
window.log.debug(`ConfigurationSyncJob starting ${this.persistedData.identifier}`);
const us = UserUtils.getOurPubKeyStrFromCache();
@ -169,29 +177,17 @@ class ConfigurationSyncJob extends PersistedJob<ConfigurationSyncPersistedData>
return RunJobResult.PermanentFailure;
}
for (let index = 0; index < LibSessionUtil.requiredUserVariants.length; index++) {
const variant = LibSessionUtil.requiredUserVariants[index];
switch (variant) {
case 'UserConfig':
await SessionUtilUserProfile.insertUserProfileIntoWrapper(us);
break;
case 'ContactsConfig':
await SessionUtilContact.insertAllContactsIntoContactsWrapper();
break;
case 'UserGroupsConfig':
await SessionUtilUserGroups.insertAllUserGroupsIntoWrapper();
break;
case 'ConvoInfoVolatileConfig':
await SessionUtilConvoInfoVolatile.insertAllConvoInfoVolatileIntoWrapper();
break;
default:
assertUnreachable(variant, `ConfigurationSyncDumpJob unhandled variant: "${variant}"`);
}
}
// TODOLATER add a way to have a few configuration sync jobs running at the same time, but only a single one per pubkey
const thisJobDestination = us;
// save the dumps to DB even before trying to push them, so at least we have an up to date dumps in the DB in case of crash, no network etc
await saveDumpsNeededToDB(thisJobDestination);
// if the feature flag is not enabled, we want to keep updating the dumps, but just not sync them.
if (!window.sessionFeatureFlags.useSharedUtilForUserConfig) {
this.triggerConfSyncJobDone();
return RunJobResult.Success;
}
const singleDestChanges = await retrieveSingleDestinationChanges(thisJobDestination);
// If there are no pending changes then the job can just complete (next time something

@ -19,30 +19,10 @@ import { PubKey } from '../../types';
* Also, to make sure that our wrapper is up to date, we schedule jobs to be run and fetch all contacts and update all the wrappers entries.
* This is done in the
* - `ConfigurationSyncJob` (sending data to the network) and the
* - `ConfigurationSyncDumpJob` (just dumping locally the data)
* with `insertAllContactsIntoContactsWrapper()`
*
*/
const mappedContactWrapperValues = new Map<string, ContactInfo>();
/**
* Update the ContactWrapper with all the data is cares about from the database.
*/
async function insertAllContactsIntoContactsWrapper() {
const idsToInsert = getConversationController()
.getConversations()
.filter(isContactToStoreInWrapper)
.map(m => m.id);
window.log.debug(`ContactsWrapper keep tracks of ${idsToInsert.length} contacts: ${idsToInsert}`);
for (let index = 0; index < idsToInsert.length; index++) {
const id = idsToInsert[index];
await insertContactFromDBIntoWrapperAndRefresh(id);
}
}
/**
* Returns true if that conversation is not us, is private, is not blinded.
*
@ -147,7 +127,6 @@ async function removeContactFromWrapper(id: string) {
}
export const SessionUtilContact = {
isContactToStoreInWrapper,
insertAllContactsIntoContactsWrapper,
insertContactFromDBIntoWrapperAndRefresh,
removeContactFromWrapper,
getContactCached,

@ -1,5 +1,5 @@
import { isEmpty, uniq } from 'lodash';
import { BaseConvoInfoVolatile, ConvoVolatileType } from 'libsession_util_nodejs';
import { isEmpty } from 'lodash';
import { Data } from '../../../data/data';
import { OpenGroupData } from '../../../data/opengroups';
import { ConversationModel } from '../../../models/conversation';
@ -29,28 +29,6 @@ const mappedLegacyGroupWrapperValues = new Map<string, BaseConvoInfoVolatile>();
*/
const mappedCommunityWrapperValues = new Map<string, BaseConvoInfoVolatile>();
/**
* Update the ConvoInfoVolatileWrapper with all the data is cares about from the database.
*/
async function insertAllConvoInfoVolatileIntoWrapper() {
const convoIdsToInsert = uniq(
getConversationController()
.getConversations()
.filter(isConvoToStoreInWrapper)
.map(m => m.id)
);
window.log.debug(
`ConvoInfoVolatileWrapper keep tracks of ${convoIdsToInsert.length} convos in total.`
);
for (let index = 0; index < convoIdsToInsert.length; index++) {
const id = convoIdsToInsert[index];
await insertConvoFromDBIntoWrapperAndRefresh(id);
}
}
/**
* Returns true if that conversation should be stored in the conversation volatile info wrapper.
* It actually relies on the two other wrappers to know what to store:
@ -278,7 +256,6 @@ function getConvoInfoVolatileTypes(): Array<ConvoVolatileType> {
export const SessionUtilConvoInfoVolatile = {
// shared
isConvoToStoreInWrapper,
insertAllConvoInfoVolatileIntoWrapper,
insertConvoFromDBIntoWrapperAndRefresh,
refreshConvoVolatileCached,
getConvoInfoVolatileTypes,

@ -1,4 +1,3 @@
import { uniq } from 'lodash';
import { CommunityInfo, LegacyGroupInfo, UserGroupsType } from 'libsession_util_nodejs';
import { Data } from '../../../data/data';
import { OpenGroupData } from '../../../data/opengroups';
@ -22,28 +21,6 @@ const mappedCommunityWrapperValues = new Map<string, CommunityInfo>();
*/
const mappedLegacyGroupWrapperValues = new Map<string, LegacyGroupInfo>();
/**
* Update the UserGroupsWrapper with all the data is cares about from the database.
*/
async function insertAllUserGroupsIntoWrapper() {
const convoIdsToInsert = uniq(
getConversationController()
.getConversations()
.filter(isUserGroupToStoreInWrapper)
.map(m => m.id)
);
window.log.debug(
`UserGroupsWrapper keep tracks of ${convoIdsToInsert.length} usergroups including groups and communities`
);
for (let index = 0; index < convoIdsToInsert.length; index++) {
const id = convoIdsToInsert[index];
await insertGroupsFromDBIntoWrapperAndRefresh(id);
}
}
/**
* Returns true if that conversation is an active group
*/
@ -268,7 +245,6 @@ function getUserGroupTypes(): Array<UserGroupsType> {
export const SessionUtilUserGroups = {
// shared
isUserGroupToStoreInWrapper,
insertAllUserGroupsIntoWrapper,
insertGroupsFromDBIntoWrapperAndRefresh,
refreshCachedUserGroup,
getUserGroupTypes,

@ -27,7 +27,6 @@ import {
VisibleMessage,
} from '../../messages/outgoing/visibleMessage/VisibleMessage';
import { PubKey } from '../../types';
import { ConfigurationDumpSync } from '../job_runners/jobs/ConfigurationSyncDumpJob';
import { ConfigurationSync } from '../job_runners/jobs/ConfigurationSyncJob';
import { fromBase64ToArray, fromHexToArray } from '../String';
import { getCompleteUrlFromRoom } from '../../apis/open_group_api/utils/OpenGroupUtils';
@ -72,9 +71,7 @@ export const syncConfigurationIfNeeded = async () => {
}
await writeLastSyncTimestampToDb(now);
} else {
await ConfigurationDumpSync.queueNewJobIfNeeded();
await ConfigurationSync.queueNewJobIfNeeded();
await ConfigurationDumpSync.queueNewJobIfNeeded();
}
};
@ -87,14 +84,12 @@ export const forceSyncConfigurationNowIfNeeded = async (waitForMessageSent = fal
}, 20000);
if (window.sessionFeatureFlags.useSharedUtilForUserConfig) {
void ConfigurationDumpSync.queueNewJobIfNeeded()
.then(ConfigurationSync.queueNewJobIfNeeded)
.catch(e => {
window.log.warn(
'forceSyncConfigurationNowIfNeeded scheduling of jobs failed with',
e.message
);
});
void ConfigurationSync.queueNewJobIfNeeded().catch(e => {
window.log.warn(
'forceSyncConfigurationNowIfNeeded scheduling of jobs failed with',
e.message
);
});
if (waitForMessageSent) {
window.Whisper.events.once(ConfigurationSyncJobDone, () => {
resolve(true);

Loading…
Cancel
Save