Skip to content

Commit

Permalink
Add "become a sustainer" button and view to badge dialog
Browse files Browse the repository at this point in the history
Co-authored-by: Evan Hahn <69474926+EvanHahn-Signal@users.noreply.github.com>
  • Loading branch information
automated-signal and EvanHahn-Signal committed Nov 16, 2021
1 parent 749bd6b commit 8f27d29
Show file tree
Hide file tree
Showing 11 changed files with 197 additions and 32 deletions.
24 changes: 24 additions & 0 deletions _locales/en/messages.json
Expand Up @@ -5272,6 +5272,30 @@
"message": "next",
"description": "Generic next label"
},
"BadgeDialog__become-a-sustainer-button": {
"message": "Become a Sustainer",
"description": "In the badge dialog. This button is shown under sustainer badges, taking users to some instructions"
},
"BadgeSustainerInstructions__header": {
"message": "Become a Sustainer",
"description": "In the instructions for becoming a sustainer. The heading."
},
"BadgeSustainerInstructions__subheader": {
"message": "Signal is powered by people like you. Contribute and receive a badge.",
"description": "In the instructions for becoming a sustainer. The subheading."
},
"BadgeSustainerInstructions__instructions__1": {
"message": "Open Signal on your phone",
"description": "In the instructions for becoming a sustainer. First instruction."
},
"BadgeSustainerInstructions__instructions__2": {
"message": "Tap on your profile photo in the top left to open Settings",
"description": "In the instructions for becoming a sustainer. Second instruction."
},
"BadgeSustainerInstructions__instructions__3": {
"message": "Tap on \"Become a Sustainer\" and subscribe",
"description": "In the instructions for becoming a sustainer. Third instruction."
},
"CompositionArea--expand": {
"message": "Expand",
"description": "Aria label for expanding composition area"
Expand Down
1 change: 1 addition & 0 deletions images/mobile-settings-dark.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions images/mobile-settings-light.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 13 additions & 1 deletion stylesheets/components/BadgeDialog.scss
Expand Up @@ -14,7 +14,7 @@
max-width: 420px;
}

&__body {
&__contents {
display: flex;
align-items: center;
}
Expand Down Expand Up @@ -110,5 +110,17 @@
&__description {
@include font-body-1;
@include fixed-height(5.5em);
margin-bottom: 12px;
}

&__instructions-button {
width: 100%;
&--hidden {
visibility: hidden;
}
}

.BadgeCarouselIndex {
margin-top: 24px;
}
}
52 changes: 52 additions & 0 deletions stylesheets/components/BadgeSustainerInstructionsDialog.scss
@@ -0,0 +1,52 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

.BadgeSustainerInstructionsDialog {
user-select: none;

// We use this selector for specificity.
&.module-Modal {
max-width: 420px;
}

&__header {
@include font-title-2;
text-align: center;
}

&__subheader {
@include font-body-1;
font-weight: normal;
text-align: center;
}

&__instructions {
@include font-body-2;
padding: 0;
list-style-position: inside;

&::before {
background-size: contain;
content: '';
display: block;
height: 160px;
margin: 24px auto;
width: 146px;

@include light-theme {
background-image: url('../images/mobile-settings-light.svg');
}
@include dark-theme {
background-image: url('../images/mobile-settings-dark.svg');
}
}

> li {
margin-top: 1em;

&:first-child {
margin-top: 0;
}
}
}
}
5 changes: 5 additions & 0 deletions stylesheets/components/Button.scss
Expand Up @@ -35,6 +35,11 @@
cursor: not-allowed;
}

&--large {
@include font-title-2;
font-weight: bold;
}

&--medium {
@include font-body-1-bold;
}
Expand Down
1 change: 1 addition & 0 deletions stylesheets/manifest.scss
Expand Up @@ -34,6 +34,7 @@
@import './components/AvatarTextEditor.scss';
@import './components/BadgeCarouselIndex.scss';
@import './components/BadgeDialog.scss';
@import './components/BadgeSustainerInstructionsDialog.scss';
@import './components/BetterAvatarBubble.scss';
@import './components/Button.scss';
@import './components/CallingLobby.scss';
Expand Down
94 changes: 64 additions & 30 deletions ts/components/BadgeDialog.tsx
Expand Up @@ -2,14 +2,18 @@
// SPDX-License-Identifier: AGPL-3.0-only

import React, { useEffect, useState } from 'react';
import classNames from 'classnames';

import { strictAssert } from '../util/assert';
import type { LocalizerType } from '../types/Util';
import type { BadgeType } from '../badges/types';
import { BadgeCategory } from '../badges/BadgeCategory';
import { Modal } from './Modal';
import { Button, ButtonSize } from './Button';
import { BadgeDescription } from './BadgeDescription';
import { BadgeImage } from './BadgeImage';
import { BadgeCarouselIndex } from './BadgeCarouselIndex';
import { BadgeSustainerInstructionsDialog } from './BadgeSustainerInstructionsDialog';

type PropsType = Readonly<{
badges: ReadonlyArray<BadgeType>;
Expand All @@ -20,25 +24,42 @@ type PropsType = Readonly<{
}>;

export function BadgeDialog(props: PropsType): null | JSX.Element {
const { badges, onClose } = props;
const { badges, i18n, onClose } = props;

const [isShowingInstructions, setIsShowingInstructions] = useState(false);

const hasBadges = badges.length > 0;
useEffect(() => {
if (!hasBadges) {
if (!hasBadges && !isShowingInstructions) {
onClose();
}
}, [hasBadges, onClose]);
}, [hasBadges, isShowingInstructions, onClose]);

if (isShowingInstructions) {
return (
<BadgeSustainerInstructionsDialog
i18n={i18n}
onClose={() => setIsShowingInstructions(false)}
/>
);
}

return hasBadges ? <BadgeDialogWithBadges {...props} /> : null;
return hasBadges ? (
<BadgeDialogWithBadges
{...props}
onShowInstructions={() => setIsShowingInstructions(true)}
/>
) : null;
}

