Sticker pack download: require just one successful sticker download

This commit is contained in:
Scott Nonnenberg 2022-03-16 12:18:16 -07:00 committed by GitHub
parent 3620309f22
commit 5a7196e464
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 159 additions and 57 deletions

View file

@ -5756,6 +5756,7 @@ button.module-image__border-overlay:focus {
grid-gap: 8px;
grid-template-columns: repeat(4, 1fr);
overflow-y: auto;
overflow-x: hidden;
padding: 0 16px;
&::after {

View file

@ -692,13 +692,6 @@ export async function startApp(): Promise<void> {
await window.Signal.Services.eraseAllStorageServiceState();
}
if (
lastVersion === 'v1.40.0-beta.1' &&
window.isAfterVersion(lastVersion, 'v1.40.0-beta.1')
) {
await window.Signal.Data.clearAllErrorStickerPackAttempts();
}
if (window.isBeforeVersion(lastVersion, 'v5.2.0')) {
const legacySenderCertificateStorageKey = 'senderCertificateWithUuid';
await removeStorageKeyJobQueue.add({
@ -715,6 +708,10 @@ export async function startApp(): Promise<void> {
await window.storage.remove(GROUP_CREDENTIALS_KEY);
}
if (window.isBeforeVersion(lastVersion, 'v5.37.0-alpha')) {
await window.Signal.Data.clearAllErrorStickerPackAttempts();
}
// This one should always be last - it could restart the app
if (window.isBeforeVersion(lastVersion, 'v5.30.0-alpha')) {
await deleteAllLogs();

View file

@ -17,27 +17,30 @@ import {
const i18n = setupI18n('en', enMessages);
storiesOf('Components/Stickers/StickerPreviewModal', module).add('Full', () => {
const book = storiesOf('Components/Stickers/StickerPreviewModal', module);
const abeSticker = {
id: -1,
emoji: '🎩',
url: squareStickerUrl,
packId: 'abe',
};
const wideSticker = {
id: -2,
emoji: '🤯',
url: landscapeGreenUrl,
packId: 'wide',
};
const tallSticker = {
id: -3,
emoji: '🔥',
url: portraitTealUrl,
packId: 'tall',
};
book.add('Full', () => {
const title = text('title', 'Foo');
const author = text('author', 'Foo McBarrington');
const abeSticker = {
id: -1,
emoji: '🎩',
url: squareStickerUrl,
packId: 'abe',
};
const wideSticker = {
id: -2,
emoji: '🤯',
url: landscapeGreenUrl,
packId: 'wide',
};
const tallSticker = {
id: -3,
emoji: '🔥',
url: portraitTealUrl,
packId: 'tall',
};
const pack = {
id: 'foo',
@ -69,3 +72,59 @@ storiesOf('Components/Stickers/StickerPreviewModal', module).add('Full', () => {
/>
);
});
book.add('Just four stickers', () => {
const title = text('title', 'Foo');
const author = text('author', 'Foo McBarrington');
const pack = {
id: 'foo',
key: 'foo',
lastUsed: Date.now(),
cover: abeSticker,
title,
isBlessed: true,
author,
status: 'downloaded' as const,
stickerCount: 101,
stickers: [abeSticker, abeSticker, abeSticker, abeSticker],
};
return (
<StickerPreviewModal
onClose={action('onClose')}
installStickerPack={action('installStickerPack')}
uninstallStickerPack={action('uninstallStickerPack')}
downloadStickerPack={action('downloadStickerPack')}
i18n={i18n}
pack={pack}
/>
);
});
book.add('Initial download', () => {
return (
<StickerPreviewModal
onClose={action('onClose')}
installStickerPack={action('installStickerPack')}
uninstallStickerPack={action('uninstallStickerPack')}
downloadStickerPack={action('downloadStickerPack')}
i18n={i18n}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
pack={{} as any}
/>
);
});
book.add('Pack deleted', () => {
return (
<StickerPreviewModal
onClose={action('onClose')}
installStickerPack={action('installStickerPack')}
uninstallStickerPack={action('uninstallStickerPack')}
downloadStickerPack={action('downloadStickerPack')}
i18n={i18n}
pack={undefined}
/>
);
});

View file

@ -28,6 +28,10 @@ export type OwnProps = {
export type Props = OwnProps;
function renderBody({ pack, i18n }: Props) {
if (!pack) {
return null;
}
if (pack && pack.status === 'error') {
return (
<div className="module-sticker-manager__preview-modal__container__error">
@ -36,7 +40,7 @@ function renderBody({ pack, i18n }: Props) {
);
}
if (!pack || pack.stickerCount === 0 || !isNumber(pack.stickerCount)) {
if (pack.stickerCount === 0 || !isNumber(pack.stickerCount)) {
return <Spinner svgSize="normal" />;
}
@ -54,15 +58,16 @@ function renderBody({ pack, i18n }: Props) {
/>
</div>
))}
{range(pack.stickerCount - pack.stickers.length).map(i => (
<div
key={`placeholder-${i}`}
className={classNames(
'module-sticker-manager__preview-modal__container__sticker-grid__cell',
'module-sticker-manager__preview-modal__container__sticker-grid__cell--placeholder'
)}
/>
))}
{pack.status === 'pending' &&
range(pack.stickerCount - pack.stickers.length).map(i => (
<div
key={`placeholder-${i}`}
className={classNames(
'module-sticker-manager__preview-modal__container__sticker-grid__cell',
'module-sticker-manager__preview-modal__container__sticker-grid__cell--placeholder'
)}
/>
))}
</div>
);
}
@ -110,6 +115,12 @@ export const StickerPreviewModal = React.memo((props: Props) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
React.useEffect(() => {
if (!pack) {
onClose();
}
}, [pack, onClose]);
const isInstalled = Boolean(pack && pack.status === 'installed');
const handleToggleInstall = React.useCallback(() => {
if (!pack) {

View file

@ -10,6 +10,7 @@ import { dropNull } from '../util/dropNull';
import { makeLookup } from '../util/makeLookup';
import { maybeParseUrl } from '../util/url';
import * as Bytes from '../Bytes';
import * as Errors from './errors';
import { deriveStickerPackKey, decryptAttachment } from '../Crypto';
import type { MIMEType } from './MIME';
import { IMAGE_WEBP } from './MIME';
@ -224,20 +225,17 @@ function doesPackNeedDownload(pack?: StickerPackType): boolean {
}
const { status, stickerCount } = pack;
const stickersDownloaded = Object.keys(pack.stickers || {}).length;
if (
(status === 'installed' || status === 'downloaded') &&
stickerCount > 0 &&
stickersDownloaded >= stickerCount
) {
if ((status === 'installed' || status === 'downloaded') && stickerCount > 0) {
return false;
}
// If we don't understand a pack's status, we'll download it
// If a pack has any other status, we'll download it
// If a pack has zero stickers in it, we'll download it
// If a pack doesn't have enough downloaded stickers, we'll download it
// Note: If a pack downloaded with less than the expected number of stickers, we're
// okay with that.
return true;
}
@ -439,10 +437,20 @@ export async function downloadEphemeralPack(
const downloadStickerJob = async (
stickerProto: Proto.StickerPack.ISticker
): Promise<void> => {
const stickerInfo = await downloadSticker(packId, packKey, stickerProto, {
ephemeral: true,
});
): Promise<boolean> => {
let stickerInfo;
try {
stickerInfo = await downloadSticker(packId, packKey, stickerProto, {
ephemeral: true,
});
} catch (error: unknown) {
log.error(
`downloadEphemeralPack/downloadStickerJob error: ${Errors.toLogFormat(
error
)}`
);
return false;
}
const sticker = {
...stickerInfo,
isCoverOnly: !coverIncludedInList && stickerInfo.id === coverStickerId,
@ -458,15 +466,21 @@ export async function downloadEphemeralPack(
}
stickerAdded(sticker);
return true;
};
// Download the cover first
await downloadStickerJob(coverProto);
// Then the rest
await pMap(nonCoverStickers, downloadStickerJob, {
const jobResults = await pMap(nonCoverStickers, downloadStickerJob, {
concurrency: 3,
});
const successfulStickerCount = jobResults.filter(item => item).length;
if (successfulStickerCount === 0) {
throw new Error('downloadEphemeralPack: All stickers failed to download');
}
} catch (error) {
// Because the user could install this pack while we are still downloading this
// ephemeral pack, we don't want to go change its status unless we're still in
@ -657,24 +671,44 @@ async function doDownloadStickerPack(
try {
const downloadStickerJob = async (
stickerProto: Proto.StickerPack.ISticker
): Promise<void> => {
const stickerInfo = await downloadSticker(packId, packKey, stickerProto);
const sticker = {
...stickerInfo,
isCoverOnly: !coverIncludedInList && stickerInfo.id === coverStickerId,
};
await Data.createOrUpdateSticker(sticker);
stickerAdded(sticker);
): Promise<boolean> => {
try {
const stickerInfo = await downloadSticker(
packId,
packKey,
stickerProto
);
const sticker = {
...stickerInfo,
isCoverOnly:
!coverIncludedInList && stickerInfo.id === coverStickerId,
};
await Data.createOrUpdateSticker(sticker);
stickerAdded(sticker);
return true;
} catch (error: unknown) {
log.error(
`doDownloadStickerPack/downloadStickerJob error: ${Errors.toLogFormat(
error
)}`
);
return false;
}
};
// Download the cover first
await downloadStickerJob(coverProto);
// Then the rest
await pMap(nonCoverStickers, downloadStickerJob, {
const jobResults = await pMap(nonCoverStickers, downloadStickerJob, {
concurrency: 3,
});
const successfulStickerCount = jobResults.filter(item => item).length;
if (successfulStickerCount === 0) {
throw new Error('doDownloadStickerPack: All stickers failed to download');
}
// Allow for the user marking this pack as installed in the middle of our download;
// don't overwrite that status.
const existingStatus = getStickerPackStatus(packId);