kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Merge branch 'upgrade-ui-zap-request' into 'main'
Upgrade ui zap request Closes #1717 See merge request soapbox-pub/soapbox!3107environments/review-main-yi2y9f/deployments/4805
commit
aff33deeba
|
@ -0,0 +1,45 @@
|
|||
import React from 'react';
|
||||
|
||||
import { useSoapboxConfig } from 'soapbox/hooks';
|
||||
|
||||
import { getAcct } from '../utils/accounts';
|
||||
|
||||
import { HStack, Text } from './ui';
|
||||
import VerificationBadge from './verification-badge';
|
||||
|
||||
import type { Account } from 'soapbox/schemas';
|
||||
|
||||
interface IDisplayName {
|
||||
account: Pick<Account, 'id' | 'acct' | 'fqn' | 'verified' | 'display_name_html'>;
|
||||
withSuffix?: boolean;
|
||||
}
|
||||
|
||||
const DisplayNameInline: React.FC<IDisplayName> = ({ account, withSuffix = true }) => {
|
||||
const { displayFqn = false } = useSoapboxConfig();
|
||||
const { verified } = account;
|
||||
|
||||
const displayName = (
|
||||
<HStack space={1} alignItems='center' justifyContent='center' grow>
|
||||
<Text
|
||||
size='sm'
|
||||
weight='normal'
|
||||
truncate
|
||||
dangerouslySetInnerHTML={{ __html: account.display_name_html }}
|
||||
/>
|
||||
|
||||
{verified && <VerificationBadge />}
|
||||
</HStack>
|
||||
);
|
||||
|
||||
const suffix = (<span className='display-name'>@{getAcct(account, displayFqn)}</span>);
|
||||
|
||||
return (
|
||||
<div className='flex max-w-80 flex-col items-center justify-center text-center sm:flex-row sm:gap-2'>
|
||||
{displayName}
|
||||
<span className='hidden text-xl font-bold sm:block'>-</span>
|
||||
{withSuffix && suffix}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DisplayNameInline;
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Modal } from 'soapbox/components/ui';
|
||||
|
||||
|
@ -18,13 +17,10 @@ const ZapPayRequestModal: React.FC<IZapPayRequestModal> = ({ account, status, on
|
|||
onClose('ZAP_PAY_REQUEST');
|
||||
};
|
||||
|
||||
const renderTitle = () => {
|
||||
return <FormattedMessage id='zap.send_to' defaultMessage='Send zaps to {target}' values={{ target: account.display_name }} />;
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal title={renderTitle()} onClose={onClickClose}>
|
||||
<ZapPayRequestForm account={account} status={status} />
|
||||
<Modal width='lg'>
|
||||
<ZapPayRequestForm account={account} status={status} onClose={onClickClose} />
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { shortNumberFormat } from 'soapbox/utils/numbers';
|
||||
|
||||
interface IButton {
|
||||
/** Whether this button expands the width of its container. */
|
||||
block?: boolean;
|
||||
/** Elements inside the <button> */
|
||||
children?: React.ReactNode;
|
||||
/** Extra class names for the button. */
|
||||
className?: string;
|
||||
/** Prevent the button from being clicked. */
|
||||
disabled?: boolean;
|
||||
/** Specifies the icon element as 'svg' or 'img'. */
|
||||
iconElement?: 'svg' | 'img';
|
||||
/** URL to an SVG icon to render inside the button. */
|
||||
icon?: string;
|
||||
/** Action when the button is clicked. */
|
||||
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
/** Amount of sats. */
|
||||
amount?: number;
|
||||
/** Makes the button into a navlink, if provided. */
|
||||
to?: string;
|
||||
/** Change the button style if it is selected. */
|
||||
selected: boolean;
|
||||
/** Whether this button should submit a form by default. */
|
||||
type?: 'button' | 'submit';
|
||||
}
|
||||
|
||||
/** Customizable button element. */
|
||||
const ZapButton = React.forwardRef<HTMLButtonElement, IButton>((props, ref): JSX.Element => {
|
||||
const {
|
||||
disabled = false,
|
||||
icon,
|
||||
onClick,
|
||||
selected,
|
||||
to,
|
||||
amount,
|
||||
type = 'button',
|
||||
className,
|
||||
} = props;
|
||||
|
||||
const renderButton = () => (
|
||||
<button
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
ref={ref}
|
||||
type={type}
|
||||
data-testid='button'
|
||||
>
|
||||
<div className={clsx(className, { 'flex flex-col items-center w-14 !box-border place-content-center border font-medium p-2 rounded-xl focus:outline-none focus:ring-2 focus:ring-offset-2 appearance-none transition-all sm:p-4 sm:w-20': true,
|
||||
'select-none disabled:opacity-75 disabled:cursor-default': disabled,
|
||||
'bg-primary-500 hover:bg-primary-400 dark:hover:bg-primary-600 border-transparent focus:bg-primary-500 text-gray-100 focus:ring-primary-300': selected,
|
||||
'border border-solid bg-transparent border-gray-400 dark:border-gray-800 hover:border-primary-300 dark:hover:border-primary-700 focus:border-primary-500 text-gray-900 dark:text-gray-100 focus:ring-primary-500': !selected })}
|
||||
>
|
||||
<img className='w-16' src={icon} alt='stack coin' />
|
||||
<span className='text-base sm:text-2xl'>
|
||||
<p>
|
||||
{shortNumberFormat(amount)}
|
||||
</p>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
|
||||
if (to) {
|
||||
return (
|
||||
<Link to={to} tabIndex={-1} className='inline-flex'>
|
||||
{renderButton()}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return renderButton();
|
||||
});
|
||||
|
||||
export {
|
||||
ZapButton as default,
|
||||
ZapButton,
|
||||
};
|
|
@ -8,15 +8,21 @@ import coinStack from 'soapbox/assets/icons/coin-stack.png';
|
|||
import coinIcon from 'soapbox/assets/icons/coin.png';
|
||||
import moneyBag from 'soapbox/assets/icons/money-bag.png';
|
||||
import pileCoin from 'soapbox/assets/icons/pile-coin.png';
|
||||
import Account from 'soapbox/components/account';
|
||||
import { Stack, Button, Input } from 'soapbox/components/ui';
|
||||
import DisplayNameInline from 'soapbox/components/display-name-inline';
|
||||
import { Stack, Button, Input, Avatar } from 'soapbox/components/ui';
|
||||
import IconButton from 'soapbox/components/ui/icon-button/icon-button';
|
||||
import { useAppDispatch } from 'soapbox/hooks';
|
||||
|
||||
import ZapButton from './zap-button/zap-button';
|
||||
|
||||
import type { Account as AccountEntity, Status as StatusEntity } from 'soapbox/types/entities';
|
||||
|
||||
const closeIcon = require('@tabler/icons/outline/x.svg');
|
||||
|
||||
interface IZapPayRequestForm {
|
||||
status?: StatusEntity;
|
||||
account: AccountEntity;
|
||||
onClose?(): void;
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -25,12 +31,15 @@ const messages = defineMessages({
|
|||
zap_commentPlaceholder: { id: 'zap.comment_input.placeholder', defaultMessage: 'Optional comment' },
|
||||
});
|
||||
|
||||
const ZapPayRequestForm = ({ account, status }: IZapPayRequestForm) => {
|
||||
const ZapPayRequestForm = ({ account, status, onClose }: IZapPayRequestForm) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const [zapComment, setZapComment] = useState('');
|
||||
// amount in millisatoshi
|
||||
const [zapAmount, setZapAmount] = useState(50);
|
||||
const ZAP_AMOUNTS = [50, 200, 1_000, 3_000, 5_000];
|
||||
const ZAP_ICONS = [coinIcon, coinStack, pileCoin, moneyBag, chestIcon];
|
||||
|
||||
|
||||
const handleSubmit = async (e?: React.FormEvent<Element>) => {
|
||||
e?.preventDefault();
|
||||
|
@ -64,28 +73,34 @@ const ZapPayRequestForm = ({ account, status }: IZapPayRequestForm) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Stack space={3} element='form' onSubmit={handleSubmit}>
|
||||
<Account account={account} showProfileHoverCard={false} />
|
||||
<div>
|
||||
<FormattedMessage id='zap.unit' defaultMessage='Zap amount in sats' />
|
||||
</div>
|
||||
<Stack space={4} element='form' onSubmit={handleSubmit} justifyContent='center' alignItems='center' className='relative'>
|
||||
<Stack space={2} justifyContent='center' alignItems='center' >
|
||||
<IconButton src={closeIcon} onClick={onClose} className='absolute -right-[1%] -top-[2%] text-gray-500 hover:text-gray-700 rtl:rotate-180 dark:text-gray-300 dark:hover:text-gray-200' />
|
||||
<span className='display-name__account text-base'>
|
||||
<FormattedMessage id='zap.send_to' defaultMessage='Send zaps to {target}' values={{ target: account.display_name }} />
|
||||
</span>
|
||||
<Avatar src={account.avatar} size={50} />
|
||||
<DisplayNameInline account={account} />
|
||||
</Stack>
|
||||
|
||||
<div className='flex justify-center '>
|
||||
<Button onClick={() => setZapAmount(50)} className='m-1' type='button' iconElement='img' icon={coinIcon} theme={zapAmount === 50 ? 'primary' : 'muted'} text='50' />
|
||||
<Button onClick={() => setZapAmount(200)} className='m-1' type='button' iconElement='img' icon={coinStack} theme={zapAmount === 200 ? 'primary' : 'muted'} text='200' />
|
||||
<Button onClick={() => setZapAmount(1_000)} className='m-1' type='button' iconElement='img' icon={pileCoin} theme={zapAmount === 1_000 ? 'primary' : 'muted'} text='1K' />
|
||||
<Button onClick={() => setZapAmount(3_000)} className='m-1' type='button' iconElement='img' icon={moneyBag} theme={zapAmount === 3_000 ? 'primary' : 'muted'} text='3K' />
|
||||
<Button onClick={() => setZapAmount(5_000)} className='m-1' type='button' iconElement='img' icon={chestIcon} theme={zapAmount === 5_000 ? 'primary' : 'muted'} text='5K' />
|
||||
{ZAP_AMOUNTS.map((amount, i) => <ZapButton onClick={() => setZapAmount(amount)} className='m-0.5 sm:m-1' selected={zapAmount === amount} icon={ZAP_ICONS[i]} amount={amount} />)}
|
||||
</div>
|
||||
|
||||
<div className='flex justify-center'>
|
||||
<Input
|
||||
type='text' onChange={handleCustomAmount} value={zapAmount}
|
||||
className='box-shadow:none max-w-28 rounded-none border-0 border-b-4 p-0.5 text-center !text-2xl font-bold !ring-0 dark:bg-transparent'
|
||||
/>
|
||||
</div>
|
||||
<Stack space={2}>
|
||||
<div className='relative flex items-end justify-center gap-4'>
|
||||
<Input
|
||||
type='text' onChange={handleCustomAmount} value={zapAmount}
|
||||
className='box-shadow:none max-w-20 rounded-none border-0 border-b-4 p-0 text-center !text-2xl font-bold !ring-0 sm:max-w-28 sm:p-0.5 sm:!text-4xl dark:bg-transparent'
|
||||
/>
|
||||
<p className='absolute -right-10 font-bold sm:-right-12 sm:text-xl'>sats</p>
|
||||
</div>
|
||||
|
||||
<Input onChange={e => setZapComment(e.target.value)} type='text' placeholder={intl.formatMessage(messages.zap_commentPlaceholder)} />
|
||||
</Stack>
|
||||
|
||||
<div className='w-full'>
|
||||
<Input onChange={e => setZapComment(e.target.value)} type='text' placeholder={intl.formatMessage(messages.zap_commentPlaceholder)} />
|
||||
</div>
|
||||
<Button className='m-auto w-auto' type='submit' theme='primary' icon={require('@tabler/icons/outline/bolt.svg')} text={renderZapButtonText()} disabled={zapAmount < 1 ? true : false} />
|
||||
</Stack>
|
||||
);
|
||||
|
|
|
@ -1653,6 +1653,5 @@
|
|||
"zap.button.text.rounded": "Zap {amount}K sats",
|
||||
"zap.comment_input.placeholder": "Optional comment",
|
||||
"zap.open_wallet": "Open Wallet",
|
||||
"zap.send_to": "Send zaps to {target}",
|
||||
"zap.unit": "Zap amount in sats"
|
||||
"zap.send_to": "Send zaps to {target}"
|
||||
}
|
Ładowanie…
Reference in New Issue