/* eslint-disable no-nested-ternary */ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context'; import Toast from '@arcblock/ux/lib/Toast'; import type { PriceCurrency, PriceRecurring, TPricingTableExpanded, TPricingTableItem } from '@blocklet/payment-types'; import { CheckOutlined } from '@mui/icons-material'; import { Avatar, Box, Chip, List, ListItem, ListItemIcon, ListItemText, MenuItem, Select, Stack, ToggleButton, ToggleButtonGroup, Typography, } from '@mui/material'; import { styled } from '@mui/system'; import { useSetState } from 'ahooks'; import { useEffect, useMemo, useState } from 'react'; import { BN } from '@ocap/util'; import isEmpty from 'lodash/isEmpty'; import { usePaymentContext } from '../contexts/payment'; import { formatError, formatPriceAmount, formatRecurring, getPriceCurrencyOptions, getPriceUintAmountByCurrency, isMobileSafari, } from '../libs/util'; import { useMobile } from '../hooks/mobile'; import TruncatedText from './truncated-text'; import LoadingButton from './loading-button'; type SortOrder = { [key: string]: number }; const sortOrder: SortOrder = { year: 1, month: 2, day: 3, hour: 4, }; const groupItemsByRecurring = (items: TPricingTableItem[], currency: { id: string; symbol: string }) => { const grouped: { [key: string]: TPricingTableItem[] } = {}; const recurring: { [key: string]: PriceRecurring } = {}; (items || []).forEach((x) => { const key = [x.price.recurring?.interval, x.price.recurring?.interval_count].join('-'); if (x.price.currency_options?.find((c: PriceCurrency) => c.currency_id === currency.id)) { recurring[key] = x.price.recurring as PriceRecurring; } if (!grouped[key]) { grouped[key] = []; } // @ts-ignore grouped[key].push(x); }); return { recurring, grouped }; }; type Props = { table: TPricingTableExpanded; onSelect: (priceId: string, currencyId: string) => void; alignItems?: 'center' | 'left'; mode?: 'checkout' | 'select'; interval?: string; hideCurrency?: boolean; }; export default function PricingTable({ table, alignItems = 'center', interval = '', mode = 'checkout', onSelect, hideCurrency = false, }: Props) { const { t, locale } = useLocaleContext(); const { isMobile } = useMobile(); const { settings: { paymentMethods = [] }, livemode, setLivemode, refresh, } = usePaymentContext(); const isMobileSafariEnv = isMobileSafari(); useEffect(() => { if (table) { if (livemode !== table.livemode) { setLivemode(table.livemode); } } }, [table, livemode, setLivemode, refresh]); const [currency, setCurrency] = useState(table.currency || {}); const { recurring, grouped } = useMemo(() => groupItemsByRecurring(table.items, currency), [table.items, currency]); const recurringKeysList = useMemo(() => { if (isEmpty(recurring)) { return []; } return Object.keys(recurring).sort((a, b) => { const [aType, aValue] = a.split('-'); const [bType, bValue] = b.split('-'); if (sortOrder[aType] !== sortOrder[bType]) { return sortOrder[aType] - sortOrder[bType]; } if (aValue && bValue) { // @ts-ignore return bValue - aValue; } // @ts-ignore return b - a; }); }, [recurring]); const [state, setState] = useSetState({ interval }); const currencyMap = useMemo(() => { if (!paymentMethods || paymentMethods.length === 0) { return {}; } const ans: { [key: string]: any } = {}; paymentMethods.forEach((paymentMethod) => { const { payment_currencies: paymentCurrencies = [] } = paymentMethod; if (paymentCurrencies && paymentCurrencies.length > 0) { paymentCurrencies.forEach((x: { id: string; symbol: string }) => { ans[x.id] = { ...x, method: paymentMethod.name, }; }); } }); return ans; }, [paymentMethods]); const currencyList = useMemo(() => { const visited: { [key: string]: boolean } = {}; if (!state.interval) { return []; } (grouped[state.interval] || []).forEach((x: TPricingTableItem) => { getPriceCurrencyOptions(x.price).forEach((c: PriceCurrency) => { visited[c?.currency_id] = true; }); }); return Object.keys(visited) .map((x) => currencyMap[x]) .filter((v) => v); }, [currencyMap, grouped, state.interval]); const productList = useMemo(() => { return (grouped[state.interval as string] || []).filter((x: TPricingTableItem) => { const price = getPriceUintAmountByCurrency(x.price, currency); if (new BN(price).isZero() || !price) { return false; } return true; }); }, [grouped, state.interval, currency]); useEffect(() => { if (table) { if (!state.interval || !grouped[state.interval]) { const keys = Object.keys(recurring); if (keys[0]) { setState({ interval: keys[0] }); } } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [table]); const Root = styled(Box)` .btn-row { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; width: 100%; gap: 20px; } .price-table-wrap { scrollbar-width: none; -ms-overflow-style: none; &::-webkit-scrollbar { display: none; } } @media (max-width: ${({ theme }) => theme.breakpoints.values.sm}px) { // .price-table-item { // width: 90% !important; // } // .btn-row { // padding: 0 20px; // } } @media (min-width: ${({ theme }) => theme.breakpoints.values.md}px) { .price-table-wrap:has(> div:nth-of-type(1)) { max-width: 360px !important; } .price-table-wrap:has(> div:nth-of-type(2)) { max-width: 780px !important; } .price-table-wrap:has(> div:nth-of-type(3)) { max-width: 1200px !important; } } `; return ( {recurringKeysList.length > 0 && ( {isMobile && recurringKeysList.length > 1 ? ( ) : ( { if (value !== null) { setState({ interval: value }); } }} exclusive> {recurringKeysList.map((x) => ( x === state.interval ? `${palette.background.default} !important` : `${palette.grey[100]} !important`, border: '0px', '&.Mui-selected': { borderRadius: '9999px !important', border: '1px solid', borderColor: 'divider', }, }}> {formatRecurring(recurring[x] as PriceRecurring, true, '', locale)} ))} )} )} {currencyList.length > 0 && !hideCurrency && ( )} {productList?.map((x: TPricingTableItem & { is_selected?: boolean; is_disabled?: boolean }) => { let action: string = x.subscription_data?.trial_period_days ? t('payment.checkout.try') : t('payment.checkout.subscription'); if (mode === 'select') { action = x.is_selected ? t('payment.checkout.selected') : t('payment.checkout.select'); } const [amount, unit] = formatPriceAmount(x.price, currency, x.product.unit_label).split('/'); return ( {x.is_highlight && ( )} {amount} {unit ? ( / {unit} ) : ( '' )} {x.product.description} {x.product.features.length > 0 && ( {x.product.features.map((f: any) => ( ))} )} ); })} ); } function Subscribe({ x, action, onSelect, currencyId }: any) { const [state, setState] = useState({ loading: '', loaded: false }); const handleSelect = async (priceId: string) => { try { setState({ loading: priceId, loaded: true }); await onSelect(priceId, currencyId); } catch (err) { console.error(err); Toast.error(formatError(err)); } }; return ( handleSelect(x.price_id)}> {action} ); }