Change order of syncs during linking
This commit is contained in:
parent
4f869e7900
commit
3b4106d9dd
5 changed files with 142 additions and 90 deletions
150
ts/background.ts
150
ts/background.ts
|
@ -2,7 +2,7 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { webFrame } from 'electron';
|
||||
import { isNumber, noop } from 'lodash';
|
||||
import { isNumber } from 'lodash';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { render } from 'react-dom';
|
||||
import { batch as batchDispatch } from 'react-redux';
|
||||
|
@ -13,7 +13,7 @@ import type {
|
|||
ProcessedDataMessage,
|
||||
} from './textsecure/Types.d';
|
||||
import { HTTPError } from './textsecure/Errors';
|
||||
import {
|
||||
import createTaskWithTimeout, {
|
||||
suspendTasksWithTimeout,
|
||||
resumeTasksWithTimeout,
|
||||
} from './textsecure/TaskWithTimeout';
|
||||
|
@ -34,7 +34,6 @@ import * as durations from './util/durations';
|
|||
import { explodePromise } from './util/explodePromise';
|
||||
import { isWindowDragElement } from './util/isWindowDragElement';
|
||||
import { assert, strictAssert } from './util/assert';
|
||||
import { dropNull } from './util/dropNull';
|
||||
import { normalizeUuid } from './util/normalizeUuid';
|
||||
import { filter } from './util/iterables';
|
||||
import { isNotNil } from './util/isNotNil';
|
||||
|
@ -80,11 +79,11 @@ import type {
|
|||
SentEventData,
|
||||
StickerPackEvent,
|
||||
TypingEvent,
|
||||
VerifiedEvent,
|
||||
ViewEvent,
|
||||
ViewOnceOpenSyncEvent,
|
||||
ViewSyncEvent,
|
||||
} from './textsecure/messageReceiverEvents';
|
||||
import { VerifiedEvent } from './textsecure/messageReceiverEvents';
|
||||
import type { WebAPIType } from './textsecure/WebAPI';
|
||||
import * as KeyChangeListener from './textsecure/KeyChangeListener';
|
||||
import { RotateSignedPreKeyListener } from './textsecure/RotateSignedPreKeyListener';
|
||||
|
@ -1709,12 +1708,6 @@ export async function startApp(): Promise<void> {
|
|||
window.reduxActions.app.openInstaller();
|
||||
}
|
||||
|
||||
window.Whisper.events.on('contactsync', () => {
|
||||
if (window.reduxStore.getState().app.appView === AppViewType.Installer) {
|
||||
window.reduxActions.app.openInbox();
|
||||
}
|
||||
});
|
||||
|
||||
window.registerForActive(() => notificationService.clear());
|
||||
window.addEventListener('unload', () => notificationService.fastClear());
|
||||
|
||||
|
@ -2082,6 +2075,7 @@ export async function startApp(): Promise<void> {
|
|||
}
|
||||
|
||||
if (firstRun === true && deviceId !== 1) {
|
||||
const { messaging } = window.textsecure;
|
||||
const hasThemeSetting = Boolean(window.storage.get('theme-setting'));
|
||||
if (
|
||||
!hasThemeSetting &&
|
||||
|
@ -2093,19 +2087,71 @@ export async function startApp(): Promise<void> {
|
|||
);
|
||||
themeChanged();
|
||||
}
|
||||
const syncRequest = window.getSyncRequest();
|
||||
window.Whisper.events.trigger('contactsync:begin');
|
||||
syncRequest.addEventListener('success', () => {
|
||||
log.info('sync successful');
|
||||
window.storage.put('synced_at', Date.now());
|
||||
window.Whisper.events.trigger('contactsync');
|
||||
runStorageService();
|
||||
});
|
||||
syncRequest.addEventListener('timeout', () => {
|
||||
log.error('sync timed out');
|
||||
window.Whisper.events.trigger('contactsync');
|
||||
runStorageService();
|
||||
});
|
||||
|
||||
const waitForEvent = createTaskWithTimeout(
|
||||
(event: string): Promise<void> => {
|
||||
const { promise, resolve } = explodePromise<void>();
|
||||
window.Whisper.events.once(event, () => resolve());
|
||||
return promise;
|
||||
},
|
||||
'firstRun:waitForEvent'
|
||||
);
|
||||
|
||||
let storageServiceSyncComplete: Promise<void>;
|
||||
if (window.ConversationController.areWePrimaryDevice()) {
|
||||
storageServiceSyncComplete = Promise.resolve();
|
||||
} else {
|
||||
storageServiceSyncComplete = waitForEvent(
|
||||
'storageService:syncComplete'
|
||||
);
|
||||
}
|
||||
|
||||
const contactSyncComplete = waitForEvent('contactSync:complete');
|
||||
|
||||
log.info('firstRun: requesting initial sync');
|
||||
|
||||
// Request configuration, block, GV1 sync messages, contacts
|
||||
// (only avatars and inboxPosition),and Storage Service sync.
|
||||
try {
|
||||
await Promise.all([
|
||||
singleProtoJobQueue.add(
|
||||
messaging.getRequestConfigurationSyncMessage()
|
||||
),
|
||||
singleProtoJobQueue.add(messaging.getRequestBlockSyncMessage()),
|
||||
singleProtoJobQueue.add(messaging.getRequestGroupSyncMessage()),
|
||||
singleProtoJobQueue.add(messaging.getRequestContactSyncMessage()),
|
||||
runStorageService(),
|
||||
]);
|
||||
} catch (error) {
|
||||
log.error(
|
||||
'connect: Failed to request initial syncs',
|
||||
Errors.toLogFormat(error)
|
||||
);
|
||||
}
|
||||
|
||||
log.info('firstRun: waiting for storage service and contact sync');
|
||||
|
||||
try {
|
||||
await Promise.all([storageServiceSyncComplete, contactSyncComplete]);
|
||||
} catch (error) {
|
||||
log.error(
|
||||
'connect: Failed to run storage service and contact syncs',
|
||||
Errors.toLogFormat(error)
|
||||
);
|
||||
}
|
||||
|
||||
log.info('firstRun: disabling post link experience');
|
||||
window.Signal.Util.postLinkExperience.stop();
|
||||
|
||||
// Switch to inbox view even if contact sync is still running
|
||||
if (
|
||||
window.reduxStore.getState().app.appView === AppViewType.Installer
|
||||
) {
|
||||
log.info('firstRun: opening inbox');
|
||||
window.reduxActions.app.openInbox();
|
||||
} else {
|
||||
log.info('firstRun: not opening inbox');
|
||||
}
|
||||
|
||||
const installedStickerPacks = Stickers.getInstalledStickerPacks();
|
||||
if (installedStickerPacks.length) {
|
||||
|
@ -2122,9 +2168,10 @@ export async function startApp(): Promise<void> {
|
|||
return;
|
||||
}
|
||||
|
||||
log.info('firstRun: requesting stickers', operations.length);
|
||||
try {
|
||||
await singleProtoJobQueue.add(
|
||||
window.textsecure.messaging.getStickerPackSync(operations)
|
||||
messaging.getStickerPackSync(operations)
|
||||
);
|
||||
} catch (error) {
|
||||
log.error(
|
||||
|
@ -2134,16 +2181,7 @@ export async function startApp(): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await singleProtoJobQueue.add(
|
||||
window.textsecure.messaging.getRequestKeySyncMessage()
|
||||
);
|
||||
} catch (error) {
|
||||
log.error(
|
||||
'Failed to queue request key sync message',
|
||||
Errors.toLogFormat(error)
|
||||
);
|
||||
}
|
||||
log.info('firstRun: done');
|
||||
}
|
||||
|
||||
window.storage.onready(async () => {
|
||||
|
@ -2486,24 +2524,12 @@ export async function startApp(): Promise<void> {
|
|||
async function onContactSyncComplete() {
|
||||
log.info('onContactSyncComplete');
|
||||
await window.storage.put('synced_at', Date.now());
|
||||
window.Whisper.events.trigger('contactSync:complete');
|
||||
}
|
||||
|
||||
async function onContactReceived(ev: ContactEvent) {
|
||||
const details = ev.contactDetails;
|
||||
|
||||
if (
|
||||
(details.number &&
|
||||
details.number === window.textsecure.storage.user.getNumber()) ||
|
||||
(details.uuid &&
|
||||
details.uuid === window.textsecure.storage.user.getUuid()?.toString())
|
||||
) {
|
||||
// special case for syncing details about ourselves
|
||||
if (details.profileKey) {
|
||||
log.info('Got sync message with our own profile key');
|
||||
ourProfileKeyService.set(details.profileKey);
|
||||
}
|
||||
}
|
||||
|
||||
const c = new window.Whisper.Conversation({
|
||||
e164: details.number,
|
||||
uuid: details.uuid,
|
||||
|
@ -2528,19 +2554,6 @@ export async function startApp(): Promise<void> {
|
|||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const conversation = window.ConversationController.get(detailsId)!;
|
||||
|
||||
if (details.profileKey && details.profileKey.length > 0) {
|
||||
const profileKey = Bytes.toBase64(details.profileKey);
|
||||
conversation.setProfileKey(profileKey);
|
||||
}
|
||||
|
||||
if (typeof details.blocked !== 'undefined') {
|
||||
if (details.blocked) {
|
||||
conversation.block();
|
||||
} else {
|
||||
conversation.unblock();
|
||||
}
|
||||
}
|
||||
|
||||
conversation.set({
|
||||
name: details.name,
|
||||
inbox_position: details.inboxPosition,
|
||||
|
@ -2569,6 +2582,8 @@ export async function startApp(): Promise<void> {
|
|||
|
||||
window.Signal.Data.updateConversation(conversation.attributes);
|
||||
|
||||
// expireTimer isn't stored in Storage Service so we have to rely on the
|
||||
// contact sync.
|
||||
const { expireTimer } = details;
|
||||
const isValidExpireTimer = typeof expireTimer === 'number';
|
||||
if (isValidExpireTimer) {
|
||||
|
@ -2585,21 +2600,6 @@ export async function startApp(): Promise<void> {
|
|||
);
|
||||
}
|
||||
|
||||
if (details.verified) {
|
||||
const { verified } = details;
|
||||
const verifiedEvent = new VerifiedEvent(
|
||||
{
|
||||
state: dropNull(verified.state),
|
||||
destination: dropNull(verified.destination),
|
||||
destinationUuid: dropNull(verified.destinationUuid),
|
||||
identityKey: dropNull(verified.identityKey),
|
||||
viaContactSync: true,
|
||||
},
|
||||
noop
|
||||
);
|
||||
await onVerified(verifiedEvent);
|
||||
}
|
||||
|
||||
if (window.Signal.Util.postLinkExperience.isActive()) {
|
||||
log.info(
|
||||
'onContactReceived: Adding the message history disclaimer on link'
|
||||
|
|
|
@ -1312,7 +1312,6 @@ async function sync(
|
|||
);
|
||||
}
|
||||
|
||||
window.Signal.Util.postLinkExperience.stop();
|
||||
log.info('storageService.sync: complete');
|
||||
return manifest;
|
||||
}
|
||||
|
@ -1453,6 +1452,9 @@ export const runStorageServiceSyncJob = debounce(() => {
|
|||
ourProfileKeyService.blockGetWithPromise(
|
||||
storageJobQueue(async () => {
|
||||
await sync();
|
||||
|
||||
// Notify listeners about sync completion
|
||||
window.Whisper.events.trigger('storageService:syncComplete');
|
||||
}, `sync v${window.storage.get('manifestVersion')}`)
|
||||
);
|
||||
}, 500);
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
waitThenRespondToGroupV2Migration,
|
||||
} from '../groups';
|
||||
import { assert } from '../util/assert';
|
||||
import { dropNull } from '../util/dropNull';
|
||||
import { normalizeUuid } from '../util/normalizeUuid';
|
||||
import { missingCaseError } from '../util/missingCaseError';
|
||||
import {
|
||||
|
@ -768,8 +769,8 @@ export async function mergeContactRecord(
|
|||
: undefined,
|
||||
};
|
||||
|
||||
const e164 = contactRecord.serviceE164 || undefined;
|
||||
const uuid = contactRecord.serviceUuid || undefined;
|
||||
const e164 = dropNull(contactRecord.serviceE164);
|
||||
const uuid = dropNull(contactRecord.serviceUuid);
|
||||
|
||||
// All contacts must have UUID
|
||||
if (!uuid) {
|
||||
|
@ -802,6 +803,25 @@ export async function mergeContactRecord(
|
|||
});
|
||||
}
|
||||
|
||||
const remoteName = dropNull(contactRecord.givenName);
|
||||
const remoteFamilyName = dropNull(contactRecord.familyName);
|
||||
const localName = conversation.get('profileName');
|
||||
const localFamilyName = conversation.get('profileFamilyName');
|
||||
if (
|
||||
remoteName &&
|
||||
(localName !== remoteName || localFamilyName !== remoteFamilyName)
|
||||
) {
|
||||
// Local name doesn't match remote name, fetch profile
|
||||
if (localName) {
|
||||
conversation.getProfiles();
|
||||
} else {
|
||||
conversation.set({
|
||||
profileName: remoteName,
|
||||
profileFamilyName: remoteFamilyName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const verified = await conversation.safeGetVerified();
|
||||
const storageServiceVerified = contactRecord.identityState || 0;
|
||||
if (verified !== storageServiceVerified) {
|
||||
|
@ -1065,6 +1085,16 @@ export async function mergeAccountRecord(
|
|||
|
||||
remotelyPinnedConversations.forEach(conversation => {
|
||||
conversation.set({ isPinned: true, isArchived: false });
|
||||
|
||||
if (
|
||||
window.Signal.Util.postLinkExperience.isActive() &&
|
||||
isGroupV2(conversation.attributes)
|
||||
) {
|
||||
log.info(
|
||||
'mergeAccountRecord: Adding the message history disclaimer on link'
|
||||
);
|
||||
conversation.addMessageHistoryDisclaimer();
|
||||
}
|
||||
updateConversation(conversation.attributes);
|
||||
});
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ describe('storage service', function needsName() {
|
|||
it('should handle message request state changes', async () => {
|
||||
const { phone, desktop, server } = bootstrap;
|
||||
|
||||
const initialState = await phone.expectStorageState('initial state');
|
||||
|
||||
debug('Creating stranger');
|
||||
const stranger = await server.createPrimaryDevice({
|
||||
profileName: 'Mysterious Stranger',
|
||||
|
@ -52,9 +54,23 @@ describe('storage service', function needsName() {
|
|||
)
|
||||
.click();
|
||||
|
||||
const initialState = await phone.expectStorageState('initial state');
|
||||
assert.strictEqual(initialState.version, 1);
|
||||
assert.isUndefined(initialState.getContact(stranger));
|
||||
debug("Verify that we stored stranger's profile key");
|
||||
const postMessageState = await phone.waitForStorageState({
|
||||
after: initialState,
|
||||
});
|
||||
{
|
||||
assert.strictEqual(postMessageState.version, 2);
|
||||
assert.isFalse(postMessageState.getContact(stranger)?.whitelisted);
|
||||
assert.strictEqual(
|
||||
postMessageState.getContact(stranger)?.profileKey?.length,
|
||||
32
|
||||
);
|
||||
|
||||
// ContactRecord
|
||||
const { added, removed } = postMessageState.diff(initialState);
|
||||
assert.strictEqual(added.length, 1, 'only one record must be added');
|
||||
assert.strictEqual(removed.length, 0, 'no records should be removed');
|
||||
}
|
||||
|
||||
debug('Accept conversation from a stranger');
|
||||
await conversationStack
|
||||
|
@ -64,15 +80,19 @@ describe('storage service', function needsName() {
|
|||
debug('Verify that storage state was updated');
|
||||
{
|
||||
const nextState = await phone.waitForStorageState({
|
||||
after: initialState,
|
||||
after: postMessageState,
|
||||
});
|
||||
assert.strictEqual(nextState.version, 2);
|
||||
assert.strictEqual(nextState.version, 3);
|
||||
assert.isTrue(nextState.getContact(stranger)?.whitelisted);
|
||||
|
||||
// ContactRecord
|
||||
const { added, removed } = nextState.diff(initialState);
|
||||
const { added, removed } = nextState.diff(postMessageState);
|
||||
assert.strictEqual(added.length, 1, 'only one record must be added');
|
||||
assert.strictEqual(removed.length, 0, 'no records should be removed');
|
||||
assert.strictEqual(
|
||||
removed.length,
|
||||
1,
|
||||
'only one record should be removed'
|
||||
);
|
||||
}
|
||||
|
||||
// Stranger should receive our profile key
|
||||
|
@ -110,6 +130,6 @@ describe('storage service', function needsName() {
|
|||
|
||||
debug('Verifying the final manifest version');
|
||||
const finalState = await phone.expectStorageState('consistency check');
|
||||
assert.strictEqual(finalState.version, 2);
|
||||
assert.strictEqual(finalState.version, 3);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2610,7 +2610,7 @@ export default class MessageReceiver
|
|||
envelope: ProcessedEnvelope,
|
||||
contacts: Proto.SyncMessage.IContacts
|
||||
): Promise<void> {
|
||||
log.info('contact sync');
|
||||
log.info('MessageReceiver: handleContacts');
|
||||
const { blob } = contacts;
|
||||
if (!blob) {
|
||||
throw new Error('MessageReceiver.handleContacts: blob field was missing');
|
||||
|
@ -2631,11 +2631,11 @@ export default class MessageReceiver
|
|||
contactDetails = contactBuffer.next();
|
||||
}
|
||||
|
||||
const finalEvent = new ContactSyncEvent();
|
||||
results.push(this.dispatchAndWait(finalEvent));
|
||||
|
||||
await Promise.all(results);
|
||||
|
||||
const finalEvent = new ContactSyncEvent();
|
||||
await this.dispatchAndWait(finalEvent);
|
||||
|
||||
log.info('handleContacts: finished');
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue