Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

frontend: only show buyable coins in moonpay dropdown #1135

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontends/web/src/locales/en/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@
"skip": "Do not show again",
"title": "Buy {{name}}"
},
"title": "Buy crypto"
"title": "Buy {{name}}"
},
"changePin": {
"newTitle": "New device password",
Expand Down
16 changes: 16 additions & 0 deletions frontends/web/src/routes/account/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,22 @@

import { CoinCode } from './account';

export function isBitcoin(code: string): boolean {
switch (code) {
case 'btc':
case 'btc-p2wpkh':
case 'btc-p2wpkh-p2sh':
case 'btc-p2pkh':
case 'tbtc':
case 'tbtc-p2wpkh':
case 'tbtc-p2wpkh-p2sh':
case 'tbtc-p2pkh':
return true;
default:
return false;
}
}

export function isBitcoinBased(coinCode: CoinCode): boolean {
switch (coinCode) {
case 'btc':
Expand Down
97 changes: 62 additions & 35 deletions frontends/web/src/routes/buy/info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import { load } from '../../decorators/load';
import { translate, TranslateProps } from '../../decorators/translate';
import { Button, Checkbox, Select } from '../../components/forms';
import { Devices } from '../device/deviceswitch';
import { AccountInterface } from '../account/account';
import { AccountInterface, CoinCode } from '../account/account';
import { setConfig } from '../../utils/config';
import { apiGet } from '../../utils/request';
import { isBitcoin } from '../account/utils';
import * as style from './info.css';

interface BuyInfoProps {
Expand All @@ -37,9 +39,15 @@ interface LoadedBuyInfoProps {
config: any;
}

interface Option {
text: string;
value: CoinCode;
}

interface State {
status: 'choose' | 'agree'
selected?: string;
options: Option[]
}

type Props = BuyInfoProps & LoadedBuyInfoProps & TranslateProps;
Expand All @@ -48,31 +56,52 @@ class BuyInfo extends Component<Props, State> {
public readonly state: State = {
status: this.props.config.frontend.skipBuyDisclaimer ? 'choose' : 'agree',
selected: this.props.code,
options: [],
}

private handleProceed = () => {
const { status, selected } = this.state;
if (selected && (status === 'choose' || this.props.config.frontend.skipBuyDisclaimer)) {
route(`/buy/moonpay/${selected}`);
} else {
this.setState({ status: 'choose' });
this.setState({ status: 'choose' }, this.checkSupportedCoins);
}
}

private checkSupportedCoins = () => {
Promise.all(
this.props.accounts.map((account) => (
apiGet(`account/${account.code}/exchange/moonpay/buy-supported`)
.then(isSupported => (isSupported ? account : false))
))
)
.then(results => results.filter(result => result))
// @ts-ignore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's ignored here exactly?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in the filter before it filters out all results that are false (the ones that returned false in buy-supported), so on the next line TypeScript still thinks there could be false

Similar issue "filter should narrow down output type"
microsoft/TypeScript#20812

Couldn't manage as suggested to do "User-Defined Type Guards"
https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. So, something like this wouldn't work?

function isSupported(a: Account | bool): a is Account {
  return a !== false
}

.then(results => results.filter<Account>(isSupported))

.then(accounts => accounts.map(({ name, code }) => ({ text: name, value: code })))
.then(options => {
if (this.state.status === 'choose' && options.length === 1) {
route(`/buy/moonpay/${options[0].value}`);
} else {
this.setState({ options });
}
})
.catch(console.error);
}

private handleSkipDisclaimer = (e) => {
setConfig({ frontend: { skipBuyDisclaimer: e.target.checked }});
}

public render(
{ accounts,
code,
{ code,
t }: RenderableProps<Props>,
{
status,
selected,
options,
}: State
) {
const name = (code === 'btc' || code === 'tbtc') ? 'Bitcoin' : 'crypto';
const name = (code && isBitcoin(code)) ? 'Bitcoin' : 'crypto';
return (
<div class="contentWithGuide">
<div class="container">
Expand Down Expand Up @@ -142,7 +171,7 @@ class BuyInfo extends Component<Props, State> {
<h2 class={style.title}>
{t('buy.info.disclaimer.protection.title')}
</h2>
<p>{t('buy.info.disclaimer.protection.description')}</p>
<p>{t('buy.info.disclaimer.protection.description', { name })}</p>
<p>
<A class={style.link} href="https://support.moonpay.com/hc/en-gb/articles/360009279877-What-are-your-supported-countries-states-and-territories-">
{t('buy.info.disclaimer.privacyPolicy')}
Expand All @@ -164,36 +193,34 @@ class BuyInfo extends Component<Props, State> {
</div>
</div>
) : (
<div class="content narrow isVerticallyCentered">
<h1 class={style.title}>{t('buy.title')}</h1>
<Select
label={t('buy.info.selectLabel')}
placeholder={t('buy.info.selectPlaceholder')}
options={[{
text: t('buy.info.selectPlaceholder'),
disabled: true,
value: 'choose',
},
...accounts.map(({ code, name}) => ({
text: `${name} (${code})`,
value: code,
}))]
}
onChange={e => this.setState({ selected: e.target.value})}
value={selected}
defaultValue={'choose'}
id="coinAndAccountCode"
/>
<div class="buttons text-center m-bottom-xxlarge">
<Button
primary
onClick={this.handleProceed}
disabled={!selected}>
{t('buy.info.next')}
</Button>
options.length === 0 ? null : (
<div class="content narrow isVerticallyCentered">
<h1 class={style.title}>{t('buy.title', { name })}</h1>
<Select
label={t('buy.info.selectLabel')}
placeholder={t('buy.info.selectPlaceholder')}
options={[{
text: t('buy.info.selectPlaceholder'),
disabled: true,
value: 'choose',
},
...options]
}
onChange={e => this.setState({ selected: e.target.value})}
value={selected}
defaultValue={'choose'}
id="coinAndAccountCode"
/>
<div class="buttons text-center m-bottom-xxlarge">
<Button
primary
onClick={this.handleProceed}
disabled={!selected}>
{t('buy.info.next')}
</Button>
</div>
</div>

</div>
)
)}
</div>
</div>
Expand Down