function BadgeDialogWithBadges({
badges,
firstName,
i18n,
onClose,
onShowInstructions,
title,
}: PropsType): JSX.Element {
}: PropsType & { onShowInstructions: () => unknown }): JSX.Element {
const firstBadge = badges[0];
strictAssert(
firstBadge,
Expand Down Expand Up @@ -75,35 +96,48 @@ function BadgeDialogWithBadges({
i18n={i18n}
onClose={onClose}
>
<button
aria-label={i18n('previous')}
className="BadgeDialog__nav BadgeDialog__nav--previous"
disabled={currentBadgeIndex === 0}
onClick={() => navigate(-1)}
type="button"
/>
<div className="BadgeDialog__main">
<BadgeImage badge={currentBadge} size={160} />
<div className="BadgeDialog__name">{currentBadge.name}</div>
<div className="BadgeDialog__description">
<BadgeDescription
firstName={firstName}
template={currentBadge.descriptionTemplate}
title={title}
<div className="BadgeDialog__contents">
<button
aria-label={i18n('previous')}
className="BadgeDialog__nav BadgeDialog__nav--previous"
disabled={currentBadgeIndex === 0}
onClick={() => navigate(-1)}
type="button"
/>
<div className="BadgeDialog__main">
<BadgeImage badge={currentBadge} size={160} />
<div className="BadgeDialog__name">{currentBadge.name}</div>
<div className="BadgeDialog__description">
<BadgeDescription
firstName={firstName}
template={currentBadge.descriptionTemplate}
title={title}
/>
</div>
<Button
className={classNames(
'BadgeDialog__instructions-button',
currentBadge.category !== BadgeCategory.Donor &&
'BadgeDialog__instructions-button--hidden'
)}
onClick={onShowInstructions}
size={ButtonSize.Large}
>
{i18n('BadgeDialog__become-a-sustainer-button')}
</Button>
<BadgeCarouselIndex
currentIndex={currentBadgeIndex}
totalCount={badges.length}
/>
</div>
<BadgeCarouselIndex
currentIndex={currentBadgeIndex}
totalCount={badges.length}
<button
aria-label={i18n('next')}
className="BadgeDialog__nav BadgeDialog__nav--next"
disabled={currentBadgeIndex === badges.length - 1}
onClick={() => navigate(1)}
type="button"
/>
</div>
<button
aria-label={i18n('next')}
className="BadgeDialog__nav BadgeDialog__nav--next"
disabled={currentBadgeIndex === badges.length - 1}
onClick={() => navigate(1)}
type="button"
/>
</Modal>
);
}
33 changes: 33 additions & 0 deletions ts/components/BadgeSustainerInstructionsDialog.tsx
@@ -0,0 +1,33 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

import type { ReactElement } from 'react';
import React from 'react';
import type { LocalizerType } from '../types/Util';
import { Modal } from './Modal';

export function BadgeSustainerInstructionsDialog({
i18n,
onClose,
}: Readonly<{ i18n: LocalizerType; onClose: () => unknown }>): ReactElement {
return (
<Modal
hasXButton
moduleClassName="BadgeSustainerInstructionsDialog"
i18n={i18n}
onClose={onClose}
>
<h1 className="BadgeSustainerInstructionsDialog__header">
{i18n('BadgeSustainerInstructions__header')}
</h1>
<h2 className="BadgeSustainerInstructionsDialog__subheader">
{i18n('BadgeSustainerInstructions__subheader')}
</h2>
<ol className="BadgeSustainerInstructionsDialog__instructions">
<li>{i18n('BadgeSustainerInstructions__instructions__1')}</li>
<li>{i18n('BadgeSustainerInstructions__instructions__2')}</li>
<li>{i18n('BadgeSustainerInstructions__instructions__3')}</li>
</ol>
</Modal>
);
}
2 changes: 1 addition & 1 deletion ts/components/Button.stories.tsx
Expand Up @@ -13,7 +13,7 @@ story.add('Kitchen sink', () => (
<>
{Object.values(ButtonVariant).map(variant => (
<React.Fragment key={variant}>
{[ButtonSize.Medium, ButtonSize.Small].map(size => (
{[ButtonSize.Large, ButtonSize.Medium, ButtonSize.Small].map(size => (
<React.Fragment key={size}>
<p>
<Button onClick={action('onClick')} size={size} variant={variant}>
Expand Down
2 changes: 2 additions & 0 deletions ts/components/Button.tsx
Expand Up @@ -8,6 +8,7 @@ import classNames from 'classnames';
import { assert } from '../util/assert';

export enum ButtonSize {
Large,
Medium,
Small,
}
Expand Down Expand Up @@ -65,6 +66,7 @@ type PropsType = {
);

const SIZE_CLASS_NAMES = new Map<ButtonSize, string>([
[ButtonSize.Large, 'module-Button--large'],
[ButtonSize.Medium, 'module-Button--medium'],
[ButtonSize.Small, 'module-Button--small'],
]);
Expand Down

0 comments on commit 8f27d29

Please sign in to comment.