From bf3c114a72796c0fe60eacf962fd09a95e347e10 Mon Sep 17 00:00:00 2001 From: c0mrade Date: Tue, 26 May 2026 22:24:20 +0300 Subject: [PATCH] refactor(subscription): extract DeleteSubscriptionSheet from god page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the delete-subscription expand/confirm block into src/components/subscription/sheets/DeleteSubscriptionSheet.tsx. The sheet itself decides between the Telegram destructive native dialog and the inline web confirm panel, so the parent no longer needs the platform discriminator. It owns deleteLoading and the API call; the parent passes onDeleted (navigate + invalidate) and the textSecondary glass color token used in the warning copy. Subscription.tsx: 1828 → 1750 lines. Drops now-unused imports (usePlatform; deleteLoading state). --- .../sheets/DeleteSubscriptionSheet.tsx | 127 ++++++++++++++++++ src/pages/Subscription.tsx | 102 ++------------ 2 files changed, 139 insertions(+), 90 deletions(-) create mode 100644 src/components/subscription/sheets/DeleteSubscriptionSheet.tsx diff --git a/src/components/subscription/sheets/DeleteSubscriptionSheet.tsx b/src/components/subscription/sheets/DeleteSubscriptionSheet.tsx new file mode 100644 index 0000000..b00c46f --- /dev/null +++ b/src/components/subscription/sheets/DeleteSubscriptionSheet.tsx @@ -0,0 +1,127 @@ +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { subscriptionApi } from '../../../api/subscription'; +import { usePlatform } from '../../../platform'; +import { useDestructiveConfirm } from '../../../platform/hooks/useNativeDialog'; + +// ────────────────────────────────────────────────────────────────── +// Delete-subscription sheet. Telegram path goes through the native +// destructive dialog and skips the inline expand; the web path expands +// an inline confirm panel with two buttons. +// +// Self-owns deleteLoading; parent only supplies the id, theme colour +// hints, open-state, and the onDeleted callback (used for navigate + +// invalidate after the API resolves). +// ────────────────────────────────────────────────────────────────── + +export interface DeleteSubscriptionSheetProps { + subscriptionId: number; + open: boolean; + onOpen: () => void; + onClose: () => void; + onDeleted: () => void; + /** color/background tokens already resolved from glassTheme */ + textSecondary: string; +} + +export function DeleteSubscriptionSheet({ + subscriptionId, + open, + onOpen, + onClose, + onDeleted, + textSecondary, +}: DeleteSubscriptionSheetProps) { + const { t } = useTranslation(); + const { platform } = usePlatform(); + const destructiveConfirm = useDestructiveConfirm(); + const [deleteLoading, setDeleteLoading] = useState(false); + + const performDelete = async () => { + setDeleteLoading(true); + try { + await subscriptionApi.deleteSubscription(subscriptionId); + onDeleted(); + } catch { + setDeleteLoading(false); + onClose(); + } + }; + + const handleTriggerClick = async () => { + if (platform === 'telegram') { + const confirmed = await destructiveConfirm( + t( + 'subscription.deleteWarning', + 'Подписка будет удалена безвозвратно. Все данные, устройства и настройки будут потеряны.', + ), + t('subscription.confirmDelete', 'Да, удалить'), + t('subscription.deleteTitle', 'Удалить подписку?'), + ); + if (!confirmed) return; + await performDelete(); + } else { + onOpen(); + } + }; + + if (!open) { + return ( + + ); + } + + return ( +
+
+ {t('subscription.deleteTitle', 'Удалить подписку?')} +
+
+ {t( + 'subscription.deleteWarning', + 'Подписка будет удалена безвозвратно. Все данные, устройства и настройки будут потеряны. Это действие нельзя отменить.', + )} +
+
+ + +
+
+ ); +} diff --git a/src/pages/Subscription.tsx b/src/pages/Subscription.tsx index d43eb7e..743f80d 100644 --- a/src/pages/Subscription.tsx +++ b/src/pages/Subscription.tsx @@ -6,7 +6,6 @@ import { subscriptionApi } from '../api/subscription'; import { DEVICE_ALIAS_MAX_LENGTH } from '../constants/devices'; import { WebBackButton } from '../components/WebBackButton'; import { useDestructiveConfirm } from '../platform/hooks/useNativeDialog'; -import { usePlatform } from '../platform'; import TrafficProgressBar from '../components/dashboard/TrafficProgressBar'; import { HoverBorderGradient } from '../components/ui/hover-border-gradient'; import { useTrafficZone } from '../hooks/useTrafficZone'; @@ -31,6 +30,7 @@ import { DeviceTopupSheet } from '../components/subscription/sheets/DeviceTopupS import { DeviceReductionSheet } from '../components/subscription/sheets/DeviceReductionSheet'; import { TrafficTopupSheet } from '../components/subscription/sheets/TrafficTopupSheet'; import { ServerManagementSheet } from '../components/subscription/sheets/ServerManagementSheet'; +import { DeleteSubscriptionSheet } from '../components/subscription/sheets/DeleteSubscriptionSheet'; /** Isolated countdown so 1s interval doesn't re-render the whole page */ const CountdownTimer = memo(function CountdownTimer({ @@ -195,8 +195,6 @@ export default function Subscription() { const haptic = useHaptic(); const [copied, setCopied] = useState(false); const [showDeleteSheet, setShowDeleteSheet] = useState(false); - const [deleteLoading, setDeleteLoading] = useState(false); - const { platform } = usePlatform(); const destructiveConfirm = useDestructiveConfirm(); // Helper to format price from kopeks @@ -1340,93 +1338,17 @@ export default function Subscription() { !subscription.is_trial && !subscription.is_limited && (
- {!showDeleteSheet ? ( - - ) : ( -
-
- {t('subscription.deleteTitle', 'Удалить подписку?')} -
-
- {t( - 'subscription.deleteWarning', - 'Подписка будет удалена безвозвратно. Все данные, устройства и настройки будут потеряны. Это действие нельзя отменить.', - )} -
-
- - -
-
- )} + setShowDeleteSheet(true)} + onClose={() => setShowDeleteSheet(false)} + textSecondary={g.textSecondary} + onDeleted={() => { + queryClient.invalidateQueries({ queryKey: ['subscriptions-list'] }); + navigate('/subscriptions', { replace: true }); + }} + />
)}