mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-04-18 07:36:00 +01:00
Make explicit storage item preserve/remove behavior on unlink
This commit is contained in:
@@ -45,7 +45,7 @@ import { createSupportUrl } from '../ts/util/createSupportUrl.std.js';
|
||||
import { missingCaseError } from '../ts/util/missingCaseError.std.js';
|
||||
import { strictAssert } from '../ts/util/assert.std.js';
|
||||
import { drop } from '../ts/util/drop.std.js';
|
||||
import type { ThemeSettingType } from '../ts/types/StorageUIKeys.std.js';
|
||||
import type { ThemeSettingType } from '../ts/util/theme.std.js';
|
||||
import { ThemeType } from '../ts/types/Util.std.js';
|
||||
import { NotificationType } from '../ts/types/notifications.std.js';
|
||||
import * as Errors from '../ts/types/errors.std.js';
|
||||
|
||||
@@ -3395,18 +3395,6 @@ export async function startApp(): Promise<void> {
|
||||
|
||||
void Registration.remove();
|
||||
|
||||
const NUMBER_ID_KEY = 'number_id';
|
||||
const UUID_ID_KEY = 'uuid_id';
|
||||
const PNI_KEY = 'pni';
|
||||
const LAST_PROCESSED_INDEX_KEY = 'attachmentMigration_lastProcessedIndex';
|
||||
const IS_MIGRATION_COMPLETE_KEY = 'attachmentMigration_isComplete';
|
||||
|
||||
const previousNumberId = itemStorage.get(NUMBER_ID_KEY);
|
||||
const previousUuidId = itemStorage.get(UUID_ID_KEY);
|
||||
const previousPni = itemStorage.get(PNI_KEY);
|
||||
const lastProcessedIndex = itemStorage.get(LAST_PROCESSED_INDEX_KEY);
|
||||
const isMigrationComplete = itemStorage.get(IS_MIGRATION_COMPLETE_KEY);
|
||||
|
||||
try {
|
||||
log.info('unlinkAndDisconnect: removing configuration');
|
||||
|
||||
@@ -3429,30 +3417,6 @@ export async function startApp(): Promise<void> {
|
||||
// Finally, conversations in the database, and delete all config tables
|
||||
await signalProtocolStore.removeAllConfiguration();
|
||||
|
||||
// These three bits of data are important to ensure that the app loads up
|
||||
// the conversation list, instead of showing just the QR code screen.
|
||||
if (previousNumberId !== undefined) {
|
||||
await itemStorage.put(NUMBER_ID_KEY, previousNumberId);
|
||||
}
|
||||
if (previousUuidId !== undefined) {
|
||||
await itemStorage.put(UUID_ID_KEY, previousUuidId);
|
||||
}
|
||||
if (previousPni !== undefined) {
|
||||
await itemStorage.put(PNI_KEY, previousPni);
|
||||
}
|
||||
|
||||
// These two are important to ensure we don't rip through every message
|
||||
// in the database attempting to upgrade it after starting up again.
|
||||
await itemStorage.put(
|
||||
IS_MIGRATION_COMPLETE_KEY,
|
||||
isMigrationComplete || false
|
||||
);
|
||||
if (lastProcessedIndex !== undefined) {
|
||||
await itemStorage.put(LAST_PROCESSED_INDEX_KEY, lastProcessedIndex);
|
||||
} else {
|
||||
await itemStorage.remove(LAST_PROCESSED_INDEX_KEY);
|
||||
}
|
||||
|
||||
// Re-hydrate items from memory; removeAllConfiguration above changed database
|
||||
await itemStorage.fetch();
|
||||
|
||||
@@ -3464,8 +3428,6 @@ export async function startApp(): Promise<void> {
|
||||
Errors.toLogFormat(eraseError)
|
||||
);
|
||||
} finally {
|
||||
await Registration.markEverDone();
|
||||
|
||||
if (window.SignalCI) {
|
||||
window.SignalCI.handleEvent('unlinkCleanupComplete', null);
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@ import type {
|
||||
NotificationSettingType,
|
||||
SentMediaQualitySettingType,
|
||||
ZoomFactorType,
|
||||
} from '../types/Storage.d.ts';
|
||||
import type { ThemeSettingType } from '../types/StorageUIKeys.std.js';
|
||||
} from '../types/StorageKeys.std.js';
|
||||
import type { ThemeSettingType } from '../util/theme.std.js';
|
||||
import type { AnyToast } from '../types/Toast.dom.js';
|
||||
import { ToastType } from '../types/Toast.dom.js';
|
||||
import type { ConversationType } from '../state/ducks/conversations.preload.js';
|
||||
|
||||
@@ -11,6 +11,9 @@ import type {
|
||||
import type { FunEmojiLocalizerIndex } from '../useFunEmojiLocalizer.dom.js';
|
||||
import { removeDiacritics } from '../../../util/removeDiacritics.std.js';
|
||||
import { createLogger } from '../../../logging/log.std.js';
|
||||
import { EmojiSkinTone } from '../../../types/emoji.std.js';
|
||||
|
||||
export { EmojiSkinTone } from '../../../types/emoji.std.js';
|
||||
|
||||
const log = createLogger('fun/data/emojis');
|
||||
|
||||
@@ -46,15 +49,6 @@ export enum EmojiPickerCategory {
|
||||
Flags = 'EmojiPickerCategory.Flags',
|
||||
}
|
||||
|
||||
export enum EmojiSkinTone {
|
||||
None = 'EmojiSkinTone.None',
|
||||
Type1 = 'EmojiSkinTone.Type1', // 1F3FB
|
||||
Type2 = 'EmojiSkinTone.Type2', // 1F3FC
|
||||
Type3 = 'EmojiSkinTone.Type3', // 1F3FD
|
||||
Type4 = 'EmojiSkinTone.Type4', // 1F3FE
|
||||
Type5 = 'EmojiSkinTone.Type5', // 1F3FF
|
||||
}
|
||||
|
||||
export function isValidEmojiSkinTone(value: unknown): value is EmojiSkinTone {
|
||||
return (
|
||||
typeof value === 'string' &&
|
||||
|
||||
@@ -32,7 +32,7 @@ import type { ReactionType } from '../types/Reactions.std.js';
|
||||
import { ReactionReadStatus } from '../types/Reactions.std.js';
|
||||
import type { AciString, ServiceIdString } from '../types/ServiceId.std.js';
|
||||
import { isServiceIdString } from '../types/ServiceId.std.js';
|
||||
import { STORAGE_UI_KEYS } from '../types/StorageUIKeys.std.js';
|
||||
import { STORAGE_KEYS_TO_PRESERVE_AFTER_UNLINK } from '../types/StorageKeys.std.js';
|
||||
import type { StoryDistributionIdString } from '../types/StoryDistributionId.std.js';
|
||||
import * as Errors from '../types/errors.std.js';
|
||||
import { assertDev, strictAssert } from '../util/assert.std.js';
|
||||
@@ -8606,7 +8606,7 @@ function removeAllConfiguration(db: WritableDB): void {
|
||||
})
|
||||
.all();
|
||||
|
||||
const allowedSet = new Set<string>(STORAGE_UI_KEYS);
|
||||
const allowedSet = new Set<string>(STORAGE_KEYS_TO_PRESERVE_AFTER_UNLINK);
|
||||
for (const id of itemIds) {
|
||||
if (!allowedSet.has(id)) {
|
||||
removeById(db, 'items', id);
|
||||
|
||||
@@ -102,10 +102,7 @@ import {
|
||||
} from './PreferencesNotificationProfiles.preload.js';
|
||||
|
||||
import type { SettingsLocation } from '../../types/Nav.std.js';
|
||||
import type {
|
||||
StorageAccessType,
|
||||
ZoomFactorType,
|
||||
} from '../../types/Storage.d.ts';
|
||||
import type { StorageAccessType } from '../../types/Storage.d.ts';
|
||||
import type { ThemeType } from '../../util/preload.preload.js';
|
||||
import type { WidthBreakpoint } from '../../components/_util.std.js';
|
||||
import { DialogType } from '../../types/Dialogs.std.js';
|
||||
@@ -121,6 +118,7 @@ import type { SmartPreferencesChatFoldersPageProps } from './PreferencesChatFold
|
||||
import type { SmartPreferencesEditChatFolderPageProps } from './PreferencesEditChatFolderPage.preload.js';
|
||||
import type { ExternalProps as SmartNotificationProfilesProps } from './PreferencesNotificationProfiles.preload.js';
|
||||
import { useMegaphonesActions } from '../ducks/megaphones.preload.js';
|
||||
import type { ZoomFactorType } from '../../types/StorageKeys.std.js';
|
||||
|
||||
const DEFAULT_NOTIFICATION_SETTING = 'message';
|
||||
|
||||
@@ -606,7 +604,8 @@ export function SmartPreferences(): React.JSX.Element | null {
|
||||
defaultValue: StorageAccessType[K],
|
||||
callback?: (value: StorageAccessType[K]) => void
|
||||
): [StorageAccessType[K], (value: StorageAccessType[K]) => void] {
|
||||
const value = items[key] ?? defaultValue;
|
||||
const value =
|
||||
(items[key] as StorageAccessType[K] | undefined) ?? defaultValue;
|
||||
const setter = (newValue: StorageAccessType[K]) => {
|
||||
putItem(key, newValue);
|
||||
callback?.(newValue);
|
||||
|
||||
@@ -42,4 +42,40 @@ describe('Remove all configuration test', () => {
|
||||
'Name (and all other fields) should be preserved'
|
||||
);
|
||||
});
|
||||
|
||||
it('Removes non-preserved storage items', async () => {
|
||||
/** Should be preserved */
|
||||
await DataWriter.createOrUpdateItem({
|
||||
id: 'zoomFactor',
|
||||
value: 1.5,
|
||||
});
|
||||
await DataWriter.createOrUpdateItem({
|
||||
id: 'version',
|
||||
value: 'v1.2.3',
|
||||
});
|
||||
await DataWriter.createOrUpdateItem({
|
||||
id: 'uuid_id',
|
||||
value: 'aci-should-be-retained',
|
||||
});
|
||||
|
||||
/** Should be deleted */
|
||||
await DataWriter.createOrUpdateItem({
|
||||
id: 'storageFetchComplete',
|
||||
value: true,
|
||||
});
|
||||
await DataWriter.createOrUpdateItem({
|
||||
// @ts-expect-error incorrect key
|
||||
id: 'unknown-key',
|
||||
value: 1.5,
|
||||
});
|
||||
|
||||
await DataWriter.removeAllConfiguration();
|
||||
|
||||
const allItems = await DataReader.getAllItems();
|
||||
assert.deepStrictEqual(allItems, {
|
||||
uuid_id: 'aci-should-be-retained',
|
||||
version: 'v1.2.3',
|
||||
zoomFactor: 1.5,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -86,6 +86,8 @@ import { signalProtocolStore } from '../SignalProtocolStore.preload.js';
|
||||
import { itemStorage } from './Storage.preload.js';
|
||||
import { deriveAccessKeyFromProfileKey } from '../util/zkgroup.node.js';
|
||||
import { wrappingAdd24 } from '../util/wrappingAdd.std.js';
|
||||
import { everDone as registrationEverDone } from '../util/registration.preload.js';
|
||||
import { isAciString } from '../util/isAciString.std.js';
|
||||
|
||||
const { isNumber, omit, orderBy } = lodash;
|
||||
|
||||
@@ -1033,8 +1035,20 @@ export default class AccountManager extends EventTarget {
|
||||
const numberChanged =
|
||||
!previousACI && previousNumber && previousNumber !== number;
|
||||
|
||||
let cleanStart = !previousACI && !previousPNI && !previousNumber;
|
||||
if (uuidChanged || numberChanged) {
|
||||
let cleanStart =
|
||||
!previousACI &&
|
||||
!previousPNI &&
|
||||
!previousNumber &&
|
||||
!registrationEverDone();
|
||||
|
||||
// To be extra safe, clear everything if we know registration happened but there's no
|
||||
// existing identifier
|
||||
const hadPreviousIdentifier =
|
||||
isAciString(previousACI) || Boolean(previousNumber);
|
||||
const missingCriticalData =
|
||||
registrationEverDone() && !hadPreviousIdentifier;
|
||||
|
||||
if (uuidChanged || numberChanged || missingCriticalData) {
|
||||
if (uuidChanged) {
|
||||
log.warn(
|
||||
'createAccount: New uuid is different from old uuid; deleting all previous data'
|
||||
@@ -1045,6 +1059,11 @@ export default class AccountManager extends EventTarget {
|
||||
'createAccount: New number is different from old number; deleting all previous data'
|
||||
);
|
||||
}
|
||||
if (missingCriticalData) {
|
||||
log.error(
|
||||
'createAccount: device had been registered but had no previous identifier'
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await signalProtocolStore.removeAllData();
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Environment } from '../environment.std.js';
|
||||
import { themeSettingSchema } from './StorageUIKeys.std.js';
|
||||
import { HourCyclePreferenceSchema } from './I18N.std.js';
|
||||
import { DNSFallbackSchema } from './DNSFallback.std.js';
|
||||
import { themeSettingSchema } from '../util/theme.std.js';
|
||||
|
||||
const environmentSchema = z.nativeEnum(Environment);
|
||||
|
||||
|
||||
310
ts/types/Storage.d.ts
vendored
310
ts/types/Storage.d.ts
vendored
@@ -1,315 +1,9 @@
|
||||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { AudioDevice } from '@signalapp/ringrtc';
|
||||
import type {
|
||||
CustomColorsItemType,
|
||||
DefaultConversationColorType,
|
||||
} from './Colors.std.js';
|
||||
import type { PhoneNumberDiscoverability } from '../util/phoneNumberDiscoverability.std.js';
|
||||
import type { RetryItemType } from '../services/retryPlaceholders.std.js';
|
||||
import type { ConfigMapType as RemoteConfigType } from '../RemoteConfig.dom.js';
|
||||
import type { ExtendedStorageID, UnknownRecord } from './StorageService.d.ts';
|
||||
import type { StorageAccessType } from './StorageKeys.std.js';
|
||||
|
||||
import type { GroupCredentialType } from '../textsecure/WebAPI.preload.js';
|
||||
import type {
|
||||
SessionResetsType,
|
||||
StorageServiceCredentials,
|
||||
} from '../textsecure/Types.d.ts';
|
||||
import type {
|
||||
BackupCredentialWrapperType,
|
||||
BackupsSubscriptionType,
|
||||
BackupStatusType,
|
||||
} from './backups.node.js';
|
||||
import type { ServiceIdString } from './ServiceId.std.js';
|
||||
import type { RegisteredChallengeType } from '../challenge.dom.js';
|
||||
import type { ServerAlertsType } from '../util/handleServerAlerts.preload.js';
|
||||
import type { NotificationProfileOverride } from './NotificationProfile.std.js';
|
||||
import type { PhoneNumberSharingMode } from './PhoneNumberSharingMode.std.js';
|
||||
import type { LocalBackupExportMetadata } from './LocalExport.std.js';
|
||||
|
||||
export type AutoDownloadAttachmentType = {
|
||||
photos: boolean;
|
||||
videos: boolean;
|
||||
audio: boolean;
|
||||
documents: boolean;
|
||||
};
|
||||
|
||||
export type SerializedCertificateType = {
|
||||
expires: number;
|
||||
serialized: Uint8Array;
|
||||
};
|
||||
|
||||
export type ZoomFactorType = 0.75 | 1 | 1.25 | 1.5 | 2 | number;
|
||||
|
||||
export type SentMediaQualitySettingType = 'standard' | 'high';
|
||||
|
||||
export type NotificationSettingType = 'message' | 'name' | 'count' | 'off';
|
||||
|
||||
export type IdentityKeyMap = Record<
|
||||
ServiceIdString,
|
||||
{
|
||||
privKey: Uint8Array;
|
||||
pubKey: Uint8Array;
|
||||
}
|
||||
>;
|
||||
|
||||
// This should be in sync with `STORAGE_UI_KEYS` in `ts/types/StorageUIKeys.ts`.
|
||||
|
||||
export type StorageAccessType = {
|
||||
'always-relay-calls': boolean;
|
||||
'audio-notification': boolean;
|
||||
'auto-download-update': boolean;
|
||||
'auto-download-attachment': AutoDownloadAttachmentType;
|
||||
autoConvertEmoji: boolean;
|
||||
'badge-count-muted-conversations': boolean;
|
||||
'blocked-groups': ReadonlyArray<string>;
|
||||
'blocked-uuids': ReadonlyArray<ServiceIdString>;
|
||||
'call-ringtone-notification': boolean;
|
||||
'call-system-notification': boolean;
|
||||
lastCallQualitySurveyTime: number;
|
||||
lastCallQualityFailureSurveyTime: number;
|
||||
cqsTestMode: boolean;
|
||||
'hide-menu-bar': boolean;
|
||||
'incoming-call-notification': boolean;
|
||||
'notification-draw-attention': boolean;
|
||||
'notification-setting': NotificationSettingType;
|
||||
'read-receipt-setting': boolean;
|
||||
'sent-media-quality': SentMediaQualitySettingType;
|
||||
audioMessage: boolean;
|
||||
attachmentMigration_isComplete: boolean;
|
||||
attachmentMigration_lastProcessedIndex: number;
|
||||
blocked: ReadonlyArray<string>;
|
||||
defaultConversationColor: DefaultConversationColorType;
|
||||
|
||||
customColors: CustomColorsItemType;
|
||||
device_name: string;
|
||||
deviceCreatedAt: number;
|
||||
existingOnboardingStoryMessageIds: ReadonlyArray<string> | undefined;
|
||||
hasSetMyStoriesPrivacy: boolean;
|
||||
hasCompletedUsernameOnboarding: boolean;
|
||||
hasCompletedUsernameLinkOnboarding: boolean;
|
||||
hasCompletedSafetyNumberOnboarding: boolean;
|
||||
hasSeenGroupStoryEducationSheet: boolean;
|
||||
hasSeenNotificationProfileOnboarding: boolean;
|
||||
hasSeenKeyTransparencyOnboarding: boolean;
|
||||
hasViewedOnboardingStory: boolean;
|
||||
hasStoriesDisabled: boolean;
|
||||
hasKeyTransparencyDisabled: boolean;
|
||||
storyViewReceiptsEnabled: boolean | undefined;
|
||||
identityKeyMap: IdentityKeyMap;
|
||||
lastAttemptedToRefreshProfilesAt: number;
|
||||
lastResortKeyUpdateTime: number;
|
||||
lastResortKeyUpdateTimePNI: number;
|
||||
accountEntropyPool: string;
|
||||
masterKey: string;
|
||||
|
||||
accountEntropyPoolLastRequestTime: number;
|
||||
maxPreKeyId: number;
|
||||
maxPreKeyIdPNI: number;
|
||||
maxKyberPreKeyId: number;
|
||||
maxKyberPreKeyIdPNI: number;
|
||||
number_id: string;
|
||||
password: string;
|
||||
profileKey: Uint8Array;
|
||||
regionCode: string;
|
||||
registrationIdMap: Record<ServiceIdString, number>;
|
||||
remoteBuildExpiration: number;
|
||||
sessionResets: SessionResetsType;
|
||||
showStickerPickerHint: boolean;
|
||||
showStickersIntroduction: boolean;
|
||||
seenPinMessageDisappearingMessagesWarningCount: number;
|
||||
hasSeenAdminDeleteEducationDialog: boolean;
|
||||
signedKeyId: number;
|
||||
signedKeyIdPNI: number;
|
||||
signedKeyUpdateTime: number;
|
||||
signedKeyUpdateTimePNI: number;
|
||||
storageKey: string;
|
||||
synced_at: number | undefined;
|
||||
userAgent: string;
|
||||
uuid_id: string;
|
||||
useRingrtcAdm: boolean;
|
||||
pni: string;
|
||||
version: string;
|
||||
linkPreviews: boolean;
|
||||
universalExpireTimer: number;
|
||||
retryPlaceholders: ReadonlyArray<RetryItemType>;
|
||||
donationWorkflow: string;
|
||||
chromiumRegistrationDoneEver: '';
|
||||
chromiumRegistrationDone: '';
|
||||
phoneNumberSharingMode: PhoneNumberSharingMode;
|
||||
phoneNumberDiscoverability: PhoneNumberDiscoverability;
|
||||
pinnedConversationIds: ReadonlyArray<string>;
|
||||
preferContactAvatars: boolean;
|
||||
textFormatting: boolean;
|
||||
typingIndicators: boolean;
|
||||
sealedSenderIndicators: boolean;
|
||||
storageFetchComplete: boolean;
|
||||
avatarUrl: string | undefined;
|
||||
manifestVersion: number;
|
||||
manifestRecordIkm: Uint8Array;
|
||||
storageCredentials: StorageServiceCredentials;
|
||||
'storage-service-error-records': ReadonlyArray<UnknownRecord>;
|
||||
'storage-service-unknown-records': ReadonlyArray<UnknownRecord>;
|
||||
'storage-service-pending-deletes': ReadonlyArray<ExtendedStorageID>;
|
||||
'preferred-video-input-device': string | undefined;
|
||||
'preferred-audio-input-device': AudioDevice | undefined;
|
||||
'preferred-audio-output-device': AudioDevice | undefined;
|
||||
remoteConfig: RemoteConfigType;
|
||||
remoteConfigHash: string;
|
||||
serverTimeSkew: number;
|
||||
unidentifiedDeliveryIndicators: boolean;
|
||||
groupCredentials: ReadonlyArray<GroupCredentialType>;
|
||||
callLinkAuthCredentials: ReadonlyArray<GroupCredentialType>;
|
||||
backupCombinedCredentials: ReadonlyArray<BackupCredentialWrapperType>;
|
||||
backupCombinedCredentialsLastRequestTime: number;
|
||||
backupMediaRootKey: Uint8Array;
|
||||
backupMediaDownloadTotalBytes: number;
|
||||
backupMediaDownloadCompletedBytes: number;
|
||||
backupMediaDownloadPaused: boolean;
|
||||
backupMediaDownloadBannerDismissed: boolean;
|
||||
attachmentDownloadManagerIdled: boolean;
|
||||
messageInsertTriggersDisabled: boolean;
|
||||
setBackupMessagesSignatureKey: boolean;
|
||||
setBackupMediaSignatureKey: boolean;
|
||||
lastReceivedAtCounter: number;
|
||||
preferredReactionEmoji: ReadonlyArray<string>;
|
||||
emojiSkinToneDefault: EmojiSkinToneDefault;
|
||||
unreadCount: number;
|
||||
'challenge:conversations': ReadonlyArray<RegisteredChallengeType>;
|
||||
|
||||
deviceNameEncrypted: boolean;
|
||||
'indexeddb-delete-needed': boolean;
|
||||
senderCertificate: SerializedCertificateType;
|
||||
senderCertificateNoE164: SerializedCertificateType;
|
||||
paymentAddress: string;
|
||||
zoomFactor: ZoomFactorType;
|
||||
preferredLeftPaneWidth: number;
|
||||
nextScheduledUpdateKeyTime: number;
|
||||
navTabsCollapsed: boolean;
|
||||
areWeASubscriber: boolean;
|
||||
subscriberId: Uint8Array;
|
||||
subscriberCurrencyCode: string;
|
||||
// Note: for historical reasons, this has two l's
|
||||
donorSubscriptionManuallyCancelled: boolean;
|
||||
backupsSubscriberId: Uint8Array;
|
||||
backupsSubscriberPurchaseToken: string;
|
||||
backupsSubscriberOriginalTransactionId: string;
|
||||
displayBadgesOnProfile: boolean;
|
||||
keepMutedChatsArchived: boolean;
|
||||
usernameLastIntegrityCheck: number;
|
||||
usernameCorrupted: boolean;
|
||||
usernameLinkCorrupted: boolean;
|
||||
usernameLinkColor: number;
|
||||
usernameLink: {
|
||||
entropy: Uint8Array;
|
||||
serverId: Uint8Array;
|
||||
};
|
||||
serverAlerts: ServerAlertsType;
|
||||
needOrphanedAttachmentCheck: boolean;
|
||||
needProfileMovedModal: boolean;
|
||||
notificationProfileOverride: NotificationProfileOverride | undefined;
|
||||
notificationProfileOverrideFromPrimary:
|
||||
| NotificationProfileOverride
|
||||
| undefined;
|
||||
notificationProfileSyncDisabled: boolean;
|
||||
observedCapabilities: {
|
||||
attachmentBackfill?: true;
|
||||
|
||||
// Note: Upon capability deprecation - change the value type to `never` and
|
||||
// remove it in `ts/background.ts`
|
||||
deleteSync?: never;
|
||||
ssre2?: never;
|
||||
};
|
||||
releaseNotesNextFetchTime: number;
|
||||
releaseNotesVersionWatermark: string;
|
||||
releaseNotesPreviousManifestHash: string;
|
||||
|
||||
// If present - we are downloading backup
|
||||
backupDownloadPath: string;
|
||||
|
||||
// If present together with backupDownloadPath - we are downloading
|
||||
// link-and-sync backup
|
||||
backupEphemeralKey: Uint8Array;
|
||||
|
||||
// If present - we are resuming the download of known transfer archive
|
||||
backupTransitArchive: {
|
||||
cdn: number;
|
||||
key: string;
|
||||
};
|
||||
|
||||
backupTier: number | undefined;
|
||||
cloudBackupStatus: BackupStatusType | undefined;
|
||||
backupSubscriptionStatus: BackupsSubscriptionType | undefined;
|
||||
|
||||
backupKeyViewed: boolean;
|
||||
lastLocalBackup: LocalBackupExportMetadata;
|
||||
localBackupFolder: string | undefined;
|
||||
|
||||
// If true Desktop message history was restored from backup
|
||||
isRestoredFromBackup: boolean;
|
||||
|
||||
// The `firstAppVersion` present on an BackupInfo from an imported backup.
|
||||
restoredBackupFirstAppVersion: string;
|
||||
|
||||
// Stored solely for pesistance during import/export sequence
|
||||
svrPin: string;
|
||||
optimizeOnDeviceStorage: boolean;
|
||||
pinReminders: boolean | undefined;
|
||||
screenLockTimeoutMinutes: number | undefined;
|
||||
'auto-download-attachment-primary':
|
||||
| undefined
|
||||
| {
|
||||
photos: number;
|
||||
audio: number;
|
||||
videos: number;
|
||||
documents: number;
|
||||
};
|
||||
androidSpecificSettings: unknown;
|
||||
callsUseLessDataSetting: unknown;
|
||||
allowSealedSenderFromAnyone: unknown;
|
||||
|
||||
postRegistrationSyncsStatus: 'incomplete' | 'complete';
|
||||
|
||||
avatarsHaveBeenMigrated: boolean;
|
||||
|
||||
// Key Transparency
|
||||
lastDistinguishedTreeHead: Uint8Array;
|
||||
// Meaning of values:
|
||||
//
|
||||
// - undefined - status unknown or uninitialized
|
||||
// - 'ok' - last check passed
|
||||
// - 'intermittent' - last check failed, but we haven't retried yet
|
||||
// - 'fail' - last check failed after retry
|
||||
keyTransparencySelfHealth: undefined | 'ok' | 'intermittent' | 'fail';
|
||||
lastKeyTransparencySelfCheck: number;
|
||||
|
||||
// Test-only
|
||||
// Not used UI, stored as is when imported from backup during tests
|
||||
defaultWallpaperPhotoPointer: Uint8Array;
|
||||
defaultWallpaperPreset: number;
|
||||
defaultDimWallpaperInDarkMode: boolean;
|
||||
defaultAutoBubbleColor: boolean;
|
||||
|
||||
// Deprecated
|
||||
'challenge:retry-message-ids': never;
|
||||
nextSignedKeyRotationTime: number;
|
||||
previousAudioDeviceModule: never;
|
||||
senderCertificateWithUuid: never;
|
||||
signaling_key: never;
|
||||
signedKeyRotationRejected: number;
|
||||
lastHeartbeat: never;
|
||||
lastStartup: never;
|
||||
sendEditWarningShown: never;
|
||||
formattingWarningShown: never;
|
||||
hasRegisterSupportForUnauthenticatedDelivery: never;
|
||||
masterKeyLastRequestTime: never;
|
||||
versionedExpirationTimer: never;
|
||||
primarySendsSms: never;
|
||||
backupMediaDownloadIdle: never;
|
||||
callQualitySurveyCooldownDisabled: never;
|
||||
localDeleteWarningShown: never;
|
||||
};
|
||||
export type { StorageAccessType } from './StorageKeys.std.js';
|
||||
|
||||
export type StorageInterface = {
|
||||
onready(callback: () => void): void;
|
||||
|
||||
540
ts/types/StorageKeys.std.ts
Normal file
540
ts/types/StorageKeys.std.ts
Normal file
@@ -0,0 +1,540 @@
|
||||
// Copyright 2026 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { AudioDevice } from '@signalapp/ringrtc';
|
||||
import type {
|
||||
CustomColorsItemType,
|
||||
DefaultConversationColorType,
|
||||
} from './Colors.std.js';
|
||||
import type { PhoneNumberDiscoverability } from '../util/phoneNumberDiscoverability.std.js';
|
||||
import type { RetryItemType } from '../services/retryPlaceholders.std.js';
|
||||
import type { ConfigMapType as RemoteConfigType } from '../RemoteConfig.dom.js';
|
||||
import type { ExtendedStorageID, UnknownRecord } from './StorageService.js';
|
||||
|
||||
import type { GroupCredentialType } from '../textsecure/WebAPI.preload.js';
|
||||
import type {
|
||||
SessionResetsType,
|
||||
StorageServiceCredentials,
|
||||
} from '../textsecure/Types.js';
|
||||
import type {
|
||||
BackupCredentialWrapperType,
|
||||
BackupsSubscriptionType,
|
||||
BackupStatusType,
|
||||
} from './backups.node.js';
|
||||
import type { ServiceIdString } from './ServiceId.std.js';
|
||||
import type { RegisteredChallengeType } from '../challenge.dom.js';
|
||||
import type { NotificationProfileOverride } from './NotificationProfile.std.js';
|
||||
import type { PhoneNumberSharingMode } from './PhoneNumberSharingMode.std.js';
|
||||
import type { LocalBackupExportMetadata } from './LocalExport.std.js';
|
||||
import type { ServerAlertsType } from './ServerAlert.std.js';
|
||||
import type { EmojiSkinTone } from './emoji.std.js';
|
||||
import type { AssertSameMembers } from './Util.std.js';
|
||||
|
||||
export type AutoDownloadAttachmentType = {
|
||||
photos: boolean;
|
||||
videos: boolean;
|
||||
audio: boolean;
|
||||
documents: boolean;
|
||||
};
|
||||
|
||||
export type SerializedCertificateType = {
|
||||
expires: number;
|
||||
serialized: Uint8Array;
|
||||
};
|
||||
|
||||
export type ZoomFactorType = 0.75 | 1 | 1.25 | 1.5 | 2 | number;
|
||||
|
||||
export type SentMediaQualitySettingType = 'standard' | 'high';
|
||||
|
||||
export type NotificationSettingType = 'message' | 'name' | 'count' | 'off';
|
||||
|
||||
export type IdentityKeyMap = Record<
|
||||
ServiceIdString,
|
||||
{
|
||||
privKey: Uint8Array;
|
||||
pubKey: Uint8Array;
|
||||
}
|
||||
>;
|
||||
|
||||
export type StorageAccessType = {
|
||||
'always-relay-calls': boolean;
|
||||
'audio-notification': boolean;
|
||||
'auto-download-update': boolean;
|
||||
'auto-download-attachment': AutoDownloadAttachmentType;
|
||||
autoConvertEmoji: boolean;
|
||||
'badge-count-muted-conversations': boolean;
|
||||
'blocked-groups': ReadonlyArray<string>;
|
||||
'blocked-uuids': ReadonlyArray<ServiceIdString>;
|
||||
'call-ringtone-notification': boolean;
|
||||
'call-system-notification': boolean;
|
||||
lastCallQualitySurveyTime: number;
|
||||
lastCallQualityFailureSurveyTime: number;
|
||||
cqsTestMode: boolean;
|
||||
'hide-menu-bar': boolean;
|
||||
'incoming-call-notification': boolean;
|
||||
'notification-draw-attention': boolean;
|
||||
'notification-setting': NotificationSettingType;
|
||||
'read-receipt-setting': boolean;
|
||||
'sent-media-quality': SentMediaQualitySettingType;
|
||||
audioMessage: boolean;
|
||||
attachmentMigration_isComplete: boolean;
|
||||
attachmentMigration_lastProcessedIndex: number;
|
||||
blocked: ReadonlyArray<string>;
|
||||
defaultConversationColor: DefaultConversationColorType;
|
||||
|
||||
customColors: CustomColorsItemType;
|
||||
device_name: string;
|
||||
deviceCreatedAt: number;
|
||||
existingOnboardingStoryMessageIds: ReadonlyArray<string> | undefined;
|
||||
hasSetMyStoriesPrivacy: boolean;
|
||||
hasCompletedUsernameOnboarding: boolean;
|
||||
hasCompletedUsernameLinkOnboarding: boolean;
|
||||
hasCompletedSafetyNumberOnboarding: boolean;
|
||||
hasSeenGroupStoryEducationSheet: boolean;
|
||||
hasSeenNotificationProfileOnboarding: boolean;
|
||||
hasSeenKeyTransparencyOnboarding: boolean;
|
||||
hasViewedOnboardingStory: boolean;
|
||||
hasStoriesDisabled: boolean;
|
||||
hasKeyTransparencyDisabled: boolean;
|
||||
storyViewReceiptsEnabled: boolean | undefined;
|
||||
identityKeyMap: IdentityKeyMap;
|
||||
lastAttemptedToRefreshProfilesAt: number;
|
||||
lastResortKeyUpdateTime: number;
|
||||
lastResortKeyUpdateTimePNI: number;
|
||||
accountEntropyPool: string;
|
||||
masterKey: string;
|
||||
|
||||
accountEntropyPoolLastRequestTime: number;
|
||||
maxPreKeyId: number;
|
||||
maxPreKeyIdPNI: number;
|
||||
maxKyberPreKeyId: number;
|
||||
maxKyberPreKeyIdPNI: number;
|
||||
number_id: string;
|
||||
password: string;
|
||||
profileKey: Uint8Array;
|
||||
regionCode: string;
|
||||
registrationIdMap: Record<ServiceIdString, number>;
|
||||
remoteBuildExpiration: number;
|
||||
sessionResets: SessionResetsType;
|
||||
showStickerPickerHint: boolean;
|
||||
showStickersIntroduction: boolean;
|
||||
seenPinMessageDisappearingMessagesWarningCount: number;
|
||||
hasSeenAdminDeleteEducationDialog: boolean;
|
||||
signedKeyId: number;
|
||||
signedKeyIdPNI: number;
|
||||
signedKeyUpdateTime: number;
|
||||
signedKeyUpdateTimePNI: number;
|
||||
storageKey: string;
|
||||
synced_at: number | undefined;
|
||||
userAgent: string;
|
||||
uuid_id: string;
|
||||
useRingrtcAdm: boolean;
|
||||
pni: string;
|
||||
version: string;
|
||||
linkPreviews: boolean;
|
||||
universalExpireTimer: number;
|
||||
retryPlaceholders: ReadonlyArray<RetryItemType>;
|
||||
donationWorkflow: string;
|
||||
chromiumRegistrationDoneEver: '';
|
||||
chromiumRegistrationDone: '';
|
||||
phoneNumberSharingMode: PhoneNumberSharingMode;
|
||||
phoneNumberDiscoverability: PhoneNumberDiscoverability;
|
||||
pinnedConversationIds: ReadonlyArray<string>;
|
||||
preferContactAvatars: boolean;
|
||||
textFormatting: boolean;
|
||||
typingIndicators: boolean;
|
||||
sealedSenderIndicators: boolean;
|
||||
storageFetchComplete: boolean;
|
||||
avatarUrl: string | undefined;
|
||||
manifestVersion: number;
|
||||
manifestRecordIkm: Uint8Array;
|
||||
storageCredentials: StorageServiceCredentials;
|
||||
'storage-service-error-records': ReadonlyArray<UnknownRecord>;
|
||||
'storage-service-unknown-records': ReadonlyArray<UnknownRecord>;
|
||||
'storage-service-pending-deletes': ReadonlyArray<ExtendedStorageID>;
|
||||
'preferred-video-input-device': string | undefined;
|
||||
'preferred-audio-input-device': AudioDevice | undefined;
|
||||
'preferred-audio-output-device': AudioDevice | undefined;
|
||||
remoteConfig: RemoteConfigType;
|
||||
remoteConfigHash: string;
|
||||
serverTimeSkew: number;
|
||||
unidentifiedDeliveryIndicators: boolean;
|
||||
groupCredentials: ReadonlyArray<GroupCredentialType>;
|
||||
callLinkAuthCredentials: ReadonlyArray<GroupCredentialType>;
|
||||
backupCombinedCredentials: ReadonlyArray<BackupCredentialWrapperType>;
|
||||
backupCombinedCredentialsLastRequestTime: number;
|
||||
backupMediaRootKey: Uint8Array;
|
||||
backupMediaDownloadTotalBytes: number;
|
||||
backupMediaDownloadCompletedBytes: number;
|
||||
backupMediaDownloadPaused: boolean;
|
||||
backupMediaDownloadBannerDismissed: boolean;
|
||||
attachmentDownloadManagerIdled: boolean;
|
||||
messageInsertTriggersDisabled: boolean;
|
||||
setBackupMessagesSignatureKey: boolean;
|
||||
setBackupMediaSignatureKey: boolean;
|
||||
lastReceivedAtCounter: number;
|
||||
preferredReactionEmoji: ReadonlyArray<string>;
|
||||
emojiSkinToneDefault: EmojiSkinTone;
|
||||
unreadCount: number;
|
||||
'challenge:conversations': ReadonlyArray<RegisteredChallengeType>;
|
||||
|
||||
deviceNameEncrypted: boolean;
|
||||
'indexeddb-delete-needed': boolean;
|
||||
senderCertificate: SerializedCertificateType;
|
||||
senderCertificateNoE164: SerializedCertificateType;
|
||||
paymentAddress: string;
|
||||
zoomFactor: ZoomFactorType;
|
||||
preferredLeftPaneWidth: number;
|
||||
nextScheduledUpdateKeyTime: number;
|
||||
navTabsCollapsed: boolean;
|
||||
areWeASubscriber: boolean;
|
||||
subscriberId: Uint8Array;
|
||||
subscriberCurrencyCode: string;
|
||||
// Note: for historical reasons, this has two l's
|
||||
donorSubscriptionManuallyCancelled: boolean;
|
||||
backupsSubscriberId: Uint8Array;
|
||||
backupsSubscriberPurchaseToken: string;
|
||||
backupsSubscriberOriginalTransactionId: string;
|
||||
displayBadgesOnProfile: boolean;
|
||||
keepMutedChatsArchived: boolean;
|
||||
usernameLastIntegrityCheck: number;
|
||||
usernameCorrupted: boolean;
|
||||
usernameLinkCorrupted: boolean;
|
||||
usernameLinkColor: number;
|
||||
usernameLink: {
|
||||
entropy: Uint8Array;
|
||||
serverId: Uint8Array;
|
||||
};
|
||||
serverAlerts: ServerAlertsType;
|
||||
needOrphanedAttachmentCheck: boolean;
|
||||
needProfileMovedModal: boolean;
|
||||
notificationProfileOverride: NotificationProfileOverride | undefined;
|
||||
notificationProfileOverrideFromPrimary:
|
||||
| NotificationProfileOverride
|
||||
| undefined;
|
||||
notificationProfileSyncDisabled: boolean;
|
||||
observedCapabilities: {
|
||||
attachmentBackfill?: true;
|
||||
|
||||
// Note: Upon capability deprecation - change the value type to `never` and
|
||||
// remove it in `ts/background.ts`
|
||||
deleteSync?: never;
|
||||
ssre2?: never;
|
||||
};
|
||||
releaseNotesNextFetchTime: number;
|
||||
releaseNotesVersionWatermark: string;
|
||||
releaseNotesPreviousManifestHash: string;
|
||||
|
||||
// If present - we are downloading backup
|
||||
backupDownloadPath: string;
|
||||
|
||||
// If present together with backupDownloadPath - we are downloading
|
||||
// link-and-sync backup
|
||||
backupEphemeralKey: Uint8Array;
|
||||
|
||||
// If present - we are resuming the download of known transfer archive
|
||||
backupTransitArchive: {
|
||||
cdn: number;
|
||||
key: string;
|
||||
};
|
||||
|
||||
backupTier: number | undefined;
|
||||
cloudBackupStatus: BackupStatusType | undefined;
|
||||
backupSubscriptionStatus: BackupsSubscriptionType | undefined;
|
||||
|
||||
backupKeyViewed: boolean;
|
||||
lastLocalBackup: LocalBackupExportMetadata;
|
||||
localBackupFolder: string | undefined;
|
||||
|
||||
// If true Desktop message history was restored from backup
|
||||
isRestoredFromBackup: boolean;
|
||||
|
||||
// The `firstAppVersion` present on an BackupInfo from an imported backup.
|
||||
restoredBackupFirstAppVersion: string;
|
||||
|
||||
// Stored solely for pesistance during import/export sequence
|
||||
svrPin: string;
|
||||
optimizeOnDeviceStorage: boolean;
|
||||
pinReminders: boolean | undefined;
|
||||
screenLockTimeoutMinutes: number | undefined;
|
||||
'auto-download-attachment-primary':
|
||||
| undefined
|
||||
| {
|
||||
photos: number;
|
||||
audio: number;
|
||||
videos: number;
|
||||
documents: number;
|
||||
};
|
||||
androidSpecificSettings: unknown;
|
||||
callsUseLessDataSetting: unknown;
|
||||
allowSealedSenderFromAnyone: unknown;
|
||||
|
||||
postRegistrationSyncsStatus: 'incomplete' | 'complete';
|
||||
|
||||
avatarsHaveBeenMigrated: boolean;
|
||||
|
||||
// Key Transparency
|
||||
lastDistinguishedTreeHead: Uint8Array;
|
||||
// Meaning of values:
|
||||
//
|
||||
// - undefined - status unknown or uninitialized
|
||||
// - 'ok' - last check passed
|
||||
// - 'intermittent' - last check failed, but we haven't retried yet
|
||||
// - 'fail' - last check failed after retry
|
||||
keyTransparencySelfHealth: undefined | 'ok' | 'intermittent' | 'fail';
|
||||
lastKeyTransparencySelfCheck: number;
|
||||
|
||||
// Test-only
|
||||
// Not used UI, stored as is when imported from backup during tests
|
||||
defaultWallpaperPhotoPointer: Uint8Array;
|
||||
defaultWallpaperPreset: number;
|
||||
defaultDimWallpaperInDarkMode: boolean;
|
||||
defaultAutoBubbleColor: boolean;
|
||||
|
||||
// Deprecated
|
||||
'challenge:retry-message-ids': never;
|
||||
nextSignedKeyRotationTime: number;
|
||||
previousAudioDeviceModule: never;
|
||||
senderCertificateWithUuid: never;
|
||||
signaling_key: never;
|
||||
signedKeyRotationRejected: number;
|
||||
lastHeartbeat: never;
|
||||
lastStartup: never;
|
||||
sendEditWarningShown: never;
|
||||
formattingWarningShown: never;
|
||||
hasRegisterSupportForUnauthenticatedDelivery: never;
|
||||
masterKeyLastRequestTime: never;
|
||||
versionedExpirationTimer: never;
|
||||
primarySendsSms: never;
|
||||
backupMediaDownloadIdle: never;
|
||||
callQualitySurveyCooldownDisabled: never;
|
||||
localDeleteWarningShown: never;
|
||||
};
|
||||
|
||||
export const STORAGE_KEYS_TO_PRESERVE_AFTER_UNLINK = [
|
||||
// UI & user-setting-related keys
|
||||
'always-relay-calls',
|
||||
'audio-notification',
|
||||
'audioMessage',
|
||||
'auto-download-update',
|
||||
'autoConvertEmoji',
|
||||
'badge-count-muted-conversations',
|
||||
'call-ringtone-notification',
|
||||
'call-system-notification',
|
||||
'customColors',
|
||||
'defaultConversationColor',
|
||||
'existingOnboardingStoryMessageIds',
|
||||
'hasCompletedSafetyNumberOnboarding',
|
||||
'hasCompletedUsernameLinkOnboarding',
|
||||
'hide-menu-bar',
|
||||
'incoming-call-notification',
|
||||
'navTabsCollapsed',
|
||||
'notification-draw-attention',
|
||||
'notification-setting',
|
||||
'pinnedConversationIds',
|
||||
'preferred-audio-input-device',
|
||||
'preferred-audio-output-device',
|
||||
'preferred-video-input-device',
|
||||
'preferredLeftPaneWidth',
|
||||
'preferredReactionEmoji',
|
||||
'sent-media-quality',
|
||||
'showStickerPickerHint',
|
||||
'showStickersIntroduction',
|
||||
'emojiSkinToneDefault',
|
||||
'textFormatting',
|
||||
'zoomFactor',
|
||||
|
||||
// Bookkeeping keys
|
||||
'attachmentMigration_lastProcessedIndex',
|
||||
'attachmentMigration_isComplete',
|
||||
'chromiumRegistrationDoneEver',
|
||||
'version',
|
||||
'number_id',
|
||||
'uuid_id',
|
||||
'pni',
|
||||
] as const satisfies ReadonlyArray<keyof StorageAccessType>;
|
||||
|
||||
const STORAGE_KEYS_TO_REMOVE_AFTER_UNLINK = [
|
||||
'auto-download-attachment',
|
||||
'blocked-groups',
|
||||
'blocked-uuids',
|
||||
'lastCallQualitySurveyTime',
|
||||
'lastCallQualityFailureSurveyTime',
|
||||
'cqsTestMode',
|
||||
'read-receipt-setting',
|
||||
'blocked',
|
||||
'device_name',
|
||||
'deviceCreatedAt',
|
||||
'hasSetMyStoriesPrivacy',
|
||||
'hasCompletedUsernameOnboarding',
|
||||
'hasSeenGroupStoryEducationSheet',
|
||||
'hasSeenNotificationProfileOnboarding',
|
||||
'hasSeenKeyTransparencyOnboarding',
|
||||
'hasViewedOnboardingStory',
|
||||
'hasStoriesDisabled',
|
||||
'hasKeyTransparencyDisabled',
|
||||
'storyViewReceiptsEnabled',
|
||||
'identityKeyMap',
|
||||
'lastAttemptedToRefreshProfilesAt',
|
||||
'lastResortKeyUpdateTime',
|
||||
'lastResortKeyUpdateTimePNI',
|
||||
'accountEntropyPool',
|
||||
'masterKey',
|
||||
'accountEntropyPoolLastRequestTime',
|
||||
'maxPreKeyId',
|
||||
'maxPreKeyIdPNI',
|
||||
'maxKyberPreKeyId',
|
||||
'maxKyberPreKeyIdPNI',
|
||||
'password',
|
||||
'profileKey',
|
||||
'regionCode',
|
||||
'registrationIdMap',
|
||||
'remoteBuildExpiration',
|
||||
'sessionResets',
|
||||
'seenPinMessageDisappearingMessagesWarningCount',
|
||||
'hasSeenAdminDeleteEducationDialog',
|
||||
'signedKeyId',
|
||||
'signedKeyIdPNI',
|
||||
'signedKeyUpdateTime',
|
||||
'signedKeyUpdateTimePNI',
|
||||
'storageKey',
|
||||
'synced_at',
|
||||
'userAgent',
|
||||
'useRingrtcAdm',
|
||||
'linkPreviews',
|
||||
'universalExpireTimer',
|
||||
'retryPlaceholders',
|
||||
'donationWorkflow',
|
||||
'chromiumRegistrationDone',
|
||||
'phoneNumberSharingMode',
|
||||
'phoneNumberDiscoverability',
|
||||
'preferContactAvatars',
|
||||
'typingIndicators',
|
||||
'sealedSenderIndicators',
|
||||
'storageFetchComplete',
|
||||
'avatarUrl',
|
||||
'manifestVersion',
|
||||
'manifestRecordIkm',
|
||||
'storageCredentials',
|
||||
'storage-service-error-records',
|
||||
'storage-service-unknown-records',
|
||||
'storage-service-pending-deletes',
|
||||
'remoteConfig',
|
||||
'remoteConfigHash',
|
||||
'serverTimeSkew',
|
||||
'unidentifiedDeliveryIndicators',
|
||||
'groupCredentials',
|
||||
'callLinkAuthCredentials',
|
||||
'backupCombinedCredentials',
|
||||
'backupCombinedCredentialsLastRequestTime',
|
||||
'backupMediaRootKey',
|
||||
'backupMediaDownloadTotalBytes',
|
||||
'backupMediaDownloadCompletedBytes',
|
||||
'backupMediaDownloadPaused',
|
||||
'backupMediaDownloadBannerDismissed',
|
||||
'attachmentDownloadManagerIdled',
|
||||
'messageInsertTriggersDisabled',
|
||||
'setBackupMessagesSignatureKey',
|
||||
'setBackupMediaSignatureKey',
|
||||
'lastReceivedAtCounter',
|
||||
'unreadCount',
|
||||
'challenge:conversations',
|
||||
'deviceNameEncrypted',
|
||||
'indexeddb-delete-needed',
|
||||
'senderCertificate',
|
||||
'senderCertificateNoE164',
|
||||
'paymentAddress',
|
||||
'nextScheduledUpdateKeyTime',
|
||||
'areWeASubscriber',
|
||||
'subscriberId',
|
||||
'subscriberCurrencyCode',
|
||||
'donorSubscriptionManuallyCancelled',
|
||||
'backupsSubscriberId',
|
||||
'backupsSubscriberPurchaseToken',
|
||||
'backupsSubscriberOriginalTransactionId',
|
||||
'displayBadgesOnProfile',
|
||||
'keepMutedChatsArchived',
|
||||
'usernameLastIntegrityCheck',
|
||||
'usernameCorrupted',
|
||||
'usernameLinkCorrupted',
|
||||
'usernameLinkColor',
|
||||
'usernameLink',
|
||||
'serverAlerts',
|
||||
'needOrphanedAttachmentCheck',
|
||||
'needProfileMovedModal',
|
||||
'notificationProfileOverride',
|
||||
'notificationProfileOverrideFromPrimary',
|
||||
'notificationProfileSyncDisabled',
|
||||
'observedCapabilities',
|
||||
'releaseNotesNextFetchTime',
|
||||
'releaseNotesVersionWatermark',
|
||||
'releaseNotesPreviousManifestHash',
|
||||
'backupDownloadPath',
|
||||
'backupEphemeralKey',
|
||||
'backupTransitArchive',
|
||||
'backupTier',
|
||||
'cloudBackupStatus',
|
||||
'backupSubscriptionStatus',
|
||||
'backupKeyViewed',
|
||||
'lastLocalBackup',
|
||||
'localBackupFolder',
|
||||
'isRestoredFromBackup',
|
||||
'restoredBackupFirstAppVersion',
|
||||
'svrPin',
|
||||
'optimizeOnDeviceStorage',
|
||||
'pinReminders',
|
||||
'screenLockTimeoutMinutes',
|
||||
'auto-download-attachment-primary',
|
||||
'androidSpecificSettings',
|
||||
'callsUseLessDataSetting',
|
||||
'allowSealedSenderFromAnyone',
|
||||
'postRegistrationSyncsStatus',
|
||||
'avatarsHaveBeenMigrated',
|
||||
'lastDistinguishedTreeHead',
|
||||
'keyTransparencySelfHealth',
|
||||
'lastKeyTransparencySelfCheck',
|
||||
'defaultWallpaperPhotoPointer',
|
||||
'defaultWallpaperPreset',
|
||||
'defaultDimWallpaperInDarkMode',
|
||||
'defaultAutoBubbleColor',
|
||||
'challenge:retry-message-ids',
|
||||
'nextSignedKeyRotationTime',
|
||||
'previousAudioDeviceModule',
|
||||
'senderCertificateWithUuid',
|
||||
'signaling_key',
|
||||
'signedKeyRotationRejected',
|
||||
'lastHeartbeat',
|
||||
'lastStartup',
|
||||
'sendEditWarningShown',
|
||||
'formattingWarningShown',
|
||||
'hasRegisterSupportForUnauthenticatedDelivery',
|
||||
'masterKeyLastRequestTime',
|
||||
'versionedExpirationTimer',
|
||||
'primarySendsSms',
|
||||
'backupMediaDownloadIdle',
|
||||
'callQualitySurveyCooldownDisabled',
|
||||
'localDeleteWarningShown',
|
||||
] as const satisfies ReadonlyArray<keyof StorageAccessType>;
|
||||
|
||||
// Ensure every storage key is explicitly marked to be preserved or removed on unlink.
|
||||
|
||||
type AssertTrue<T extends true> = T;
|
||||
|
||||
type StorageKeysToPreserveAfterUnlink =
|
||||
(typeof STORAGE_KEYS_TO_PRESERVE_AFTER_UNLINK)[number];
|
||||
type StorageKeysToRemoveAfterUnlink =
|
||||
(typeof STORAGE_KEYS_TO_REMOVE_AFTER_UNLINK)[number];
|
||||
|
||||
export type AssertStorageUnlinkKeysDoNotOverlap = AssertTrue<
|
||||
AssertSameMembers<
|
||||
Extract<StorageKeysToPreserveAfterUnlink, StorageKeysToRemoveAfterUnlink>,
|
||||
never
|
||||
>
|
||||
>;
|
||||
|
||||
export type AssertStorageUnlinkKeysAreExhaustive = AssertTrue<
|
||||
AssertSameMembers<
|
||||
StorageKeysToPreserveAfterUnlink | StorageKeysToRemoveAfterUnlink,
|
||||
keyof StorageAccessType
|
||||
>
|
||||
>;
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { z } from 'zod';
|
||||
import type { StorageAccessType } from './Storage.d.ts';
|
||||
|
||||
export const themeSettingSchema = z.enum(['system', 'light', 'dark']);
|
||||
export type ThemeSettingType = z.infer<typeof themeSettingSchema>;
|
||||
|
||||
// Configuration keys that only affect UI
|
||||
export const STORAGE_UI_KEYS: ReadonlyArray<keyof StorageAccessType> = [
|
||||
'always-relay-calls',
|
||||
'audio-notification',
|
||||
'audioMessage',
|
||||
'auto-download-update',
|
||||
'autoConvertEmoji',
|
||||
'badge-count-muted-conversations',
|
||||
'call-ringtone-notification',
|
||||
'call-system-notification',
|
||||
'customColors',
|
||||
'defaultConversationColor',
|
||||
'existingOnboardingStoryMessageIds',
|
||||
'hasCompletedSafetyNumberOnboarding',
|
||||
'hasCompletedUsernameLinkOnboarding',
|
||||
'hide-menu-bar',
|
||||
'incoming-call-notification',
|
||||
'navTabsCollapsed',
|
||||
'notification-draw-attention',
|
||||
'notification-setting',
|
||||
'pinnedConversationIds',
|
||||
'preferred-audio-input-device',
|
||||
'preferred-audio-output-device',
|
||||
'preferred-video-input-device',
|
||||
'preferredLeftPaneWidth',
|
||||
'preferredReactionEmoji',
|
||||
'sent-media-quality',
|
||||
'showStickerPickerHint',
|
||||
'showStickersIntroduction',
|
||||
'emojiSkinToneDefault',
|
||||
'textFormatting',
|
||||
'version',
|
||||
'zoomFactor',
|
||||
];
|
||||
@@ -15,3 +15,12 @@ export type LocaleEmojiType = z.infer<typeof LocaleEmojiSchema>;
|
||||
export const LocaleEmojiListSchema = LocaleEmojiSchema.array();
|
||||
|
||||
export type LocaleEmojiListType = z.infer<typeof LocaleEmojiListSchema>;
|
||||
|
||||
export enum EmojiSkinTone {
|
||||
None = 'EmojiSkinTone.None',
|
||||
Type1 = 'EmojiSkinTone.Type1', // 1F3FB
|
||||
Type2 = 'EmojiSkinTone.Type2', // 1F3FC
|
||||
Type3 = 'EmojiSkinTone.Type3', // 1F3FD
|
||||
Type4 = 'EmojiSkinTone.Type4', // 1F3FE
|
||||
Type5 = 'EmojiSkinTone.Type5', // 1F3FF
|
||||
}
|
||||
|
||||
@@ -943,7 +943,7 @@ export abstract class Updater {
|
||||
'getItemById',
|
||||
'auto-download-update'
|
||||
);
|
||||
return result?.value ?? true;
|
||||
return typeof result?.value === 'boolean' ? result.value : true;
|
||||
} catch (error) {
|
||||
this.logger.warn(
|
||||
'getAutoDownloadUpdateSetting: Failed to fetch, returning false',
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ipcRenderer } from 'electron';
|
||||
import type { SystemPreferences } from 'electron';
|
||||
import lodash from 'lodash';
|
||||
|
||||
import type { ZoomFactorType } from '../types/Storage.d.ts';
|
||||
import type { ZoomFactorType } from '../types/StorageKeys.std.js';
|
||||
import * as Errors from '../types/errors.std.js';
|
||||
import * as Stickers from '../types/Stickers.preload.js';
|
||||
import * as Settings from '../types/Settings.std.js';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { z } from 'zod';
|
||||
import { missingCaseError } from './missingCaseError.std.js';
|
||||
import { ThemeType } from '../types/Util.std.js';
|
||||
|
||||
@@ -9,6 +10,9 @@ export enum Theme {
|
||||
Dark,
|
||||
}
|
||||
|
||||
export const themeSettingSchema = z.enum(['system', 'light', 'dark']);
|
||||
export type ThemeSettingType = z.infer<typeof themeSettingSchema>;
|
||||
|
||||
export function themeClassName(theme: Theme): string {
|
||||
switch (theme) {
|
||||
case Theme.Light:
|
||||
|
||||
Reference in New Issue
Block a user