Refresh the sticker pack manager

This commit is contained in:
Josh Perez 2022-12-13 19:06:15 -05:00 committed by GitHub
parent fdfc0539a3
commit 94875efaf6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 550 additions and 627 deletions

View file

@ -1,19 +1,20 @@
// Copyright 2019-2020 Signal Messenger, LLC
// Copyright 2019-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { createPortal } from 'react-dom';
import { isNumber, range } from 'lodash';
import classNames from 'classnames';
import { StickerPackInstallButton } from './StickerPackInstallButton';
import { ConfirmationDialog } from '../ConfirmationDialog';
import type { LocalizerType } from '../../types/Util';
import type { StickerPackType } from '../../state/ducks/stickers';
import { Spinner } from '../Spinner';
import { useRestoreFocus } from '../../hooks/useRestoreFocus';
import { Modal } from '../Modal';
import { Button, ButtonVariant } from '../Button';
export type OwnProps = {
readonly closeStickerPackPreview: () => unknown;
readonly onClose?: () => unknown;
readonly closeStickerPackPreview: (packId: string) => unknown;
readonly downloadStickerPack: (
packId: string,
packKey: string,
@ -34,7 +35,7 @@ function renderBody({ pack, i18n }: Props) {
if (pack && pack.status === 'error') {
return (
<div className="module-sticker-manager__preview-modal__container__error">
<div className="module-sticker-manager__preview-modal__error">
{i18n('stickers--StickerPreview--Error')}
</div>
);
@ -45,29 +46,28 @@ function renderBody({ pack, i18n }: Props) {
}
return (
<div className="module-sticker-manager__preview-modal__container__sticker-grid">
<div className="module-sticker-manager__preview-modal__sticker-grid">
{pack.stickers.map(({ id, url }) => (
<div
key={id}
className="module-sticker-manager__preview-modal__container__sticker-grid__cell"
className="module-sticker-manager__preview-modal__sticker-grid__cell"
>
<img
className="module-sticker-manager__preview-modal__container__sticker-grid__cell__image"
className="module-sticker-manager__preview-modal__sticker-grid__cell__image"
src={url}
alt={pack.title}
/>
</div>
))}
{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'
)}
/>
))}
{range(pack.stickerCount - pack.stickers.length).map(i => (
<div
key={`placeholder-${i}`}
className={classNames(
'module-sticker-manager__preview-modal__sticker-grid__cell',
'module-sticker-manager__preview-modal__sticker-grid__cell--placeholder'
)}
/>
))}
</div>
);
}
@ -77,28 +77,18 @@ export const StickerPreviewModal = React.memo(function StickerPreviewModalInner(
) {
const {
closeStickerPackPreview,
pack,
i18n,
downloadStickerPack,
i18n,
installStickerPack,
onClose,
pack,
uninstallStickerPack,
} = props;
const [root, setRoot] = React.useState<HTMLElement | null>(null);
const [confirmingUninstall, setConfirmingUninstall] = React.useState(false);
// Restore focus on teardown
const [focusRef] = useRestoreFocus();
React.useEffect(() => {
const div = document.createElement('div');
document.body.appendChild(div);
setRoot(div);
return () => {
document.body.removeChild(div);
};
}, []);
React.useEffect(() => {
if (pack && pack.status === 'known') {
downloadStickerPack(pack.id, pack.key);
@ -117,11 +107,12 @@ export const StickerPreviewModal = React.memo(function StickerPreviewModalInner(
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
React.useEffect(() => {
if (!pack) {
closeStickerPackPreview();
const handleClose = React.useCallback(() => {
if (pack?.id) {
closeStickerPackPreview(pack.id);
}
}, [pack, closeStickerPackPreview]);
onClose?.();
}, [closeStickerPackPreview, onClose, pack]);
const isInstalled = Boolean(pack && pack.status === 'installed');
const handleToggleInstall = React.useCallback(() => {
@ -132,16 +123,16 @@ export const StickerPreviewModal = React.memo(function StickerPreviewModalInner(
setConfirmingUninstall(true);
} else if (pack.status === 'ephemeral') {
downloadStickerPack(pack.id, pack.key, { finalStatus: 'installed' });
closeStickerPackPreview();
handleClose();
} else {
installStickerPack(pack.id, pack.key);
closeStickerPackPreview();
handleClose();
}
}, [
downloadStickerPack,
installStickerPack,
isInstalled,
closeStickerPackPreview,
handleClose,
pack,
setConfirmingUninstall,
]);
@ -155,100 +146,70 @@ export const StickerPreviewModal = React.memo(function StickerPreviewModalInner(
// closeStickerPackPreview is called by <ConfirmationDialog />'s onClose
}, [uninstallStickerPack, setConfirmingUninstall, pack]);
React.useEffect(() => {
const handler = ({ key }: KeyboardEvent) => {
if (key === 'Escape') {
closeStickerPackPreview();
}
};
const buttonLabel = isInstalled
? i18n('stickers--StickerManager--Uninstall')
: i18n('stickers--StickerManager--Install');
document.addEventListener('keydown', handler);
return () => {
document.removeEventListener('keydown', handler);
};
}, [closeStickerPackPreview]);
const handleClickToClose = React.useCallback(
(e: React.MouseEvent) => {
if (e.target === e.currentTarget) {
closeStickerPackPreview();
}
},
[closeStickerPackPreview]
);
return root
? createPortal(
// Not really a button. Just a background which can be clicked to close modal
// eslint-disable-next-line max-len
// eslint-disable-next-line jsx-a11y/interactive-supports-focus, jsx-a11y/click-events-have-key-events
<div
role="button"
className="module-sticker-manager__preview-modal__overlay"
onClick={handleClickToClose}
>
{confirmingUninstall ? (
<ConfirmationDialog
dialogName="StickerPreviewModal.confirmUninstall"
i18n={i18n}
onClose={closeStickerPackPreview}
actions={[
{
style: 'negative',
text: i18n('stickers--StickerManager--Uninstall'),
action: handleUninstall,
},
]}
>
{i18n('stickers--StickerManager--UninstallWarning')}
</ConfirmationDialog>
const modalFooter =
pack && pack.status !== 'error' ? (
<div className="module-sticker-manager__preview-modal__footer">
<div className="module-sticker-manager__preview-modal__footer--info">
<h3 className="module-sticker-manager__preview-modal__footer--title">
{pack.title}
{pack.isBlessed ? (
<span className="module-sticker-manager__preview-modal__footer--blessed-icon" />
) : null}
</h3>
<h4 className="module-sticker-manager__preview-modal__footer--author">
{pack.author}
</h4>
</div>
<div className="module-sticker-manager__preview-modal__footer--install">
{pack.status === 'pending' ? (
<Spinner svgSize="small" size="14px" />
) : (
<div className="module-sticker-manager__preview-modal__container">
<header className="module-sticker-manager__preview-modal__container__header">
<h2 className="module-sticker-manager__preview-modal__container__header__text">
{i18n('stickers--StickerPreview--Title')}
</h2>
<button
type="button"
onClick={closeStickerPackPreview}
className="module-sticker-manager__preview-modal__container__header__close-button"
aria-label={i18n('close')}
/>
</header>
{renderBody(props)}
{pack && pack.status !== 'error' ? (
<div className="module-sticker-manager__preview-modal__container__meta-overlay">
<div className="module-sticker-manager__preview-modal__container__meta-overlay__info">
<h3 className="module-sticker-manager__preview-modal__container__meta-overlay__info__title">
{pack.title}
{pack.isBlessed ? (
<span className="module-sticker-manager__preview-modal__container__meta-overlay__info__blessed-icon" />
) : null}
</h3>
<h4 className="module-sticker-manager__preview-modal__container__meta-overlay__info__author">
{pack.author}
</h4>
</div>
<div className="module-sticker-manager__preview-modal__container__meta-overlay__install">
{pack.status === 'pending' ? (
<Spinner svgSize="small" size="14px" />
) : (
<StickerPackInstallButton
ref={focusRef}
installed={isInstalled}
i18n={i18n}
onClick={handleToggleInstall}
blue
/>
)}
</div>
</div>
) : null}
</div>
<Button
aria-label={buttonLabel}
ref={focusRef}
onClick={handleToggleInstall}
variant={ButtonVariant.Primary}
>
{buttonLabel}
</Button>
)}
</div>,
root
)
: null;
</div>
</div>
) : undefined;
return (
<>
{confirmingUninstall && (
<ConfirmationDialog
dialogName="StickerPreviewModal.confirmUninstall"
actions={[
{
style: 'negative',
text: i18n('stickers--StickerManager--Uninstall'),
action: handleUninstall,
},
]}
i18n={i18n}
onClose={() => setConfirmingUninstall(false)}
>
{i18n('stickers--StickerManager--UninstallWarning')}
</ConfirmationDialog>
)}
<Modal
hasXButton
i18n={i18n}
modalFooter={modalFooter}
modalName="StickerPreviewModal"
moduleClassName="module-sticker-manager__preview-modal__modal"
onClose={handleClose}
title={i18n('stickers--StickerPreview--Title')}
>
{renderBody(props)}
</Modal>
</>
);
});