Stickers in storage service

This commit is contained in:
Fedor Indutny 2022-08-03 10:10:49 -07:00 committed by GitHub
parent d8a7e99c81
commit b47a906211
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1216 additions and 80 deletions

View file

@ -21,11 +21,13 @@ import {
mergeGroupV1Record,
mergeGroupV2Record,
mergeStoryDistributionListRecord,
mergeStickerPackRecord,
toAccountRecord,
toContactRecord,
toGroupV1Record,
toGroupV2Record,
toStoryDistributionListRecord,
toStickerPackRecord,
} from './storageRecordOps';
import type { MergeResultType } from './storageRecordOps';
import { MAX_READ_KEYS } from './storageConstants';
@ -52,7 +54,12 @@ import type {
UnknownRecord,
} from '../types/StorageService.d';
import MessageSender from '../textsecure/SendMessage';
import type { StoryDistributionWithMembersType } from '../sql/Interface';
import type {
StoryDistributionWithMembersType,
StorageServiceFieldsType,
StickerPackType,
UninstalledStickerPackType,
} from '../sql/Interface';
import { MY_STORIES_ID } from '../types/Stories';
type IManifestRecordIdentifier = Proto.ManifestRecord.IIdentifier;
@ -322,11 +329,15 @@ async function generateManifest(
}
}
const storyDistributionLists =
await dataInterface.getAllStoryDistributionsWithMembers();
const {
storyDistributionLists,
installedStickerPacks,
uninstalledStickerPacks,
} = await getNonConversationRecords();
log.info(
`storageService.upload(${version}): adding storyDistributionLists=${storyDistributionLists.length}`
`storageService.upload(${version}): ` +
`adding storyDistributionLists=${storyDistributionLists.length}`
);
storyDistributionLists.forEach(storyDistributionList => {
@ -355,6 +366,81 @@ async function generateManifest(
}
});
log.info(
`storageService.upload(${version}): ` +
`adding uninstalled stickerPacks=${uninstalledStickerPacks.length}`
);
const uninstalledStickerPackIds = new Set<string>();
uninstalledStickerPacks.forEach(stickerPack => {
const storageRecord = new Proto.StorageRecord();
storageRecord.stickerPack = toStickerPackRecord(stickerPack);
uninstalledStickerPackIds.add(stickerPack.id);
const { isNewItem, storageID } = processStorageRecord({
currentStorageID: stickerPack.storageID,
currentStorageVersion: stickerPack.storageVersion,
identifierType: ITEM_TYPE.STICKER_PACK,
storageNeedsSync: stickerPack.storageNeedsSync,
storageRecord,
});
if (isNewItem) {
postUploadUpdateFunctions.push(() => {
dataInterface.addUninstalledStickerPack({
...stickerPack,
storageID,
storageVersion: version,
storageNeedsSync: false,
});
});
}
});
log.info(
`storageService.upload(${version}): ` +
`adding installed stickerPacks=${installedStickerPacks.length}`
);
installedStickerPacks.forEach(stickerPack => {
if (uninstalledStickerPackIds.has(stickerPack.id)) {
log.error(
`storageService.upload(${version}): ` +
`sticker pack ${stickerPack.id} is both installed and uninstalled`
);
window.reduxActions.stickers.uninstallStickerPack(
stickerPack.id,
stickerPack.key,
{ fromSync: true }
);
return;
}
const storageRecord = new Proto.StorageRecord();
storageRecord.stickerPack = toStickerPackRecord(stickerPack);
const { isNewItem, storageID } = processStorageRecord({
currentStorageID: stickerPack.storageID,
currentStorageVersion: stickerPack.storageVersion,
identifierType: ITEM_TYPE.STICKER_PACK,
storageNeedsSync: stickerPack.storageNeedsSync,
storageRecord,
});
if (isNewItem) {
postUploadUpdateFunctions.push(() => {
dataInterface.createOrUpdateStickerPack({
...stickerPack,
storageID,
storageVersion: version,
storageNeedsSync: false,
});
});
}
});
const unknownRecordsArray: ReadonlyArray<UnknownRecord> = (
window.storage.get('storage-service-unknown-records') || []
).filter((record: UnknownRecord) => !validRecordTypes.has(record.itemType));
@ -858,6 +944,15 @@ async function mergeRecord(
storageVersion,
storageRecord.storyDistributionList
);
} else if (
itemType === ITEM_TYPE.STICKER_PACK &&
storageRecord.stickerPack
) {
mergeResult = await mergeStickerPackRecord(
storageID,
storageVersion,
storageRecord.stickerPack
);
} else {
isUnsupported = true;
log.warn(
@ -914,6 +1009,31 @@ async function mergeRecord(
};
}
type NonConversationRecordsResultType = Readonly<{
installedStickerPacks: ReadonlyArray<StickerPackType>;
uninstalledStickerPacks: ReadonlyArray<UninstalledStickerPackType>;
storyDistributionLists: ReadonlyArray<StoryDistributionWithMembersType>;
}>;
// TODO: DESKTOP-3929
async function getNonConversationRecords(): Promise<NonConversationRecordsResultType> {
const [
storyDistributionLists,
uninstalledStickerPacks,
installedStickerPacks,
] = await Promise.all([
dataInterface.getAllStoryDistributionsWithMembers(),
dataInterface.getUninstalledStickerPacks(),
dataInterface.getInstalledStickerPacks(),
]);
return {
storyDistributionLists,
uninstalledStickerPacks,
installedStickerPacks,
};
}
async function processManifest(
manifest: Proto.IManifestRecord,
version: number
@ -930,6 +1050,7 @@ async function processManifest(
const remoteKeys = new Set(remoteKeysTypeMap.keys());
const localVersions = new Map<string, number | undefined>();
let localRecordCount = 0;
const conversations = window.getConversations();
conversations.forEach((conversation: ConversationModel) => {
@ -938,6 +1059,33 @@ async function processManifest(
localVersions.set(storageID, conversation.get('storageVersion'));
}
});
localRecordCount += conversations.length;
{
const {
storyDistributionLists,
installedStickerPacks,
uninstalledStickerPacks,
} = await getNonConversationRecords();
const collectLocalKeysFromFields = ({
storageID,
storageVersion,
}: StorageServiceFieldsType): void => {
if (storageID) {
localVersions.set(storageID, storageVersion);
}
};
storyDistributionLists.forEach(collectLocalKeysFromFields);
localRecordCount += storyDistributionLists.length;
uninstalledStickerPacks.forEach(collectLocalKeysFromFields);
localRecordCount += uninstalledStickerPacks.length;
installedStickerPacks.forEach(collectLocalKeysFromFields);
localRecordCount += installedStickerPacks.length;
}
const unknownRecordsArray: ReadonlyArray<UnknownRecord> =
window.storage.get('storage-service-unknown-records') || [];
@ -973,7 +1121,7 @@ async function processManifest(
);
log.info(
`storageService.process(${version}): localRecords=${conversations.length} ` +
`storageService.process(${version}): localRecords=${localRecordCount} ` +
`localKeys=${localVersions.size} unknownKeys=${stillUnknown.length} ` +
`remoteKeys=${remoteKeys.size}`
);
@ -1025,33 +1173,96 @@ async function processManifest(
}
});
// Check to make sure we have a "My Stories" distribution list set up
const myStories = await dataInterface.getStoryDistributionWithMembers(
MY_STORIES_ID
);
// Refetch various records post-merge
{
const {
storyDistributionLists,
installedStickerPacks,
uninstalledStickerPacks,
} = await getNonConversationRecords();
if (!myStories) {
const storyDistribution: StoryDistributionWithMembersType = {
allowsReplies: true,
id: MY_STORIES_ID,
isBlockList: true,
members: [],
name: MY_STORIES_ID,
senderKeyInfo: undefined,
storageNeedsSync: true,
};
uninstalledStickerPacks.forEach(stickerPack => {
const { storageID, storageVersion } = stickerPack;
if (!storageID || remoteKeys.has(storageID)) {
return;
}
await dataInterface.createNewStoryDistribution(storyDistribution);
const missingKey = redactStorageID(storageID, storageVersion);
log.info(
`storageService.process(${version}): localKey=${missingKey} was not ` +
'in remote manifest'
);
dataInterface.addUninstalledStickerPack({
...stickerPack,
storageID: undefined,
storageVersion: undefined,
});
});
const shouldSave = false;
window.reduxActions.storyDistributionLists.createDistributionList(
storyDistribution.name,
storyDistribution.members,
storyDistribution,
shouldSave
installedStickerPacks.forEach(stickerPack => {
const { storageID, storageVersion } = stickerPack;
if (!storageID || remoteKeys.has(storageID)) {
return;
}
const missingKey = redactStorageID(storageID, storageVersion);
log.info(
`storageService.process(${version}): localKey=${missingKey} was not ` +
'in remote manifest'
);
dataInterface.createOrUpdateStickerPack({
...stickerPack,
storageID: undefined,
storageVersion: undefined,
});
});
storyDistributionLists.forEach(storyDistributionList => {
const { storageID, storageVersion } = storyDistributionList;
if (!storageID || remoteKeys.has(storageID)) {
return;
}
const missingKey = redactStorageID(storageID, storageVersion);
log.info(
`storageService.process(${version}): localKey=${missingKey} was not ` +
'in remote manifest'
);
dataInterface.modifyStoryDistribution({
...storyDistributionList,
storageID: undefined,
storageVersion: undefined,
});
});
// Check to make sure we have a "My Stories" distribution list set up
const myStories = storyDistributionLists.find(
({ id }) => id === MY_STORIES_ID
);
conflictCount += 1;
if (!myStories) {
const storyDistribution: StoryDistributionWithMembersType = {
allowsReplies: true,
id: MY_STORIES_ID,
isBlockList: true,
members: [],
name: MY_STORIES_ID,
senderKeyInfo: undefined,
storageNeedsSync: true,
};
await dataInterface.createNewStoryDistribution(storyDistribution);
const shouldSave = false;
window.reduxActions.storyDistributionLists.createDistributionList(
storyDistribution.name,
storyDistribution.members,
storyDistribution,
shouldSave
);
conflictCount += 1;
}
}
log.info(