Sticker pack download: require just one successful sticker download
This commit is contained in:
parent
3620309f22
commit
5a7196e464
5 changed files with 159 additions and 57 deletions
|
@ -5756,6 +5756,7 @@ button.module-image__border-overlay:focus {
|
||||||
grid-gap: 8px;
|
grid-gap: 8px;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(4, 1fr);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
|
|
|
@ -692,13 +692,6 @@ export async function startApp(): Promise<void> {
|
||||||
await window.Signal.Services.eraseAllStorageServiceState();
|
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')) {
|
if (window.isBeforeVersion(lastVersion, 'v5.2.0')) {
|
||||||
const legacySenderCertificateStorageKey = 'senderCertificateWithUuid';
|
const legacySenderCertificateStorageKey = 'senderCertificateWithUuid';
|
||||||
await removeStorageKeyJobQueue.add({
|
await removeStorageKeyJobQueue.add({
|
||||||
|
@ -715,6 +708,10 @@ export async function startApp(): Promise<void> {
|
||||||
await window.storage.remove(GROUP_CREDENTIALS_KEY);
|
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
|
// This one should always be last - it could restart the app
|
||||||
if (window.isBeforeVersion(lastVersion, 'v5.30.0-alpha')) {
|
if (window.isBeforeVersion(lastVersion, 'v5.30.0-alpha')) {
|
||||||
await deleteAllLogs();
|
await deleteAllLogs();
|
||||||
|
|
|
@ -17,27 +17,30 @@ import {
|
||||||
|
|
||||||
const i18n = setupI18n('en', enMessages);
|
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 title = text('title', 'Foo');
|
||||||
const author = text('author', 'Foo McBarrington');
|
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 = {
|
const pack = {
|
||||||
id: 'foo',
|
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}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
|
@ -28,6 +28,10 @@ export type OwnProps = {
|
||||||
export type Props = OwnProps;
|
export type Props = OwnProps;
|
||||||
|
|
||||||
function renderBody({ pack, i18n }: Props) {
|
function renderBody({ pack, i18n }: Props) {
|
||||||
|
if (!pack) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (pack && pack.status === 'error') {
|
if (pack && pack.status === 'error') {
|
||||||
return (
|
return (
|
||||||
<div className="module-sticker-manager__preview-modal__container__error">
|
<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" />;
|
return <Spinner svgSize="normal" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,15 +58,16 @@ function renderBody({ pack, i18n }: Props) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{range(pack.stickerCount - pack.stickers.length).map(i => (
|
{pack.status === 'pending' &&
|
||||||
<div
|
range(pack.stickerCount - pack.stickers.length).map(i => (
|
||||||
key={`placeholder-${i}`}
|
<div
|
||||||
className={classNames(
|
key={`placeholder-${i}`}
|
||||||
'module-sticker-manager__preview-modal__container__sticker-grid__cell',
|
className={classNames(
|
||||||
'module-sticker-manager__preview-modal__container__sticker-grid__cell--placeholder'
|
'module-sticker-manager__preview-modal__container__sticker-grid__cell',
|
||||||
)}
|
'module-sticker-manager__preview-modal__container__sticker-grid__cell--placeholder'
|
||||||
/>
|
)}
|
||||||
))}
|
/>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -110,6 +115,12 @@ export const StickerPreviewModal = React.memo((props: Props) => {
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!pack) {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
}, [pack, onClose]);
|
||||||
|
|
||||||
const isInstalled = Boolean(pack && pack.status === 'installed');
|
const isInstalled = Boolean(pack && pack.status === 'installed');
|
||||||
const handleToggleInstall = React.useCallback(() => {
|
const handleToggleInstall = React.useCallback(() => {
|
||||||
if (!pack) {
|
if (!pack) {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { dropNull } from '../util/dropNull';
|
||||||
import { makeLookup } from '../util/makeLookup';
|
import { makeLookup } from '../util/makeLookup';
|
||||||
import { maybeParseUrl } from '../util/url';
|
import { maybeParseUrl } from '../util/url';
|
||||||
import * as Bytes from '../Bytes';
|
import * as Bytes from '../Bytes';
|
||||||
|
import * as Errors from './errors';
|
||||||
import { deriveStickerPackKey, decryptAttachment } from '../Crypto';
|
import { deriveStickerPackKey, decryptAttachment } from '../Crypto';
|
||||||
import type { MIMEType } from './MIME';
|
import type { MIMEType } from './MIME';
|
||||||
import { IMAGE_WEBP } from './MIME';
|
import { IMAGE_WEBP } from './MIME';
|
||||||
|
@ -224,20 +225,17 @@ function doesPackNeedDownload(pack?: StickerPackType): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { status, stickerCount } = pack;
|
const { status, stickerCount } = pack;
|
||||||
const stickersDownloaded = Object.keys(pack.stickers || {}).length;
|
|
||||||
|
|
||||||
if (
|
if ((status === 'installed' || status === 'downloaded') && stickerCount > 0) {
|
||||||
(status === 'installed' || status === 'downloaded') &&
|
|
||||||
stickerCount > 0 &&
|
|
||||||
stickersDownloaded >= stickerCount
|
|
||||||
) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we don't understand a pack's status, we'll download it
|
// 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 any other status, we'll download it
|
||||||
// If a pack has zero stickers in it, 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -439,10 +437,20 @@ export async function downloadEphemeralPack(
|
||||||
|
|
||||||
const downloadStickerJob = async (
|
const downloadStickerJob = async (
|
||||||
stickerProto: Proto.StickerPack.ISticker
|
stickerProto: Proto.StickerPack.ISticker
|
||||||
): Promise<void> => {
|
): Promise<boolean> => {
|
||||||
const stickerInfo = await downloadSticker(packId, packKey, stickerProto, {
|
let stickerInfo;
|
||||||
ephemeral: true,
|
try {
|
||||||
});
|
stickerInfo = await downloadSticker(packId, packKey, stickerProto, {
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
} catch (error: unknown) {
|
||||||
|
log.error(
|
||||||
|
`downloadEphemeralPack/downloadStickerJob error: ${Errors.toLogFormat(
|
||||||
|
error
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const sticker = {
|
const sticker = {
|
||||||
...stickerInfo,
|
...stickerInfo,
|
||||||
isCoverOnly: !coverIncludedInList && stickerInfo.id === coverStickerId,
|
isCoverOnly: !coverIncludedInList && stickerInfo.id === coverStickerId,
|
||||||
|
@ -458,15 +466,21 @@ export async function downloadEphemeralPack(
|
||||||
}
|
}
|
||||||
|
|
||||||
stickerAdded(sticker);
|
stickerAdded(sticker);
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Download the cover first
|
// Download the cover first
|
||||||
await downloadStickerJob(coverProto);
|
await downloadStickerJob(coverProto);
|
||||||
|
|
||||||
// Then the rest
|
// Then the rest
|
||||||
await pMap(nonCoverStickers, downloadStickerJob, {
|
const jobResults = await pMap(nonCoverStickers, downloadStickerJob, {
|
||||||
concurrency: 3,
|
concurrency: 3,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const successfulStickerCount = jobResults.filter(item => item).length;
|
||||||
|
if (successfulStickerCount === 0) {
|
||||||
|
throw new Error('downloadEphemeralPack: All stickers failed to download');
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Because the user could install this pack while we are still downloading this
|
// 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
|
// ephemeral pack, we don't want to go change its status unless we're still in
|
||||||
|
@ -657,24 +671,44 @@ async function doDownloadStickerPack(
|
||||||
try {
|
try {
|
||||||
const downloadStickerJob = async (
|
const downloadStickerJob = async (
|
||||||
stickerProto: Proto.StickerPack.ISticker
|
stickerProto: Proto.StickerPack.ISticker
|
||||||
): Promise<void> => {
|
): Promise<boolean> => {
|
||||||
const stickerInfo = await downloadSticker(packId, packKey, stickerProto);
|
try {
|
||||||
const sticker = {
|
const stickerInfo = await downloadSticker(
|
||||||
...stickerInfo,
|
packId,
|
||||||
isCoverOnly: !coverIncludedInList && stickerInfo.id === coverStickerId,
|
packKey,
|
||||||
};
|
stickerProto
|
||||||
await Data.createOrUpdateSticker(sticker);
|
);
|
||||||
stickerAdded(sticker);
|
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
|
// Download the cover first
|
||||||
await downloadStickerJob(coverProto);
|
await downloadStickerJob(coverProto);
|
||||||
|
|
||||||
// Then the rest
|
// Then the rest
|
||||||
await pMap(nonCoverStickers, downloadStickerJob, {
|
const jobResults = await pMap(nonCoverStickers, downloadStickerJob, {
|
||||||
concurrency: 3,
|
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;
|
// Allow for the user marking this pack as installed in the middle of our download;
|
||||||
// don't overwrite that status.
|
// don't overwrite that status.
|
||||||
const existingStatus = getStickerPackStatus(packId);
|
const existingStatus = getStickerPackStatus(packId);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue