From 0389acdf83eb8f0e14301f0d0515000467a30ccc Mon Sep 17 00:00:00 2001 From: Fringg Date: Mon, 9 Feb 2026 05:53:33 +0300 Subject: [PATCH 1/7] fix: prevent useCloseOnSuccessNotification from firing on mount The hook checked closeOthersSignal > 0 which only skipped signal=0. After first payment, signal stays at 1+ forever causing TopUpAmount to immediately navigate away on every mount. Now tracks the signal value at mount time and only reacts to changes after mount. --- src/store/successNotification.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/store/successNotification.ts b/src/store/successNotification.ts index b5085a5..8ffdc54 100644 --- a/src/store/successNotification.ts +++ b/src/store/successNotification.ts @@ -67,9 +67,11 @@ export function useCloseOnSuccessNotification(onClose: () => void) { const onCloseRef = useRef(onClose); onCloseRef.current = onClose; + // Remember the signal value at mount time so we only react to NEW signals + const mountedSignalRef = useRef(closeOthersSignal); + useEffect(() => { - // Skip the initial render (signal = 0) - if (closeOthersSignal > 0) { + if (closeOthersSignal !== mountedSignalRef.current) { onCloseRef.current(); } }, [closeOthersSignal]); From e5ed6d0401892eabebd5bd226755cbf5f5ca927c Mon Sep 17 00:00:00 2001 From: c0mrade Date: Mon, 9 Feb 2026 10:48:25 +0300 Subject: [PATCH 2/7] fix: hide Telegram back button on bottom nav pages Back button was showing on all non-root routes including top-level pages reachable from the bottom navigation menu (subscription, balance, referral, support, wheel). These pages don't need a back button since users navigate between them via the bottom tab bar. --- src/AppWithNavigator.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/AppWithNavigator.tsx b/src/AppWithNavigator.tsx index 53ec831..6f6dfeb 100644 --- a/src/AppWithNavigator.tsx +++ b/src/AppWithNavigator.tsx @@ -20,6 +20,9 @@ import { isInTelegramWebApp } from './hooks/useTelegramSDK'; * Manages Telegram BackButton visibility based on navigation location. * Shows back button on non-root routes, hides on root. */ +/** Pages reachable from bottom nav — treat as top-level (no back button). */ +const BOTTOM_NAV_PATHS = ['/', '/subscription', '/balance', '/referral', '/support', '/wheel']; + function TelegramBackButton() { const location = useLocation(); const navigate = useNavigate(); @@ -27,9 +30,9 @@ function TelegramBackButton() { navigateRef.current = navigate; useEffect(() => { - const isRoot = location.pathname === '/' || location.pathname === ''; + const isTopLevel = location.pathname === '' || BOTTOM_NAV_PATHS.includes(location.pathname); try { - if (isRoot) { + if (isTopLevel) { hideBackButton(); } else { showBackButton(); From fb25df6f0f5dee55fc40496e29bf22c94efc27b3 Mon Sep 17 00:00:00 2001 From: c0mrade Date: Mon, 9 Feb 2026 11:23:33 +0300 Subject: [PATCH 3/7] feat: add empty state for connection page when no apps configured Show different messages depending on user role: - Regular users see a friendly "check back later" message - Admins see a prompt with a link to /admin/apps settings --- src/locales/en.json | 6 +++- src/locales/fa.json | 6 +++- src/locales/ru.json | 6 +++- src/locales/zh.json | 6 +++- src/pages/Connection.tsx | 70 +++++++++++++++++++++++++++++++++++++--- 5 files changed, 86 insertions(+), 8 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index df1bd61..395d587 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -355,7 +355,11 @@ "selectPlatform": "Select platform", "app": "app", "apps": "apps", - "tutorial": "Tutorial" + "tutorial": "Tutorial", + "notConfigured": "Connection methods are not configured yet", + "notConfiguredUser": "Connection setup is in progress. Please check back later.", + "notConfiguredAdmin": "No connection apps have been configured. Set them up in the Apps settings.", + "goToApps": "Go to Apps settings" }, "myDevices": "My Devices", "noDevices": "No connected devices", diff --git a/src/locales/fa.json b/src/locales/fa.json index c1be32b..5ffd5a5 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -307,7 +307,11 @@ "selectPlatform": "انتخاب پلتفرم", "app": "برنامه", "apps": "برنامه", - "tutorial": "آموزش" + "tutorial": "آموزش", + "notConfigured": "روش‌های اتصال هنوز پیکربندی نشده‌اند", + "notConfiguredUser": "تنظیمات اتصال در حال انجام است. لطفاً بعداً مراجعه کنید.", + "notConfiguredAdmin": "هیچ برنامه اتصالی پیکربندی نشده است. آن‌ها را در تنظیمات برنامه‌ها تنظیم کنید.", + "goToApps": "رفتن به تنظیمات برنامه‌ها" }, "additionalOptions": { "title": "گزینه‌های اضافی", diff --git a/src/locales/ru.json b/src/locales/ru.json index bd2f537..e4bb5c1 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -377,7 +377,11 @@ "selectPlatform": "Выберите платформу", "app": "приложение", "apps": "приложений", - "tutorial": "Инструкция" + "tutorial": "Инструкция", + "notConfigured": "Способы подключения ещё не настроены", + "notConfiguredUser": "Настройка подключения в процессе. Пожалуйста, загляните позже.", + "notConfiguredAdmin": "Приложения для подключения не настроены. Настройте их в разделе Приложения.", + "goToApps": "Перейти к настройке приложений" }, "myDevices": "Мои устройства", "noDevices": "Нет подключенных устройств", diff --git a/src/locales/zh.json b/src/locales/zh.json index 3fc8ca5..61f417d 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -307,7 +307,11 @@ "selectPlatform": "选择平台", "app": "个应用", "apps": "个应用", - "tutorial": "教程" + "tutorial": "教程", + "notConfigured": "连接方式尚未配置", + "notConfiguredUser": "连接设置正在进行中,请稍后再来查看。", + "notConfiguredAdmin": "尚未配置连接应用。请在应用设置中进行配置。", + "goToApps": "前往应用设置" }, "additionalOptions": { "title": "附加选项", diff --git a/src/pages/Connection.tsx b/src/pages/Connection.tsx index a68d271..d862b16 100644 --- a/src/pages/Connection.tsx +++ b/src/pages/Connection.tsx @@ -1,5 +1,5 @@ -import { useEffect, useCallback, useRef } from 'react'; -import { useNavigate } from 'react-router'; +import { useEffect, useCallback, useMemo, useRef } from 'react'; +import { Link, useNavigate } from 'react-router'; import { useTranslation } from 'react-i18next'; import { useQuery } from '@tanstack/react-query'; import { openLink as sdkOpenLink } from '@telegram-apps/sdk-react'; @@ -8,13 +8,13 @@ import { useTelegramSDK } from '../hooks/useTelegramSDK'; import { useHaptic } from '@/platform'; import { resolveTemplate, hasTemplates } from '../utils/templateEngine'; import { useAuthStore } from '../store/auth'; -import type { AppConfig } from '../types'; +import type { AppConfig, RemnawavePlatformData } from '../types'; import InstallationGuide from '../components/connection/InstallationGuide'; export default function Connection() { const { t, i18n } = useTranslation(); const navigate = useNavigate(); - const { user } = useAuthStore(); + const { user, isAdmin } = useAuthStore(); const { isTelegramWebApp } = useTelegramSDK(); const { impact: hapticImpact } = useHaptic(); @@ -79,6 +79,14 @@ export default function Connection() { [isTelegramWebApp, i18n.language, resolveUrl], ); + // Check if any platform has configured apps + const hasApps = useMemo(() => { + if (!appConfig?.platforms) return false; + return Object.values(appConfig.platforms).some( + (p: RemnawavePlatformData) => p.apps && p.apps.length > 0, + ); + }, [appConfig?.platforms]); + // Loading if (isLoading) { return ( @@ -115,6 +123,60 @@ export default function Connection() { ); } + // No apps configured + if (!hasApps) { + return ( +
+
+ + + +
+

+ {t('subscription.connection.notConfigured')} +

+

+ {isAdmin + ? t('subscription.connection.notConfiguredAdmin') + : t('subscription.connection.notConfiguredUser')} +

+ {isAdmin && ( + + + + + + {t('subscription.connection.goToApps')} + + )} +
+ ); + } + return ( Date: Mon, 9 Feb 2026 11:40:06 +0300 Subject: [PATCH 4/7] perf: extract locales into separate chunk Move locale JSON files into a dedicated 'locales' chunk to reduce the main index bundle from ~565 KB to ~186 KB. --- vite.config.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/vite.config.ts b/vite.config.ts index 3c5012f..bb06fc4 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -36,6 +36,7 @@ export default defineConfig({ rollupOptions: { output: { manualChunks(id) { + if (id.includes('/src/locales/')) return 'locales'; if (!id.includes('node_modules')) return; if ( id.includes('react-dom') || @@ -44,13 +45,17 @@ export default defineConfig({ ) return 'vendor-react'; if (id.includes('@tanstack/react-query')) return 'vendor-query'; + if (id.includes('@tanstack/react-table')) return 'vendor-table'; if (id.includes('i18next') || id.includes('react-i18next')) return 'vendor-i18n'; if (id.includes('framer-motion')) return 'vendor-motion'; - if (id.includes('@radix-ui/')) return 'vendor-radix'; + if (id.includes('@radix-ui/') || id.includes('@floating-ui/')) return 'vendor-radix'; if (id.includes('@dnd-kit/')) return 'vendor-dnd'; - if (id.includes('@telegram-apps/')) return 'vendor-telegram'; + if (id.includes('@telegram-apps/') || id.includes('/@tma.js/')) return 'vendor-telegram'; if (id.includes('/ogl/')) return 'vendor-webgl'; if (id.includes('/cmdk/')) return 'vendor-cmdk'; + if (id.includes('twemoji') || id.includes('@twemoji/')) return 'vendor-twemoji'; + if (id.includes('/jsencrypt/') || id.includes('@kastov/')) return 'vendor-crypto'; + if (id.includes('@lottiefiles/')) return 'vendor-lottie'; if ( id.includes('/axios/') || id.includes('/zustand/') || From 772d83d1c97f2689376bcadbd7b3c37cf8cb797e Mon Sep 17 00:00:00 2001 From: c0mrade Date: Mon, 9 Feb 2026 11:45:19 +0300 Subject: [PATCH 5/7] fix: remove @floating-ui from radix chunk to resolve circular dependency --- vite.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vite.config.ts b/vite.config.ts index bb06fc4..ce18db2 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -48,7 +48,7 @@ export default defineConfig({ if (id.includes('@tanstack/react-table')) return 'vendor-table'; if (id.includes('i18next') || id.includes('react-i18next')) return 'vendor-i18n'; if (id.includes('framer-motion')) return 'vendor-motion'; - if (id.includes('@radix-ui/') || id.includes('@floating-ui/')) return 'vendor-radix'; + if (id.includes('@radix-ui/')) return 'vendor-radix'; if (id.includes('@dnd-kit/')) return 'vendor-dnd'; if (id.includes('@telegram-apps/') || id.includes('/@tma.js/')) return 'vendor-telegram'; if (id.includes('/ogl/')) return 'vendor-webgl'; From a4e6e35da1f86163fbdb0ba90fd28c8ccdef4ed6 Mon Sep 17 00:00:00 2001 From: c0mrade Date: Mon, 9 Feb 2026 12:29:00 +0300 Subject: [PATCH 6/7] fix: check apps before subscription on connection page Move the empty apps check before the subscription check so that an empty or unconfigured appConfig shows "not configured" instead of the misleading "no subscription" message. --- src/pages/Connection.tsx | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/pages/Connection.tsx b/src/pages/Connection.tsx index d862b16..e9fe6c9 100644 --- a/src/pages/Connection.tsx +++ b/src/pages/Connection.tsx @@ -108,22 +108,7 @@ export default function Connection() { ); } - // No subscription - if (!appConfig.hasSubscription) { - return ( -
-

- {t('subscription.connection.title')} -

-

{t('subscription.connection.noSubscription')}

- -
- ); - } - - // No apps configured + // No apps configured — check before subscription since empty config also has no subscription if (!hasApps) { return (
@@ -177,6 +162,21 @@ export default function Connection() { ); } + // No subscription + if (!appConfig.hasSubscription) { + return ( +
+

+ {t('subscription.connection.title')} +

+

{t('subscription.connection.noSubscription')}

+ +
+ ); + } + return ( Date: Mon, 9 Feb 2026 22:30:28 +0300 Subject: [PATCH 7/7] feat: show affected subscriptions count on tariff deletion Update delete mutation to use affected_subscriptions from API response. Show contextual success notification with subscription count. Add deleteSuccessWithSubscriptions translation key in all 4 locales. --- src/api/tariffs.ts | 4 +++- src/locales/en.json | 1 + src/locales/fa.json | 1 + src/locales/ru.json | 1 + src/locales/zh.json | 1 + src/pages/AdminTariffs.tsx | 10 ++++++++-- 6 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/api/tariffs.ts b/src/api/tariffs.ts index bd8ba11..5e4234f 100644 --- a/src/api/tariffs.ts +++ b/src/api/tariffs.ts @@ -208,7 +208,9 @@ export const tariffsApi = { }, // Delete tariff - deleteTariff: async (tariffId: number): Promise<{ message: string }> => { + deleteTariff: async ( + tariffId: number, + ): Promise<{ message: string; affected_subscriptions: number }> => { const response = await apiClient.delete(`/cabinet/admin/tariffs/${tariffId}`); return response.data; }, diff --git a/src/locales/en.json b/src/locales/en.json index 395d587..deb3be8 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1370,6 +1370,7 @@ "confirmDeleteText": "This action cannot be undone. The tariff will be permanently deleted.", "confirmDeleteWithSubscriptions": "This tariff has {{count}} active subscriptions. After deletion, users will need to select a new tariff when renewing.", "deleteSuccess": "Tariff deleted successfully", + "deleteSuccessWithSubscriptions": "Tariff deleted. {{count}} subscriptions reset — users will choose a new tariff on renewal.", "saveOrder": "Save order", "orderSaved": "Order saved", "dragToReorder": "Drag to reorder", diff --git a/src/locales/fa.json b/src/locales/fa.json index 5ffd5a5..de083f3 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -1076,6 +1076,7 @@ "confirmDeleteText": "این عمل قابل بازگشت نیست. تعرفه برای همیشه حذف می‌شود.", "confirmDeleteWithSubscriptions": "این تعرفه {{count}} اشتراک فعال دارد. پس از حذف، کاربران هنگام تمدید باید تعرفه جدید انتخاب کنند.", "deleteSuccess": "تعرفه با موفقیت حذف شد", + "deleteSuccessWithSubscriptions": "تعرفه حذف شد. تعرفه {{count}} اشتراک بازنشانی شد — کاربران هنگام تمدید تعرفه جدید انتخاب می‌کنند.", "saveOrder": "ذخیره ترتیب", "orderSaved": "ترتیب ذخیره شد", "dragToReorder": "برای تغییر ترتیب بکشید", diff --git a/src/locales/ru.json b/src/locales/ru.json index e4bb5c1..8108aa2 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -1888,6 +1888,7 @@ "confirmDeleteText": "Это действие нельзя отменить. Тариф будет удалён навсегда.", "confirmDeleteWithSubscriptions": "У этого тарифа {{count}} активных подписок. При удалении пользователям потребуется выбрать новый тариф при продлении.", "deleteSuccess": "Тариф успешно удален", + "deleteSuccessWithSubscriptions": "Тариф удален. У {{count}} подписок сброшен тариф — при продлении пользователи выберут новый.", "saveOrder": "Сохранить порядок", "orderSaved": "Порядок сохранён", "dragToReorder": "Перетащите для изменения порядка", diff --git a/src/locales/zh.json b/src/locales/zh.json index 61f417d..f99142b 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -1114,6 +1114,7 @@ "confirmDeleteText": "此操作不可撤销。套餐将被永久删除。", "confirmDeleteWithSubscriptions": "此套餐有 {{count}} 个活跃订阅。删除后,用户续订时需要选择新套餐。", "deleteSuccess": "套餐删除成功", + "deleteSuccessWithSubscriptions": "套餐已删除。{{count}} 个订阅已重置 — 用户续订时将选择新套餐。", "saveOrder": "保存排序", "orderSaved": "排序已保存", "dragToReorder": "拖动以重新排序", diff --git a/src/pages/AdminTariffs.tsx b/src/pages/AdminTariffs.tsx index ba93d31..c45831c 100644 --- a/src/pages/AdminTariffs.tsx +++ b/src/pages/AdminTariffs.tsx @@ -282,9 +282,15 @@ export default function AdminTariffs() { // Mutations const deleteMutation = useMutation({ mutationFn: tariffsApi.deleteTariff, - onSuccess: () => { + onSuccess: (data) => { queryClient.invalidateQueries({ queryKey: ['admin-tariffs'] }); - notify.success(t('admin.tariffs.deleteSuccess')); + if (data.affected_subscriptions > 0) { + notify.success( + t('admin.tariffs.deleteSuccessWithSubscriptions', { count: data.affected_subscriptions }), + ); + } else { + notify.success(t('admin.tariffs.deleteSuccess')); + } }, });