Style donation amount picker

Co-authored-by: ayumi-signal <143036029+ayumi-signal@users.noreply.github.com>
This commit is contained in:
automated-signal 2025-07-21 14:31:09 -05:00 committed by GitHub
parent 52c57e3000
commit 3a617fb9ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 414 additions and 98 deletions

View file

@ -8866,6 +8866,18 @@
"messageformat": "Date paid",
"description": "Label for the payment date in donation receipt modal"
},
"icu:DonateFlow__make-a-one-time-donation": {
"messageformat": "Make a one time donation",
"description": "In the Donations settings section after beginning the donations process, then the user selects currency and amount which they want to donate. This string is the section header above currency amount options for a one time donation."
},
"icu:DonateFlow__desktop-one-time-only-notice": {
"messageformat": "Only one time donations are available on desktop. Monthly donations can be made on your mobile device.",
"description": "In the Donations settings section after beginning the donations process, then the user selects currency and amount which they want to donate. This footer notice indicates that only one time donations are available on desktop, and recurring donations must be processed on the mobile device."
},
"icu:DonateFlow__having-issues-contact-support": {
"messageformat": "Having issues? <contactSupportLink>Contact support</contactSupportLink>",
"description": "In the Donations settings section, this footer text appears during parts of the donation workflow such as when picking a donation currency and amount, or entering the credit card info."
},
"icu:DonationReceipt__title": {
"messageformat": "Donation receipt",
"description": "Title shown at the top of donation receipt documents"

View file

@ -40,6 +40,7 @@ $color-gray-65: #4a4a4a;
$color-gray-75: #3b3b3b;
$color-gray-78: #343434;
$color-gray-80: #2e2e2e;
$color-gray-85: #262626;
$color-gray-90: #1b1b1b;
$color-gray-95: #121212;
$color-black: #000000;

View file

@ -0,0 +1,149 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
@use '../mixins';
@use '../variables';
.DonationForm {
text-align: center;
}
.DonationForm__CurrencySelect {
width: 78px;
margin-inline: auto;
}
.DonationForm__CurrencySelect.module-select select {
height: auto;
min-width: auto;
padding-block: 5px;
}
.DonationForm .DonationForm__CurrencySelect.module-select select {
border-color: light-dark(
variables.$color-gray-25,
variables.$color-white-alpha-12
);
}
.DonationForm__HelpFooter {
@include mixins.font-body-small;
flex-grow: 1;
color: light-dark(
variables.$color-black-alpha-50,
variables.$color-white-alpha-50
);
align-content: flex-end;
text-align: center;
}
.DonationForm__HelpFooterDesktopOneTimeOnlyNotice {
margin-block: 10px 17px;
}
a.DonationFormHelpFooter__ContactSupportLink {
color: variables.$color-ultramarine;
text-decoration: none;
}
.DonationAmountPicker__AmountOptions {
display: flex;
flex-wrap: wrap;
max-width: 340px;
justify-content: center;
}
.DonationAmountPicker__PresetButton,
.DonationForm.PreferencesDonations
.DonationAmountPicker__CustomInput__container,
.DonationForm .DonationAmountPicker__CustomInput--selected__container,
.DonationForm .DonationAmountPicker__CustomInput--with-error__container {
margin-block: 5px;
margin-inline: 5px;
border-width: 0.5px;
border-style: solid;
border-radius: 6px;
font-weight: 400;
}
.DonationAmountPicker__PresetButton,
.DonationForm .DonationForm__CurrencySelect.module-select select,
.DonationForm.PreferencesDonations
.DonationAmountPicker__CustomInput__container,
.DonationForm.PreferencesDonations
.DonationAmountPicker__CustomInput--selected__container,
.DonationForm.PreferencesDonations
.DonationAmountPicker__CustomInput--with-error__container {
background-color: light-dark(
variables.$color-white,
variables.$color-gray-85
);
border-color: light-dark(
variables.$color-gray-25,
variables.$color-white-alpha-12
);
}
.DonationAmountPicker__PresetButton {
@include mixins.font-body-1;
width: 100px;
padding-inline: 12px;
padding-block: 14px;
margin-block: 5px;
margin-inline: 5px;
font-weight: 400;
}
.DonationAmountPicker__PresetButton--selected,
.DonationForm .DonationAmountPicker__CustomInput--selected__container,
.DonationForm
.DonationAmountPicker__CustomInput--with-error__container:focus-within,
.DonationForm .DonationAmountPicker__CustomInput__container:focus-within {
border-color: variables.$color-ultramarine;
outline: 2.5px solid variables.$color-ultramarine;
outline-offset: -2.5px;
}
.DonationForm .DonationAmountPicker__CustomInput__container,
.DonationForm .DonationAmountPicker__CustomInput--selected__container,
.DonationForm .DonationAmountPicker__CustomInput--with-error__container {
width: 320px;
padding-block: 0;
border-width: 0.5px;
}
.DonationForm
.DonationAmountPicker__CustomInput--with-error__container:not(:focus-within) {
border-color: variables.$color-deep-red;
outline: 2.5px solid variables.$color-deep-red;
outline-offset: -2.5px;
}
.DonationForm .DonationAmountPicker__CustomInput__input,
.DonationForm .DonationAmountPicker__CustomInput--selected__input,
.DonationForm .DonationAmountPicker__CustomInput--with-error__input {
@include mixins.font-body-1;
padding-inline: 12px;
padding-block: 14px;
text-align: center;
}
.DonationAmountPicker__CustomInput__input:not(:focus)::placeholder {
color: light-dark(
variables.$color-black-alpha-85,
variables.$color-white-alpha-85
);
opacity: 1;
}
.DonationAmountPicker__CustomInput__input:focus::placeholder,
.DonationAmountPicker__CustomInput--selected__input:focus::placeholder,
.DonationAmountPicker__CustomInput--with-error__input:focus::placeholder {
color: transparent;
}
.DonationAmountPicker__PrimaryButtonContainer {
margin-block-start: 11px;
margin-inline-end: 10px;
text-align: end;
}

View file

@ -8,6 +8,7 @@
display: flex;
flex-direction: column;
align-items: center;
min-height: 590px;
padding-block: 0;
padding-inline: 0;
margin-inline-start: 24px;
@ -15,14 +16,14 @@
&__title {
@include mixins.font-title-medium;
margin-bottom: 16px;
margin-bottom: 8px;
}
&__description {
@include mixins.font-body-2;
text-align: center;
max-width: 320px;
margin-bottom: 24px;
margin-block-end: 12px;
color: light-dark(
variables.$color-black-alpha-50,
variables.$color-white-alpha-50
@ -42,7 +43,7 @@
}
&__donate-button {
margin-bottom: 24px;
margin-block-end: 32px;
}
&__separator {
@ -60,13 +61,20 @@
&__section-header {
@include mixins.font-body-2-bold;
width: 100%;
margin-top: 12px;
margin-bottom: 12px;
margin-block: 12px;
padding-inline: 8px;
color: light-dark(
variables.$color-black-alpha-85,
variables.$color-white-alpha-85
);
&--my-support {
margin-block-start: 6px;
}
&--donate-flow {
margin-block-start: 22px;
}
}
&__list {
@ -161,9 +169,9 @@
&__mobile-info {
@include mixins.font-subtitle;
margin-top: 18px;
align-self: flex-start;
padding-inline: 8px;
margin-block-start: 10px;
align-self: flex-start;
color: light-dark(
variables.$color-black-alpha-50,
variables.$color-white-alpha-50
@ -171,6 +179,10 @@
}
}
.PreferencesDonations__avatar {
margin-block-end: 12px;
}
// Receipts page specific styles
.PreferencesDonations--receiptList {
&__info {
@ -386,3 +398,11 @@
}
}
}
.PreferencesDonations__PrimaryButton {
@include mixins.font-body-2;
padding-block: 5px;
padding-inline: 12px;
font-weight: 400;
border-radius: 6px;
}

View file

@ -97,6 +97,7 @@
@use 'components/DisappearingTimeDialog.scss';
@use 'components/DisappearingTimerSelect.scss';
@use 'components/DonationErrorModal.scss';
@use 'components/DonationForm.scss';
@use 'components/DonationProgressModal.scss';
@use 'components/DonationStillProcessingModal.scss';
@use 'components/DonationVerificationModal.scss';

View file

@ -46,6 +46,7 @@ export enum AvatarSize {
FORTY = 40,
FORTY_EIGHT = 48,
FIFTY_TWO = 52,
SEVENTY_TWO = 72,
SIXTY_FOUR = 64,
EIGHTY = 80,
NINETY_SIX = 96,

View file

@ -34,6 +34,7 @@ export type PropsType = {
moduleClassName?: string;
onChange: (value: string) => unknown;
onBlur?: () => unknown;
onFocus?: () => unknown;
onEnter?: () => unknown;
placeholder: string;
value?: string;
@ -80,6 +81,7 @@ export const Input = forwardRef<
moduleClassName,
onChange,
onBlur,
onFocus,
onEnter,
placeholder,
value = '',
@ -220,6 +222,7 @@ export const Input = forwardRef<
spellCheck: !disableSpellcheck,
onChange: handleChange,
onBlur,
onFocus,
onKeyDown: handleKeyDown,
onPaste: handlePaste,
placeholder,

View file

@ -195,6 +195,7 @@ function RenderProfileEditor(): JSX.Element {
function RenderDonationsPane(props: {
me: typeof me;
donationReceipts: ReadonlyArray<DonationReceipt>;
page: SettingsPage;
saveAttachmentToDisk: (options: {
data: Uint8Array;
name: string;
@ -212,12 +213,12 @@ function RenderDonationsPane(props: {
i18n={i18n}
contentsRef={contentsRef}
clearWorkflow={action('clearWorkflow')}
isStaging={false}
page={SettingsPage.Donations}
isStaging
page={props.page}
setPage={action('setPage')}
submitDonation={action('submitDonation')}
workflow={undefined}
userAvatarData={[]}
badge={undefined}
color={props.me.color}
firstName={props.me.firstName}
profileAvatarUrl={props.me.profileAvatarUrl}
@ -227,6 +228,7 @@ function RenderDonationsPane(props: {
saveAttachmentToDisk={props.saveAttachmentToDisk}
generateDonationReceiptBlob={props.generateDonationReceiptBlob}
showToast={props.showToast}
theme={ThemeType.light}
/>
);
}
@ -353,6 +355,7 @@ export default {
RenderDonationsPane({
me,
donationReceipts: [],
page: SettingsPage.Donations,
saveAttachmentToDisk: async () => {
action('saveAttachmentToDisk')();
return { fullPath: '/mock/path/to/file.png', name: 'file.png' };
@ -516,6 +519,26 @@ Donations.args = {
donationsFeatureEnabled: true,
page: SettingsPage.Donations,
};
export const DonationsDonateFlow = Template.bind({});
DonationsDonateFlow.args = {
donationsFeatureEnabled: true,
page: SettingsPage.DonationsDonateFlow,
renderDonationsPane: () =>
RenderDonationsPane({
me,
donationReceipts: [],
page: SettingsPage.DonationsDonateFlow,
saveAttachmentToDisk: async () => {
action('saveAttachmentToDisk')();
return { fullPath: '/mock/path/to/file.png', name: 'file.png' };
},
generateDonationReceiptBlob: async () => {
action('generateDonationReceiptBlob')();
return new Blob();
},
showToast: action('showToast'),
}),
};
export const Internal = Template.bind({});
Internal.args = {
page: SettingsPage.Internal,

View file

@ -10,6 +10,7 @@ import React, {
useState,
} from 'react';
import classNames from 'classnames';
import type { LocalizerType } from '../types/Util';
import { useConfirmDiscard } from '../hooks/useConfirmDiscard';
import { Button, ButtonVariant } from './Button';
@ -55,12 +56,16 @@ import {
DonateInputCardCvc,
getCardCvcErrorMessage,
} from './preferences/donations/DonateInputCardCvc';
import { I18n } from './I18n';
const SUPPORT_URL = 'https://support.signal.org/hc/requests/new?desktop';
export type PropsDataType = {
i18n: LocalizerType;
donationAmountsConfig: OneTimeDonationHumanAmounts | undefined;
validCurrencies: ReadonlyArray<string>;
workflow: DonationWorkflow | undefined;
renderDonationHero: () => JSX.Element;
};
type PropsHousekeepingType = {
@ -82,6 +87,7 @@ export function PreferencesDonateFlow({
validCurrencies,
workflow,
clearWorkflow,
renderDonationHero,
submitDonation,
onBack,
}: PropsType): JSX.Element {
@ -191,8 +197,8 @@ export function PreferencesDonateFlow({
// TODO: DESKTOP-8950
};
confirmDiscardIf(true, onDiscard);
}, [confirmDiscardIf]);
confirmDiscardIf(step === 'paymentDetails', onDiscard);
}, [confirmDiscardIf, step]);
tryClose.current = onTryClose;
let innerContent: JSX.Element;
@ -200,14 +206,18 @@ export function PreferencesDonateFlow({
if (step === 'amount') {
innerContent = (
<AmountPicker
i18n={i18n}
initialAmount={amount}
initialCurrency={currency}
donationAmountsConfig={donationAmountsConfig}
validCurrencies={validCurrencies}
onSubmit={handleAmountPickerResult}
/>
<>
{renderDonationHero()}
<AmountPicker
i18n={i18n}
initialAmount={amount}
initialCurrency={currency}
donationAmountsConfig={donationAmountsConfig}
validCurrencies={validCurrencies}
onSubmit={handleAmountPickerResult}
/>
<HelpFooter i18n={i18n} showOneTimeOnlyNotice />
</>
);
// Dismiss DonateFlow and return to Donations home
handleBack = () => onBack();
@ -287,10 +297,10 @@ export function PreferencesDonateFlow({
/>
);
const content = (
<>
<div className="PreferencesDonations DonationForm">
{confirmDiscardModal}
{innerContent}
</>
</div>
);
return (
@ -321,16 +331,16 @@ function AmountPicker({
donationAmountsConfig,
i18n,
initialAmount,
initialCurrency,
initialCurrency = 'usd',
validCurrencies,
onSubmit,
}: AmountPickerProps): JSX.Element {
const [currency, setCurrency] = useState(initialCurrency ?? 'usd');
const [currency, setCurrency] = useState(initialCurrency);
const [presetAmount, setPresetAmount] = useState<
HumanDonationAmount | undefined
>(initialAmount);
const [customAmount, setCustomAmount] = useState<string>();
>();
const [customAmount, setCustomAmount] = useState<string | undefined>();
// Reset amount selections when API donation config or selected currency changes
// Memo here so preset options instantly load when component mounts.
@ -345,9 +355,17 @@ function AmountPicker({
}, [donationAmountsConfig, currency]);
useEffect(() => {
setCustomAmount(undefined);
setPresetAmount(undefined);
}, [donationAmountsConfig, currency]);
if (
initialAmount &&
presetAmountOptions.find(option => option === initialAmount)
) {
setPresetAmount(initialAmount);
setCustomAmount(undefined);
} else {
setPresetAmount(undefined);
setCustomAmount(initialAmount?.toString());
}
}, [initialAmount, presetAmountOptions]);
const minimumAmount = useMemo<HumanDonationAmount>(() => {
if (!donationAmountsConfig || !donationAmountsConfig[currency]) {
@ -422,51 +440,103 @@ function AmountPicker({
onSubmit({ amount, currency });
}, [amount, currency, isContinueEnabled, onSubmit]);
let customInputClassName;
if (error) {
customInputClassName = 'DonationAmountPicker__CustomInput--with-error';
} else if (parsedCustomAmount) {
customInputClassName = 'DonationAmountPicker__CustomInput--selected';
} else {
customInputClassName = 'DonationAmountPicker__CustomInput';
}
return (
<div>
<div className="DonationAmountPicker">
<Select
moduleClassName="DonationForm__CurrencySelect"
id="currency"
options={currencyOptionsForSelect}
onChange={handleCurrencyChanged}
value={currency}
/>
<div>
<div className="PreferencesDonations__section-header PreferencesDonations__section-header--donate-flow">
{i18n('icu:DonateFlow__make-a-one-time-donation')}
</div>
<div className="DonationAmountPicker__AmountOptions">
{presetAmountOptions.map(value => (
<Button
<button
className={classNames({
DonationAmountPicker__PresetButton: true,
'DonationAmountPicker__PresetButton--selected':
presetAmount === value,
})}
key={value}
onClick={() => {
setCustomAmount(undefined);
setPresetAmount(value);
}}
variant={
presetAmount === value
? ButtonVariant.SecondaryAffirmative
: ButtonVariant.Secondary
}
type="button"
>
{toHumanCurrencyString({ amount: value, currency })}
</Button>
</button>
))}
</div>
<label htmlFor="customAmount">Custom Amount</label>
<div>
<Input
moduleClassName={customInputClassName}
id="customAmount"
i18n={i18n}
onChange={handleCustomAmountChanged}
onFocus={() => setPresetAmount(undefined)}
placeholder="Enter Custom Amount"
value={customAmount}
/>
<span>{currency.toUpperCase()}</span>
</div>
{error && <div>Error: {error}</div>}
<Button
disabled={!isContinueEnabled}
onClick={handleContinueClicked}
variant={ButtonVariant.Primary}
>
Continue
</Button>
<div className="DonationAmountPicker__PrimaryButtonContainer">
<Button
className="PreferencesDonations__PrimaryButton"
disabled={!isContinueEnabled}
onClick={handleContinueClicked}
variant={ButtonVariant.Primary}
>
Continue
</Button>
</div>
</div>
);
}
type HelpFooterProps = {
i18n: LocalizerType;
showOneTimeOnlyNotice?: boolean;
};
function HelpFooter({
i18n,
showOneTimeOnlyNotice,
}: HelpFooterProps): JSX.Element {
const contactSupportLink = (parts: Array<string | JSX.Element>) => (
<a
className="DonationFormHelpFooter__ContactSupportLink"
href={SUPPORT_URL}
rel="noreferrer"
target="_blank"
>
{parts}
</a>
);
return (
<div className="DonationForm__HelpFooter">
{showOneTimeOnlyNotice && (
<div className="DonationForm__HelpFooterDesktopOneTimeOnlyNotice">
{i18n('icu:DonateFlow__desktop-one-time-only-notice')}
</div>
)}
<I18n
id="icu:DonateFlow__having-issues-contact-support"
i18n={i18n}
components={{
contactSupportLink,
}}
/>
</div>
);
}

View file

@ -8,7 +8,7 @@ import type { MutableRefObject, ReactNode } from 'react';
import { ListBox, ListBoxItem } from 'react-aria-components';
import { getDateTimeFormatter } from '../util/formatTimestamp';
import type { LocalizerType } from '../types/Util';
import type { LocalizerType, ThemeType } from '../types/Util';
import { PreferencesContent } from './Preferences';
import { SettingsPage } from '../types/Nav';
import { PreferencesDonateFlow } from './PreferencesDonateFlow';
@ -18,8 +18,6 @@ import type {
OneTimeDonationHumanAmounts,
} from '../types/Donations';
import type { AvatarColorType } from '../types/Colors';
import type { AvatarDataType } from '../types/Avatar';
import { AvatarPreview } from './AvatarPreview';
import { Button, ButtonSize, ButtonVariant } from './Button';
import { Modal } from './Modal';
import { Spinner } from './Spinner';
@ -32,6 +30,8 @@ import { openLinkInWebBrowser } from '../util/openLinkInWebBrowser';
import { DonationPrivacyInformationModal } from './DonationPrivacyInformationModal';
import type { SubmitDonationType } from '../state/ducks/donations';
import { getHumanDonationAmount } from '../util/currency';
import { Avatar, AvatarSize } from './Avatar';
import type { BadgeType } from '../badges/types';
const log = createLogger('PreferencesDonations');
@ -44,13 +44,14 @@ export type PropsDataType = {
isStaging: boolean;
page: SettingsPage;
workflow: DonationWorkflow | undefined;
userAvatarData: ReadonlyArray<AvatarDataType>;
color?: AvatarColorType;
firstName?: string;
badge: BadgeType | undefined;
color: AvatarColorType | undefined;
firstName: string | undefined;
profileAvatarUrl?: string;
donationAmountsConfig: OneTimeDonationHumanAmounts | undefined;
validCurrencies: ReadonlyArray<string>;
donationReceipts: ReadonlyArray<DonationReceipt>;
theme: ThemeType;
saveAttachmentToDisk: (options: {
data: Uint8Array;
name: string;
@ -76,8 +77,9 @@ type DonationPage =
| SettingsPage.DonationsDonateFlow
| SettingsPage.DonationsReceiptList;
type PreferencesHomeProps = PropsType & {
type PreferencesHomeProps = Omit<PropsType, 'badge' | 'theme'> & {
navigateToPage: (newPage: SettingsPage) => void;
renderDonationHero: () => JSX.Element;
};
function isDonationPage(page: SettingsPage): page is DonationPage {
@ -88,20 +90,19 @@ function isDonationPage(page: SettingsPage): page is DonationPage {
);
}
function DonationsHome({
i18n,
userAvatarData,
type DonationHeroProps = Pick<
PropsDataType,
'badge' | 'color' | 'firstName' | 'i18n' | 'profileAvatarUrl' | 'theme'
>;
function DonationHero({
badge,
color,
firstName,
i18n,
profileAvatarUrl,
navigateToPage,
setPage,
isStaging,
donationReceipts,
}: PreferencesHomeProps): JSX.Element {
const avatarData = userAvatarData[0];
const avatarBuffer = avatarData?.buffer;
const hasReceipts = donationReceipts.length > 0;
theme,
}: DonationHeroProps): JSX.Element {
const [showPrivacyModal, setShowPrivacyModal] = useState(false);
const ReadMoreButtonWithModal = useCallback(
@ -122,18 +123,25 @@ function DonationsHome({
);
return (
<div className="PreferencesDonations">
<div className="PreferencesDonations__avatar">
<AvatarPreview
avatarColor={color}
avatarUrl={profileAvatarUrl}
avatarValue={avatarBuffer}
conversationTitle={firstName || i18n('icu:unknownContact')}
<>
{showPrivacyModal && (
<DonationPrivacyInformationModal
i18n={i18n}
style={{
height: 80,
width: 80,
}}
onClose={() => setShowPrivacyModal(false)}
/>
)}
<div className="PreferencesDonations__avatar">
<Avatar
avatarUrl={profileAvatarUrl}
badge={badge}
color={color}
conversationType="direct"
title={firstName ?? ''}
i18n={i18n}
sharedGroupNames={[]}
size={AvatarSize.SEVENTY_TWO}
theme={theme}
/>
</div>
<div className="PreferencesDonations__title">
@ -148,9 +156,26 @@ function DonationsHome({
id="icu:PreferencesDonations__description"
/>
</div>
</>
);
}
function DonationsHome({
i18n,
renderDonationHero,
navigateToPage,
setPage,
isStaging,
donationReceipts,
}: PreferencesHomeProps): JSX.Element {
const hasReceipts = donationReceipts.length > 0;
return (
<div className="PreferencesDonations">
{renderDonationHero()}
{isStaging && (
<Button
className="PreferencesDonations__donate-button"
className="PreferencesDonations__PrimaryButton PreferencesDonations__donate-button"
variant={ButtonVariant.Primary}
size={ButtonSize.Medium}
onClick={() => {
@ -164,7 +189,7 @@ function DonationsHome({
<hr className="PreferencesDonations__separator" />
{hasReceipts && (
<div className="PreferencesDonations__section-header">
<div className="PreferencesDonations__section-header PreferencesDonations__section-header--my-support">
{i18n('icu:PreferencesDonations__my-support')}
</div>
)}
@ -203,13 +228,6 @@ function DonationsHome({
<div className="PreferencesDonations__mobile-info">
{i18n('icu:PreferencesDonations__mobile-info')}
</div>
{showPrivacyModal && (
<DonationPrivacyInformationModal
i18n={i18n}
onClose={() => setShowPrivacyModal(false)}
/>
)}
</div>
);
}
@ -432,13 +450,14 @@ export function PreferencesDonations({
clearWorkflow,
setPage,
submitDonation,
userAvatarData,
badge,
color,
firstName,
profileAvatarUrl,
donationAmountsConfig,
validCurrencies,
donationReceipts,
theme,
saveAttachmentToDisk,
generateDonationReceiptBlob,
showToast,
@ -450,6 +469,20 @@ export function PreferencesDonations({
[setPage]
);
const renderDonationHero = useCallback(
() => (
<DonationHero
badge={badge}
color={color}
firstName={firstName}
i18n={i18n}
profileAvatarUrl={profileAvatarUrl}
theme={theme}
/>
),
[badge, color, firstName, i18n, profileAvatarUrl, theme]
);
if (!isDonationPage(page)) {
return null;
}
@ -465,6 +498,7 @@ export function PreferencesDonations({
validCurrencies={validCurrencies}
workflow={workflow}
clearWorkflow={clearWorkflow}
renderDonationHero={renderDonationHero}
submitDonation={submitDonation}
onBack={() => setPage(SettingsPage.Donations)}
/>
@ -475,7 +509,6 @@ export function PreferencesDonations({
<DonationsHome
contentsRef={contentsRef}
i18n={i18n}
userAvatarData={userAvatarData}
color={color}
firstName={firstName}
profileAvatarUrl={profileAvatarUrl}
@ -490,6 +523,7 @@ export function PreferencesDonations({
page={page}
workflow={workflow}
clearWorkflow={clearWorkflow}
renderDonationHero={renderDonationHero}
setPage={setPage}
submitDonation={submitDonation}
/>

View file

@ -6,7 +6,7 @@ import { useSelector } from 'react-redux';
import type { MutableRefObject } from 'react';
import { getIntl } from '../selectors/user';
import { getIntl, getTheme } from '../selectors/user';
import { getMe } from '../selectors/conversations';
import { PreferencesDonations } from '../../components/PreferencesDonations';
import type { SettingsPage } from '../../types/Nav';
@ -18,6 +18,7 @@ import { useToastActions } from '../ducks/toast';
import { getDonationHumanAmounts } from '../../util/subscriptionConfiguration';
import { drop } from '../../util/drop';
import type { OneTimeDonationHumanAmounts } from '../../types/Donations';
import { getPreferredBadgeSelector } from '../selectors/badges';
export const SmartPreferencesDonations = memo(
function SmartPreferencesDonations({
@ -35,24 +36,24 @@ export const SmartPreferencesDonations = memo(
const [donationAmountsConfig, setDonationAmountsConfig] =
useState<OneTimeDonationHumanAmounts>();
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
const isStaging = isStagingServer();
const i18n = useSelector(getIntl);
const theme = useSelector(getTheme);
const workflow = useSelector(
(state: StateType) => state.donations.currentWorkflow
);
const { clearWorkflow, submitDonation } = useDonationsActions();
const {
avatars: userAvatarData = [],
color,
firstName,
profileAvatarUrl,
} = useSelector(getMe);
const { badges, color, firstName, profileAvatarUrl } = useSelector(getMe);
const badge = getPreferredBadge(badges);
const { showToast } = useToastActions();
const donationReceipts = useSelector(
(state: StateType) => state.donations.receipts
);
const { saveAttachmentToDisk } = window.Signal.Migrations;
// Eagerly load donation config from API when entering Donations Home so the
@ -70,7 +71,7 @@ export const SmartPreferencesDonations = memo(
return (
<PreferencesDonations
i18n={i18n}
userAvatarData={userAvatarData}
badge={badge}
color={color}
firstName={firstName}
profileAvatarUrl={profileAvatarUrl}
@ -87,6 +88,7 @@ export const SmartPreferencesDonations = memo(
clearWorkflow={clearWorkflow}
submitDonation={submitDonation}
setPage={setPage}
theme={theme}
/>
);
}