From fefa5d9a254a343141a5703f94c8e45c82768442 Mon Sep 17 00:00:00 2001 From: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:14:56 -0700 Subject: [PATCH] Send FETCH_LATEST.LOCAL_PROFILE on capability flip --- ts/services/profiles.ts | 44 +++++++++++++++++++++++++++++++++++++++++ ts/textsecure/WebAPI.ts | 2 ++ ts/types/Storage.d.ts | 7 +++++++ 3 files changed, 53 insertions(+) diff --git a/ts/services/profiles.ts b/ts/services/profiles.ts index 931ae8a336ba..690a3c6977b5 100644 --- a/ts/services/profiles.ts +++ b/ts/services/profiles.ts @@ -9,7 +9,9 @@ import type { ConversationModel } from '../models/conversations'; import type { GetProfileOptionsType, GetProfileUnauthOptionsType, + CapabilitiesType, } from '../textsecure/WebAPI'; +import MessageSender from '../textsecure/SendMessage'; import type { ServiceIdString } from '../types/ServiceId'; import { DataWriter } from '../sql/Client'; import * as log from '../logging/log'; @@ -30,6 +32,7 @@ import { parseBadgesFromServer } from '../badges/parseBadgesFromServer'; import { strictAssert } from '../util/assert'; import { drop } from '../util/drop'; import { findRetryAfterTimeFromError } from '../jobs/helpers/findRetryAfterTimeFromError'; +import { singleProtoJobQueue } from '../jobs/singleProtoJobQueue'; import { SEALED_SENDER } from '../types/SealedSender'; import { HTTPError } from '../textsecure/Errors'; import { Address } from '../types/Address'; @@ -59,6 +62,11 @@ type JobType = { // - Enforce a maximum profile fetch frequency // - Don't even attempt jobs when offline +const OBSERVED_CAPABILITY_KEYS = Object.keys({ + deleteSync: true, + versionedExpirationTimer: true, +} satisfies CapabilitiesType) as ReadonlyArray; + export class ProfileService { private jobQueue: PQueue; @@ -453,12 +461,48 @@ async function doGetProfile(c: ConversationModel): Promise { await window.storage.put('paymentAddress', profile.paymentAddress); } + const pastCapabilities = c.get('capabilities'); if (profile.capabilities) { c.set({ capabilities: profile.capabilities }); } else { c.unset('capabilities'); } + if (isMe(c.attributes)) { + const newCapabilities = c.get('capabilities'); + + let hasChanged = false; + const observedCapabilities = { + ...window.storage.get('observedCapabilities'), + }; + const newKeys = new Array(); + for (const key of OBSERVED_CAPABILITY_KEYS) { + // Already reported + if (observedCapabilities[key]) { + continue; + } + + if (newCapabilities?.[key]) { + if (!pastCapabilities?.[key]) { + hasChanged = true; + newKeys.push(key); + } + observedCapabilities[key] = true; + } + } + + await window.storage.put('observedCapabilities', observedCapabilities); + if (hasChanged) { + log.info( + 'getProfile: detected a capability flip, sending fetch profile', + newKeys + ); + await singleProtoJobQueue.add( + MessageSender.getFetchLocalProfileSyncMessage() + ); + } + } + const badges = parseBadgesFromServer(profile.badges, updatesUrl); if (badges.length) { await window.reduxActions.badges.updateOrCreate(badges); diff --git a/ts/textsecure/WebAPI.ts b/ts/textsecure/WebAPI.ts index 8bf81b8de787..4902ad843919 100644 --- a/ts/textsecure/WebAPI.ts +++ b/ts/textsecure/WebAPI.ts @@ -738,6 +738,8 @@ export type WebAPIConnectType = { connect: (options: WebAPIConnectOptionsType) => WebAPIType; }; +// When updating this make sure to update `observedCapabilities` type in +// ts/types/Storage.d.ts export type CapabilitiesType = { deleteSync: boolean; versionedExpirationTimer: boolean; diff --git a/ts/types/Storage.d.ts b/ts/types/Storage.d.ts index 2f8771bbbb74..9168474a79cf 100644 --- a/ts/types/Storage.d.ts +++ b/ts/types/Storage.d.ts @@ -175,6 +175,13 @@ export type StorageAccessType = { serverId: Uint8Array; }; needOrphanedAttachmentCheck: boolean; + observedCapabilities: { + deleteSync?: true; + versionedExpirationTimer?: true; + + // Note: Upon capability deprecation - change the value type to `never` and + // remove it in `ts/background.ts` + }; // Deprecated 'challenge:retry-message-ids': never;