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

@ -2352,8 +2352,16 @@
"message": "Sticker pack could not be installed",
"description": "Shown in a toast if the user attempts to install a sticker pack and it fails"
},
"stickers--StickerManager--title": {
"message": "Stickers",
"description": "Title for the sticker manager"
},
"stickers--StickerManager--Available": {
"message": "Available",
"description": "Shown in the sticker pack manager as a tab for available stickers"
},
"stickers--StickerManager--InstalledPacks": {
"message": "Installed Stickers",
"message": "Installed",
"description": "Shown in the sticker pack manager above your installed sticker packs."
},
"stickers--StickerManager--InstalledPacks--Empty": {

View file

@ -2713,6 +2713,10 @@ button.ConversationDetails__action-button {
vertical-align: middle;
}
.module-image--hidden {
visibility: hidden;
}
.module-image--tap-to-play,
.module-image--not-downloaded {
align-items: center;
@ -5741,421 +5745,6 @@ button.module-image__border-overlay:focus {
}
}
// Module: StickerManager
.module-sticker-manager {
padding: 0 16px;
outline: none;
}
.module-sticker-manager__text {
height: 18px;
letter-spacing: 0px;
line-height: 18px;
@include light-theme() {
color: $color-gray-60;
}
@include dark-theme() {
color: $color-gray-25;
}
&--heading {
@include font-body-1-bold;
@include light-theme() {
color: $color-gray-90;
}
@include dark-theme() {
color: $color-gray-05;
}
}
}
.module-sticker-manager__empty {
display: flex;
justify-content: center;
align-items: center;
height: 64px;
border-radius: 8px;
@include light-theme {
background: $color-gray-02;
color: $color-gray-60;
}
@include dark-theme {
background: $color-gray-90;
color: $color-gray-25;
}
}
%blessed-sticker-pack-icon {
height: 14px;
width: 14px;
border-radius: 8px;
background-color: $color-white;
display: inline-block;
vertical-align: middle;
margin: {
left: 5px;
bottom: 2px;
}
position: relative;
&::before {
content: '';
display: block;
width: 16px;
height: 16px;
position: absolute;
top: -1px;
left: -1px;
@include light-theme {
@include color-svg(
'../images/icons/v2/check-circle-solid-24.svg',
$color-accent-blue
);
}
@include dark-theme {
@include color-svg(
'../images/icons/v2/check-circle-solid-24.svg',
$color-accent-blue
);
}
}
}
.module-sticker-manager__pack-row {
@include button-reset;
display: flex;
flex-direction: row;
padding: 16px;
@include light-theme {
& + & {
border-top: 1px solid $color-gray-15;
}
}
@include dark-theme {
& + & {
border-top: 1px solid $color-gray-75;
}
}
@include keyboard-mode {
&:focus {
box-shadow: 0px 0px 0px 2px $color-ultramarine;
}
}
&__cover {
width: 48px;
height: 48px;
object-fit: contain;
}
&__cover-placeholder {
width: 48px;
height: 48px;
background: $color-gray-05;
}
&__meta {
flex-grow: 1;
display: flex;
flex-direction: column;
&:not(:first-child) {
padding: 0 12px;
}
&__title {
flex: 1;
}
&__author {
flex: 1;
@include light-theme() {
color: $color-gray-45;
}
@include dark-theme() {
color: $color-gray-25;
}
}
&__blessed-icon {
@extend %blessed-sticker-pack-icon;
}
}
&__controls {
flex-shrink: 1;
display: flex;
justify-content: center;
align-items: center;
&__button {
background: none;
border: 0;
&--menu {
&::after {
content: '';
display: block;
min-width: 24px;
min-height: 24px;
@include light-theme {
@include color-svg(
'../images/icons/v2/more-horiz-24.svg',
$color-gray-60
);
}
@include dark-theme {
@include color-svg(
'../images/icons/v2/more-horiz-24.svg',
$color-gray-25
);
}
}
}
}
}
}
.module-sticker-manager__install-button {
background: none;
border: 0;
color: $color-gray-90;
@include font-body-1-bold;
height: 24px;
background: $color-gray-05;
border-radius: 12px;
display: flex;
justify-content: center;
align-items: center;
padding: 0 12px;
@include dark-theme {
color: $color-gray-05;
background: $color-gray-75;
}
@include mouse-mode {
outline: none;
}
&--blue {
@include light-theme {
background: $color-ultramarine;
color: $color-white;
}
@include dark-theme {
background: $color-ultramarine-light;
color: $color-white;
}
}
}
.module-sticker-manager__preview-modal {
&__overlay {
background: $color-black-alpha-40;
position: fixed;
left: 0;
top: 0;
width: var(--window-width);
height: var(--window-height);
display: flex;
justify-content: center;
align-items: center;
z-index: $z-index-popup-overlay;
}
&__container {
position: relative;
border-radius: 8px;
box-shadow: 0 4px 12px 0 $color-black-alpha-20;
width: 440px;
height: 360px;
overflow: hidden;
display: flex;
flex-direction: column;
@include light-theme {
background: $color-white;
}
@include dark-theme {
background: $color-gray-75;
}
&__error {
color: $color-accent-red;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
width: 100%;
height: 100%;
padding: 0 80px 30px 80px;
@include font-body-1-bold;
}
&__header {
display: flex;
flex-direction: row;
flex-shrink: 0;
height: 36px;
padding: 0 8px 0 16px;
justify-content: space-between;
align-items: center;
&__text {
@include font-body-1-bold;
color: $color-gray-90;
@include dark-theme {
color: $color-gray-05;
}
}
&__close-button {
border: none;
width: 20px;
height: 20px;
@include light-theme {
@include color-svg('../images/icons/v2/x-24.svg', $color-gray-60);
}
@include dark-theme {
@include color-svg('../images/icons/v2/x-24.svg', $color-gray-05);
}
}
}
&__sticker-grid {
width: 100%;
display: grid;
grid-gap: 8px;
grid-template-columns: repeat(4, 1fr);
overflow-y: auto;
overflow-x: hidden;
padding: 0 16px;
&::after {
content: '';
display: block;
height: 80px;
grid-column: 1 / span 4;
}
&__cell {
width: 96px;
height: 96px;
display: flex;
justify-content: center;
align-items: center;
&__image {
width: 100%;
height: 100%;
object-fit: contain;
}
&--placeholder {
border-radius: 4px;
@include light-theme() {
background: $color-gray-05;
}
@include dark-theme() {
background: $color-gray-60;
}
}
}
}
&__meta-overlay {
border-radius: 4px;
width: 408px;
height: 52px;
position: absolute;
left: 16px;
bottom: 16px;
padding: 0 12px;
display: flex;
flex-direction: row;
align-items: center;
@include light-theme {
background: $color-gray-05;
}
@include dark-theme {
background: $color-gray-60;
}
&__info {
display: flex;
flex-direction: column;
justify-content: center;
flex-grow: 1;
flex-shrink: 1;
overflow: hidden;
&__title {
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
@include font-body-1-bold;
@include light-theme {
color: $color-gray-90;
}
@include dark-theme {
color: $color-gray-05;
}
}
&__author {
margin: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
@include light-theme {
color: $color-gray-45;
}
@include dark-theme {
color: $color-gray-25;
}
}
&__blessed-icon {
@extend %blessed-sticker-pack-icon;
}
}
&__install {
flex-shrink: 0;
overflow: hidden;
}
}
}
}
// Module: Sticker button (launches the sticker picker)
.sticker-button-wrapper {

View file

@ -0,0 +1,323 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
.module-sticker-manager {
padding: 0 16px;
outline: none;
}
.module-sticker-manager__text {
height: 18px;
letter-spacing: 0px;
line-height: 18px;
padding-left: 8px;
@include light-theme() {
color: $color-gray-60;
}
@include dark-theme() {
color: $color-gray-25;
}
&--heading {
@include font-body-1-bold;
@include light-theme() {
color: $color-gray-90;
}
@include dark-theme() {
color: $color-gray-05;
}
}
}
.module-sticker-manager__empty {
display: flex;
justify-content: center;
align-items: center;
height: 64px;
border-radius: 8px;
@include light-theme {
background: $color-gray-02;
color: $color-gray-60;
}
@include dark-theme {
background: $color-gray-90;
color: $color-gray-25;
}
}
%blessed-sticker-pack-icon {
height: 14px;
width: 14px;
border-radius: 8px;
background-color: $color-white;
display: inline-block;
vertical-align: middle;
margin: {
left: 5px;
bottom: 2px;
}
position: relative;
&::before {
content: '';
display: block;
width: 16px;
height: 16px;
position: absolute;
top: -1px;
left: -1px;
@include light-theme {
@include color-svg(
'../images/icons/v2/check-circle-solid-24.svg',
$color-accent-blue
);
}
@include dark-theme {
@include color-svg(
'../images/icons/v2/check-circle-solid-24.svg',
$color-accent-blue
);
}
}
}
.module-sticker-manager__pack-row {
@include button-reset;
display: flex;
flex-direction: row;
padding: 16px;
padding-left: 8px;
@include keyboard-mode {
&:focus {
box-shadow: 0px 0px 0px 2px $color-ultramarine;
}
}
&__cover {
width: 48px;
height: 48px;
object-fit: contain;
}
&__cover-placeholder {
width: 48px;
height: 48px;
background: $color-gray-05;
}
&__meta {
flex-grow: 1;
display: flex;
flex-direction: column;
&:not(:first-child) {
padding: 0 12px;
}
&__title {
flex: 1;
}
&__author {
flex: 1;
@include light-theme() {
color: $color-gray-45;
}
@include dark-theme() {
color: $color-gray-25;
}
}
&__blessed-icon {
@extend %blessed-sticker-pack-icon;
}
}
&__controls {
flex-shrink: 1;
display: flex;
justify-content: center;
align-items: center;
&__button {
background: none;
border: 0;
&--menu {
&::after {
content: '';
display: block;
min-width: 24px;
min-height: 24px;
@include light-theme {
@include color-svg(
'../images/icons/v2/more-horiz-24.svg',
$color-gray-60
);
}
@include dark-theme {
@include color-svg(
'../images/icons/v2/more-horiz-24.svg',
$color-gray-25
);
}
}
}
}
}
}
.module-sticker-manager__install-button {
background: none;
border: 0;
color: $color-gray-90;
@include font-body-1-bold;
height: 24px;
background: $color-gray-05;
border-radius: 12px;
display: flex;
justify-content: center;
align-items: center;
padding: 0 12px;
@include dark-theme {
color: $color-gray-05;
background: $color-gray-75;
}
@include mouse-mode {
outline: none;
}
&--blue {
@include light-theme {
background: $color-ultramarine;
color: $color-white;
}
@include dark-theme {
background: $color-ultramarine-light;
color: $color-white;
}
}
}
.module-sticker-manager__preview-modal {
&__modal.module-Modal {
max-width: 440px;
width: 440px;
}
&__error {
color: $color-accent-red;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
width: 100%;
height: 100%;
padding: 0 80px 30px 80px;
@include font-body-1-bold;
}
&__sticker-grid {
width: 100%;
display: grid;
grid-gap: 8px;
grid-template-columns: repeat(4, 1fr);
overflow-y: auto;
&__cell {
width: 96px;
height: 96px;
display: flex;
justify-content: center;
align-items: center;
&__image {
width: 100%;
height: 100%;
object-fit: contain;
}
&--placeholder {
border-radius: 4px;
@include light-theme() {
background: $color-gray-05;
}
@include dark-theme() {
background: $color-gray-60;
}
}
}
}
&__footer {
display: flex;
justify-content: space-between;
width: 100%;
&--info {
display: flex;
flex-direction: column;
flex-grow: 1;
flex-shrink: 1;
justify-content: center;
overflow: hidden;
}
&--title {
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
@include font-body-1-bold;
@include light-theme {
color: $color-gray-90;
}
@include dark-theme {
color: $color-gray-05;
}
}
&--author {
margin: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
@include light-theme {
color: $color-gray-45;
}
@include dark-theme {
color: $color-gray-25;
}
}
&--blessed-icon {
@extend %blessed-sticker-pack-icon;
}
&--install {
flex-shrink: 0;
overflow: hidden;
}
}
}

View file

@ -111,14 +111,15 @@
@import './components/SignalConnectionsModal.scss';
@import './components/Slider.scss';
@import './components/StagedLinkPreview.scss';
@import './components/StickerManager.scss';
@import './components/Stories.scss';
@import './components/StoriesSettingsModal.scss';
@import './components/StoryCreator.scss';
@import './components/StoryDetailsModal.scss';
@import './components/StoryImage.scss';
@import './components/StoryLinkPreview.scss';
@import './components/StoryListItem.scss';
@import './components/StoryReplyQuote.scss';
@import './components/StoriesSettingsModal.scss';
@import './components/StoryViewer.scss';
@import './components/StoryViewsNRepliesModal.scss';
@import './components/SystemMessage.scss';

View file

@ -44,6 +44,7 @@ const knownPacks = [
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
blessedPacks: overrideProps.blessedPacks || [],
closeStickerPackPreview: action('closeStickerPackPreview'),
downloadStickerPack: action('downloadStickerPack'),
i18n,
installStickerPack: action('installStickerPack'),

View file

@ -1,35 +1,42 @@
// 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 classNames from 'classnames';
import { StickerManagerPackRow } from './StickerManagerPackRow';
import { StickerPreviewModal } from './StickerPreviewModal';
import type { LocalizerType } from '../../types/Util';
import type { StickerPackType } from '../../state/ducks/stickers';
import { Tabs } from '../Tabs';
export type OwnProps = {
readonly installedPacks: ReadonlyArray<StickerPackType>;
readonly receivedPacks: ReadonlyArray<StickerPackType>;
readonly blessedPacks: ReadonlyArray<StickerPackType>;
readonly knownPacks?: ReadonlyArray<StickerPackType>;
readonly closeStickerPackPreview: (packId: string) => unknown;
readonly downloadStickerPack: (packId: string, packKey: string) => unknown;
readonly installStickerPack: (packId: string, packKey: string) => unknown;
readonly uninstallStickerPack: (packId: string, packKey: string) => unknown;
readonly i18n: LocalizerType;
readonly installStickerPack: (packId: string, packKey: string) => unknown;
readonly installedPacks: ReadonlyArray<StickerPackType>;
readonly knownPacks?: ReadonlyArray<StickerPackType>;
readonly receivedPacks: ReadonlyArray<StickerPackType>;
readonly uninstallStickerPack: (packId: string, packKey: string) => unknown;
};
export type Props = OwnProps;
enum TabViews {
Available = 'Available',
Installed = 'Installed',
}
export const StickerManager = React.memo(function StickerManagerInner({
installedPacks,
receivedPacks,
knownPacks,
blessedPacks,
closeStickerPackPreview,
downloadStickerPack,
installStickerPack,
uninstallStickerPack,
i18n,
installStickerPack,
installedPacks,
knownPacks,
receivedPacks,
uninstallStickerPack,
}: Props) {
const focusRef = React.createRef<HTMLDivElement>();
const [packToPreview, setPackToPreview] =
@ -66,68 +73,94 @@ export const StickerManager = React.memo(function StickerManagerInner({
<>
{packToPreview ? (
<StickerPreviewModal
i18n={i18n}
pack={packToPreview}
closeStickerPackPreview={clearPackToPreview}
closeStickerPackPreview={closeStickerPackPreview}
downloadStickerPack={downloadStickerPack}
i18n={i18n}
installStickerPack={installStickerPack}
onClose={clearPackToPreview}
pack={packToPreview}
uninstallStickerPack={uninstallStickerPack}
/>
) : null}
<div className="module-sticker-manager" tabIndex={-1} ref={focusRef}>
{[
{
i18nKey: 'stickers--StickerManager--InstalledPacks',
i18nEmptyKey: 'stickers--StickerManager--InstalledPacks--Empty',
packs: installedPacks,
hideIfEmpty: false,
},
{
i18nKey: 'stickers--StickerManager--BlessedPacks',
i18nEmptyKey: 'stickers--StickerManager--BlessedPacks--Empty',
packs: blessedPacks,
hideIfEmpty: true,
},
{
i18nKey: 'stickers--StickerManager--ReceivedPacks',
i18nEmptyKey: 'stickers--StickerManager--ReceivedPacks--Empty',
packs: receivedPacks,
hideIfEmpty: false,
},
].map(section => {
if (section.hideIfEmpty && section.packs.length === 0) {
return null;
}
<Tabs
initialSelectedTab={TabViews.Available}
tabs={[
{
id: TabViews.Available,
label: i18n('stickers--StickerManager--Available'),
},
{
id: TabViews.Installed,
label: i18n('stickers--StickerManager--InstalledPacks'),
},
]}
>
{({ selectedTab }) => (
<>
{selectedTab === TabViews.Available && (
<>
<h2 className="module-sticker-manager__text module-sticker-manager__text--heading">
{i18n('stickers--StickerManager--BlessedPacks')}
</h2>
{blessedPacks.length > 0 ? (
blessedPacks.map(pack => (
<StickerManagerPackRow
key={pack.id}
pack={pack}
i18n={i18n}
onClickPreview={previewPack}
installStickerPack={installStickerPack}
uninstallStickerPack={uninstallStickerPack}
/>
))
) : (
<div className="module-sticker-manager__empty">
{i18n('stickers--StickerManager--BlessedPacks--Empty')}
</div>
)}
return (
<React.Fragment key={section.i18nKey}>
<h2
className={classNames(
'module-sticker-manager__text',
'module-sticker-manager__text--heading'
)}
>
{i18n(section.i18nKey)}
</h2>
{section.packs.length > 0 ? (
section.packs.map(pack => (
<StickerManagerPackRow
key={pack.id}
pack={pack}
i18n={i18n}
onClickPreview={previewPack}
installStickerPack={installStickerPack}
uninstallStickerPack={uninstallStickerPack}
/>
))
) : (
<div className="module-sticker-manager__empty">
{i18n(section.i18nEmptyKey)}
</div>
<h2 className="module-sticker-manager__text module-sticker-manager__text--heading">
{i18n('stickers--StickerManager--ReceivedPacks')}
</h2>
{receivedPacks.length > 0 ? (
receivedPacks.map(pack => (
<StickerManagerPackRow
key={pack.id}
pack={pack}
i18n={i18n}
onClickPreview={previewPack}
installStickerPack={installStickerPack}
uninstallStickerPack={uninstallStickerPack}
/>
))
) : (
<div className="module-sticker-manager__empty">
{i18n('stickers--StickerManager--ReceivedPacks--Empty')}
</div>
)}
</>
)}
</React.Fragment>
);
})}
{selectedTab === TabViews.Installed &&
(installedPacks.length > 0 ? (
installedPacks.map(pack => (
<StickerManagerPackRow
key={pack.id}
pack={pack}
i18n={i18n}
onClickPreview={previewPack}
installStickerPack={installStickerPack}
uninstallStickerPack={uninstallStickerPack}
/>
))
) : (
<div className="module-sticker-manager__empty">
{i18n('stickers--StickerManager--InstalledPacks--Empty')}
</div>
))}
</>
)}
</Tabs>
</div>
</>
);

View file

@ -1,11 +1,11 @@
// 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 { StickerPackInstallButton } from './StickerPackInstallButton';
import { ConfirmationDialog } from '../ConfirmationDialog';
import type { LocalizerType } from '../../types/Util';
import type { StickerPackType } from '../../state/ducks/stickers';
import { Button, ButtonVariant } from '../Button';
export type OwnProps = {
readonly i18n: LocalizerType;
@ -136,17 +136,21 @@ export const StickerManagerPackRow = React.memo(
</div>
<div className="module-sticker-manager__pack-row__controls">
{pack.status === 'installed' ? (
<StickerPackInstallButton
installed
i18n={i18n}
<Button
aria-label={i18n('stickers--StickerManager--Uninstall')}
variant={ButtonVariant.Secondary}
onClick={handleUninstall}
/>
>
{i18n('stickers--StickerManager--Uninstall')}
</Button>
) : (
<StickerPackInstallButton
installed={false}
i18n={i18n}
<Button
aria-label={i18n('stickers--StickerManager--Install')}
variant={ButtonVariant.Secondary}
onClick={handleInstall}
/>
>
{i18n('stickers--StickerManager--Install')}
</Button>
)}
</div>
</div>

View file

@ -65,6 +65,7 @@ export function Full(): JSX.Element {
return (
<StickerPreviewModal
closeStickerPackPreview={action('closeStickerPackPreview')}
onClose={action('onClose')}
installStickerPack={action('installStickerPack')}
uninstallStickerPack={action('uninstallStickerPack')}
downloadStickerPack={action('downloadStickerPack')}

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>
</>
);
});

View file

@ -142,7 +142,7 @@ describe('storage service', function needsName() {
.click({ noWaitAfter: true });
await window
.locator(
'.module-sticker-manager__preview-modal__container button >> "Install"'
'.module-sticker-manager__preview-modal__footer--install button >> "Install"'
)
.click();
@ -186,13 +186,15 @@ describe('storage service', function needsName() {
.click({ noWaitAfter: true });
await window
.locator(
'.module-sticker-manager__preview-modal__container button ' +
'.module-sticker-manager__preview-modal__footer--install button ' +
'>> "Uninstall"'
)
.click();
// Confirm
await window.locator('.module-Modal button >> "Uninstall"').click();
await window
.locator('.module-Button--destructive >> "Uninstall"')
.click();
debug('waiting for sync message');
const { syncMessage } = await phone.waitForSyncMessage(entry =>