From 32dba93f1a8ee901cd1204998317eb20fa404e82 Mon Sep 17 00:00:00 2001 From: zavul0nn <34007368+zavul0nn@users.noreply.github.com> Date: Wed, 15 Apr 2026 03:27:47 +0300 Subject: [PATCH 1/8] Make SBP default and first top-up option (#389) --- src/pages/TopUpAmount.tsx | 61 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/pages/TopUpAmount.tsx b/src/pages/TopUpAmount.tsx index fac7ef8..0c470bf 100644 --- a/src/pages/TopUpAmount.tsx +++ b/src/pages/TopUpAmount.tsx @@ -10,7 +10,7 @@ import { checkRateLimit, getRateLimitResetTime, RATE_LIMIT_KEYS } from '../utils import { useCloseOnSuccessNotification } from '../store/successNotification'; import { useHaptic, usePlatform } from '@/platform'; import { staggerContainer, staggerItem } from '@/components/motion/transitions'; -import type { PaymentMethod } from '../types'; +import type { PaymentMethod, PaymentMethodOption } from '../types'; import BentoCard from '../components/ui/BentoCard'; import { saveTopUpPendingInfo } from '../utils/topUpStorage'; @@ -80,6 +80,44 @@ const getMethodIcon = (methodId: string) => { return ; }; +const getPreferredOptionId = (options?: PaymentMethod['options']) => { + if (!options || options.length === 0) return null; + + const sbpOption = options.find((option) => { + const normalizedId = option.id.toLowerCase(); + const normalizedName = option.name.toLowerCase(); + return ( + normalizedId.includes('sbp') || + normalizedName.includes('сбп') || + normalizedName.includes('sbp') + ); + }); + + return sbpOption?.id ?? options[0].id; +}; + +const sortOptionsWithSbpFirst = (options?: PaymentMethod['options']) => { + if (!options || options.length <= 1) return options ?? []; + + const isPreferredOption = (option: PaymentMethodOption) => { + const normalizedId = option.id.toLowerCase(); + const normalizedName = option.name.toLowerCase(); + return ( + normalizedId.includes('sbp') || + normalizedName.includes('сбп') || + normalizedName.includes('sbp') + ); + }; + + return [...options].sort((left, right) => { + const leftIsPreferred = isPreferredOption(left); + const rightIsPreferred = isPreferredOption(right); + + if (leftIsPreferred === rightIsPreferred) return 0; + return leftIsPreferred ? -1 : 1; + }); +}; + export default function TopUpAmount() { const { t } = useTranslation(); const navigate = useNavigate(); @@ -135,7 +173,7 @@ export default function TopUpAmount() { const [amount, setAmount] = useState(getInitialAmount); const [error, setError] = useState(null); const [selectedOption, setSelectedOption] = useState( - method?.options && method.options.length > 0 ? method.options[0].id : null, + getPreferredOptionId(method?.options), ); const [paymentUrl, setPaymentUrl] = useState(null); const [copied, setCopied] = useState(false); @@ -154,6 +192,20 @@ export default function TopUpAmount() { } }, [cachedMethods, method, navigate, searchParams]); + useEffect(() => { + if (!method?.options || method.options.length === 0) { + if (selectedOption !== null) { + setSelectedOption(null); + } + return; + } + + const optionExists = method.options.some((option) => option.id === selectedOption); + if (!optionExists) { + setSelectedOption(getPreferredOptionId(method.options)); + } + }, [method?.id, method?.options, selectedOption]); + const starsPaymentMutation = useMutation({ mutationFn: (amountKopeks: number) => balanceApi.createStarsInvoice(amountKopeks), onSuccess: async (data) => { @@ -248,6 +300,7 @@ export default function TopUpAmount() { } const hasOptions = method.options && method.options.length > 0; + const orderedOptions = sortOptionsWithSbpFirst(method.options); const minRubles = method.min_amount_kopeks / 100; const maxRubles = method.max_amount_kopeks / 100; const methodKey = method.id.toLowerCase().replace(/-/g, '_'); @@ -344,11 +397,11 @@ export default function TopUpAmount() { {/* Payment options (if any) */} - {hasOptions && method.options && ( + {hasOptions && orderedOptions.length > 0 && (
- {method.options.map((opt) => ( + {orderedOptions.map((opt) => (