Skip to content

Commit

Permalink
Stars: Format currency with icon (#4678)
Browse files Browse the repository at this point in the history
  • Loading branch information
zubiden authored and Ajaxy committed Jun 18, 2024
1 parent 41cc393 commit 6f79159
Show file tree
Hide file tree
Showing 22 changed files with 132 additions and 46 deletions.
1 change: 0 additions & 1 deletion src/api/gramjs/apiBuilders/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,6 @@ function buildReplyButtons(message: UniversalMessage, shouldSkipBuyButton?: bool
if (media instanceof GramJs.MessageMediaInvoice && media.receiptMsgId) {
return {
type: 'receipt',
text: 'PaymentReceipt',
receiptMessageId: media.receiptMsgId,
};
}
Expand Down
1 change: 0 additions & 1 deletion src/api/types/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -726,7 +726,6 @@ interface ApiKeyboardButtonSimple {

interface ApiKeyboardButtonReceipt {
type: 'receipt';
text: string;
receiptMessageId: number;
}

Expand Down
8 changes: 4 additions & 4 deletions src/components/common/helpers/renderActionMessageText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
isExpiredMessage,
} from '../../../global/helpers';
import { getMessageSummaryText } from '../../../global/helpers/messageSummary';
import { formatCurrency } from '../../../util/formatCurrency';
import { formatCurrencyAsString } from '../../../util/formatCurrency';
import trimText from '../../../util/trimText';
import renderText from './renderText';

Expand Down Expand Up @@ -85,7 +85,7 @@ export function renderActionMessageText(
processed = processPlaceholder(
unprocessed,
'%payment_amount%',
formatCurrency(amount!, currency!, lang.code),
formatCurrencyAsString(amount!, currency!, lang.code),
);
unprocessed = processed.pop() as string;
content.push(...processed);
Expand Down Expand Up @@ -134,11 +134,11 @@ export function renderActionMessageText(
}

if (unprocessed.includes('%gift_payment_amount%')) {
const price = formatCurrency(amount!, currency!, lang.code);
const price = formatCurrencyAsString(amount!, currency!, lang.code);
let priceText = price;

if (giftCryptoInfo) {
const cryptoPrice = formatCurrency(giftCryptoInfo.amount, giftCryptoInfo.currency, lang.code);
const cryptoPrice = formatCurrencyAsString(giftCryptoInfo.amount, giftCryptoInfo.currency, lang.code);
priceText = `${cryptoPrice} (${price})`;
}

Expand Down
8 changes: 8 additions & 0 deletions src/components/common/icons/StarIcon.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@
height: 1.5rem;
}

.adaptive {
display: inline-block;
width: 1em;
height: 1em;
line-height: 1;
vertical-align: text-top;
}

.svg {
width: 100%;
height: 100%;
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/icons/StarIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import styles from './StarIcon.module.scss';

type OwnProps = {
type?: 'gold' | 'premium' | 'regular';
size?: 'small' | 'middle' | 'big';
size?: 'small' | 'middle' | 'big' | 'adaptive';
className?: string;
onClick?: VoidFunction;
};
Expand Down
3 changes: 2 additions & 1 deletion src/components/middle/HeaderPinnedMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import buildClassName from '../../util/buildClassName';
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
import { getPictogramDimensions, REM } from '../common/helpers/mediaDimensions';
import renderText from '../common/helpers/renderText';
import renderKeyboardButtonText from './composer/helpers/renderKeyboardButtonText';

import { useFastClick } from '../../hooks/useFastClick';
import useFlag from '../../hooks/useFlag';
Expand Down Expand Up @@ -198,7 +199,7 @@ const HeaderPinnedMessage: FC<OwnProps> = ({
onMouseEnter={!IS_TOUCH_ENV ? markNoHoverColor : undefined}
onMouseLeave={!IS_TOUCH_ENV ? unmarkNoHoverColor : undefined}
>
{renderText(inlineButton.text)}
{renderKeyboardButtonText(lang, inlineButton)}
</Button>
)}
</div>
Expand Down
23 changes: 18 additions & 5 deletions src/components/middle/composer/BotKeyboardMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import type { FC } from '../../../lib/teact/teact';
import React, { memo } from '../../../lib/teact/teact';
import type { FC, TeactNode } from '../../../lib/teact/teact';
import React, { memo, useMemo } from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global';

import type { ApiMessage } from '../../../api/types';

import { selectChatMessage, selectCurrentMessageList } from '../../../global/selectors';
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
import renderKeyboardButtonText from './helpers/renderKeyboardButtonText';

import useMouseInside from '../../../hooks/useMouseInside';
import useOldLang from '../../../hooks/useOldLang';

import Button from '../../ui/Button';
import Menu from '../../ui/Menu';
Expand All @@ -29,9 +31,20 @@ const BotKeyboardMenu: FC<OwnProps & StateProps> = ({
}) => {
const { clickBotInlineButton } = getActions();

const lang = useOldLang();

const [handleMouseEnter, handleMouseLeave] = useMouseInside(isOpen, onClose);
const { isKeyboardSingleUse } = message || {};

const buttonTexts = useMemo(() => {
const texts: TeactNode[][] = [];
message?.keyboardButtons!.forEach((row) => {
texts.push(row.map((button) => renderKeyboardButtonText(lang, button)));
});

return texts;
}, [lang, message?.keyboardButtons]);

if (!message || !message.keyboardButtons) {
return undefined;
}
Expand All @@ -50,16 +63,16 @@ const BotKeyboardMenu: FC<OwnProps & StateProps> = ({
noCompact
>
<div className="content custom-scroll">
{message.keyboardButtons.map((row) => (
{message.keyboardButtons.map((row, i) => (
<div className="row">
{row.map((button) => (
{row.map((button, j) => (
<Button
ripple
disabled={button.type === 'unsupported'}
// eslint-disable-next-line react/jsx-no-bind
onClick={() => clickBotInlineButton({ messageId: message.id, button })}
>
{button.text}
{buttonTexts?.[i][j]}
</Button>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { type TeactNode } from '../../../../lib/teact/teact';

import type { ApiKeyboardButton } from '../../../../api/types';

import { replaceWithTeact } from '../../../../util/replaceWithTeact';
import renderText from '../../../common/helpers/renderText';

import { type LangFn } from '../../../../hooks/useOldLang';

import Icon from '../../../common/icons/Icon';

export default function renderKeyboardButtonText(lang: LangFn, button: ApiKeyboardButton): TeactNode {
if (button.type === 'receipt') {
return lang('PaymentReceipt');
}

if (button.type === 'buy') {
return replaceWithTeact(button.text, '⭐', <Icon className="star-currency-icon" name="star" />);
}

return renderText(button.text);
}
2 changes: 1 addition & 1 deletion src/components/middle/message/InlineButtons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
margin-right: 0;
}

.icon {
.corner-icon {
font-size: 0.875rem;
position: absolute;
right: 0.1875rem;
Expand Down
35 changes: 23 additions & 12 deletions src/components/middle/message/InlineButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import type { FC } from '../../../lib/teact/teact';
import React from '../../../lib/teact/teact';
import type { FC, TeactNode } from '../../../lib/teact/teact';
import React, { memo, useMemo } from '../../../lib/teact/teact';

import type { ApiKeyboardButton, ApiMessage } from '../../../api/types';

import { RE_TME_LINK } from '../../../config';
import renderText from '../../common/helpers/renderText';
import renderKeyboardButtonText from '../composer/helpers/renderKeyboardButtonText';

import useOldLang from '../../../hooks/useOldLang';

import Icon from '../../common/icons/Icon';
import Button from '../../ui/Button';

import './InlineButtons.scss';
Expand All @@ -25,37 +26,47 @@ const InlineButtons: FC<OwnProps> = ({ message, onClick }) => {
switch (type) {
case 'url': {
if (!RE_TME_LINK.test(button.url)) {
return <i className="icon icon-arrow-right" />;
return <Icon className="corner-icon" name="arrow-right" />;
}
break;
}
case 'urlAuth':
return <i className="icon icon-arrow-right" />;
return <Icon className="corner-icon" name="arrow-right" />;
case 'buy':
case 'receipt':
return <i className="icon icon-cart" />;
return <Icon className="corner-icon" name="card" />;
case 'switchBotInline':
return <i className="icon icon-share-filled" />;
return <Icon className="corner-icon" name="share-filled" />;
case 'webView':
case 'simpleWebView':
return <i className="icon icon-webapp" />;
return <Icon className="corner-icon" name="webapp" />;
}
return undefined;
};

const buttonTexts = useMemo(() => {
const texts: TeactNode[][] = [];
message.inlineButtons!.forEach((row) => {
texts.push(row.map((button) => renderKeyboardButtonText(lang, button)));
});
return texts;
}, [lang, message.inlineButtons]);

return (
<div className="InlineButtons">
{message.inlineButtons!.map((row) => (
{message.inlineButtons!.map((row, i) => (
<div className="row">
{row.map((button) => (
{row.map((button, j) => (
<Button
size="tiny"
ripple
disabled={button.type === 'unsupported'}
// eslint-disable-next-line react/jsx-no-bind
onClick={() => onClick({ messageId: message.id, button })}
>
<span className="inline-button-text">{renderText(lang(button.text))}</span>
<span className="inline-button-text">
{buttonTexts[i][j]}
</span>
{renderIcon(button)}
</Button>
))}
Expand All @@ -65,4 +76,4 @@ const InlineButtons: FC<OwnProps> = ({ message, onClick }) => {
);
};

export default InlineButtons;
export default memo(InlineButtons);
5 changes: 5 additions & 0 deletions src/components/middle/message/Invoice.scss
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@
.test-invoice {
margin-left: 0.5rem;
}

.invoice-currency-icon {
margin-inline-end: 0.125rem;
line-height: 1.5;
}
}

.invoice-image-container {
Expand Down
2 changes: 1 addition & 1 deletion src/components/middle/message/Invoice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ const Invoice: FC<OwnProps> = ({
</div>
)}
<p className="description-text">
{formatCurrency(amount, currency, lang.code)}
{formatCurrency(amount, currency, lang.code, { iconClassName: 'invoice-currency-icon' })}
{isTest && <span className="test-invoice">{lang('PaymentTestInvoice')}</span>}
</p>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/components/middle/message/InvoiceMediaPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { ApiMessage } from '../../../api/types';
import { getMessageInvoice } from '../../../global/helpers';
import buildClassName from '../../../util/buildClassName';
import { formatMediaDuration } from '../../../util/dates/dateFormat';
import { formatCurrency } from '../../../util/formatCurrency';
import { formatCurrencyAsString } from '../../../util/formatCurrency';

import useInterval from '../../../hooks/schedulers/useInterval';
import useLastCallback from '../../../hooks/useLastCallback';
Expand Down Expand Up @@ -74,7 +74,7 @@ const InvoiceMediaPreview: FC<OwnProps> = ({
{Boolean(duration) && <div className={styles.duration}>{formatMediaDuration(duration)}</div>}
<div className={styles.buy}>
<i className={buildClassName('icon', 'icon-lock', styles.lock)} />
{lang('Checkout.PayPrice', formatCurrency(amount, currency))}
{lang('Checkout.PayPrice', formatCurrencyAsString(amount, currency))}
</div>
</div>
);
Expand Down
6 changes: 3 additions & 3 deletions src/components/modals/collectible/CollectibleInfoModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { TabState } from '../../../global/types';

import { copyTextToClipboard } from '../../../util/clipboard';
import { formatDateAtTime } from '../../../util/dates/dateFormat';
import { formatCurrency } from '../../../util/formatCurrency';
import { formatCurrencyAsString } from '../../../util/formatCurrency';
import { formatPhoneNumberWithCode } from '../../../util/phoneNumber';
import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets';
import formatUsername from '../../common/helpers/formatUsername';
Expand Down Expand Up @@ -91,8 +91,8 @@ const CollectibleInfoModal: FC<OwnProps & StateProps> = ({
if (!modal) return undefined;
const key = isUsername ? 'FragmentUsernameMessage' : 'FragmentPhoneMessage';
const date = formatDateAtTime(lang, modal.purchaseDate * 1000);
const currency = formatCurrency(modal.amount, modal.currency, lang.code);
const cryptoCurrency = formatCurrency(modal.cryptoAmount, modal.cryptoCurrency, lang.code);
const currency = formatCurrencyAsString(modal.amount, modal.currency, lang.code);
const cryptoCurrency = formatCurrencyAsString(modal.cryptoAmount, modal.cryptoCurrency, lang.code);
const paid = `${cryptoCurrency} (${currency})`;
return lang(key, [date, paid]);
}, [modal, isUsername, lang]);
Expand Down
6 changes: 3 additions & 3 deletions src/components/payment/Checkout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ const Checkout: FC<OwnProps> = ({
className={buildClassName(styles.tipsItem, tip === tipAmount && styles.tipsItem_active)}
onClick={dispatch ? () => handleTipsClick(tip === tipAmount ? 0 : tip) : undefined}
>
{formatCurrency(tip, currency, lang.code, true)}
{formatCurrency(tip, currency, lang.code, { shouldOmitFractions: true })}
</div>
))}
</div>
Expand Down Expand Up @@ -199,7 +199,7 @@ const Checkout: FC<OwnProps> = ({
label: lang('PaymentCheckoutProvider'),
customIcon: buildClassName(styles.provider, styles[paymentProvider.toLowerCase()]),
})}
{(needAddress || !isInteractive) && renderCheckoutItem({
{(needAddress || (!isInteractive && shippingAddress)) && renderCheckoutItem({
title: shippingAddress,
label: lang('PaymentShippingAddress'),
icon: 'location',
Expand All @@ -215,7 +215,7 @@ const Checkout: FC<OwnProps> = ({
label: lang('PaymentCheckoutPhoneNumber'),
icon: 'phone',
})}
{(hasShippingOptions || !isInteractive) && renderCheckoutItem({
{(hasShippingOptions || (!isInteractive && shippingMethod)) && renderCheckoutItem({
title: shippingMethod,
label: lang('PaymentCheckoutShippingMethod'),
icon: 'truck',
Expand Down
4 changes: 2 additions & 2 deletions src/components/payment/PaymentModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { getUserFullName } from '../../global/helpers';
import { selectChat, selectTabState, selectUser } from '../../global/selectors';
import buildClassName from '../../util/buildClassName';
import captureKeyboardListeners from '../../util/captureKeyboardListeners';
import { formatCurrency } from '../../util/formatCurrency';
import { formatCurrencyAsString } from '../../util/formatCurrency';
import { detectCardTypeText } from '../common/helpers/detectCardType';

import usePaymentReducer from '../../hooks/reducers/usePaymentReducer';
Expand Down Expand Up @@ -517,7 +517,7 @@ const PaymentModal: FC<OwnProps & StateProps & GlobalStateProps> = ({
}, [step, lang]);

const buttonText = step === PaymentStep.Checkout
? lang('Checkout.PayPrice', formatCurrency(totalPrice, currency!, lang.code))
? lang('Checkout.PayPrice', formatCurrencyAsString(totalPrice, currency!, lang.code))
: lang('Next');

function getIsSubmitDisabled() {
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/Radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type OwnProps = {
id?: string;
name: string;
label: TeactNode;
subLabel?: string;
subLabel?: TeactNode;
value: string;
checked: boolean;
disabled?: boolean;
Expand Down
4 changes: 2 additions & 2 deletions src/components/ui/RadioGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Radio from './Radio';

export type IRadioOption<T = string> = {
label: TeactNode;
subLabel?: string;
subLabel?: TeactNode;
value: T;
hidden?: boolean;
className?: string;
Expand All @@ -25,7 +25,7 @@ type OwnProps = {
onClickAction?: (value: string) => void;
isLink?: boolean;
subLabelClassName?: string;
subLabel?: string | undefined;
subLabel?: TeactNode;
};

const RadioGroup: FC<OwnProps> = ({
Expand Down
Loading

0 comments on commit 6f79159

Please sign in to comment.