Fix back button cycling in Telegram Mini App

Add replace: true to all navigate() calls in useBackButton hooks
Prevents history cycles when navigating back and forth
All 32 admin pages now use navigate(path, { replace: true })
Back button now goes directly to parent without cycling
This commit is contained in:
c0mrade
2026-02-04 05:59:15 +03:00
parent cb8326309c
commit 55e2068606
33 changed files with 35 additions and 33 deletions

View File

@@ -101,7 +101,9 @@ export const promoApi = {
// Deactivate current active promo discount
deactivateDiscount: async (): Promise<DeactivateDiscountResponse> => {
const response = await apiClient.post<DeactivateDiscountResponse>('/cabinet/promocode/deactivate-discount');
const response = await apiClient.post<DeactivateDiscountResponse>(
'/cabinet/promocode/deactivate-discount',
);
return response.data;
},
};

View File

@@ -590,7 +590,7 @@ export default function AdminApps() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin'));
useBackButton(() => navigate('/admin', { replace: true }));
const [selectedPlatform, setSelectedPlatform] = useState<string>('ios');
const [editingApp, setEditingApp] = useState<{ app: AppDefinition; isNew: boolean } | null>(null);

View File

@@ -119,7 +119,7 @@ export default function AdminBroadcastCreate() {
const queryClient = useQueryClient();
const fileInputRef = useRef<HTMLInputElement>(null);
useBackButton(() => navigate('/admin/broadcasts'));
useBackButton(() => navigate('/admin/broadcasts', { replace: true }));
// Channel selection
const [channel, setChannel] = useState<BroadcastChannel>('telegram');

View File

@@ -159,7 +159,7 @@ export default function AdminBroadcastDetail() {
const broadcastId = id ? parseInt(id, 10) : null;
useBackButton(() => navigate('/admin/broadcasts'));
useBackButton(() => navigate('/admin/broadcasts', { replace: true }));
// Fetch broadcast details
const {

View File

@@ -132,7 +132,7 @@ export default function AdminBroadcasts() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin'));
useBackButton(() => navigate('/admin', { replace: true }));
const [page, setPage] = useState(0);
const limit = 20;

View File

@@ -156,7 +156,7 @@ export default function AdminCampaignCreate() {
const navigate = useNavigate();
const queryClient = useQueryClient();
useBackButton(() => navigate('/admin/campaigns'));
useBackButton(() => navigate('/admin/campaigns', { replace: true }));
// Form state
const [name, setName] = useState('');

View File

@@ -133,7 +133,7 @@ export default function AdminCampaignEdit() {
const campaignId = parseInt(id || '0');
useBackButton(() => navigate('/admin/campaigns'));
useBackButton(() => navigate('/admin/campaigns', { replace: true }));
// Fetch campaign
const {

View File

@@ -98,7 +98,7 @@ export default function AdminCampaignStats() {
const [copied, setCopied] = useState(false);
const [showUsers, setShowUsers] = useState(false);
useBackButton(() => navigate('/admin/campaigns'));
useBackButton(() => navigate('/admin/campaigns', { replace: true }));
// Fetch stats
const {

View File

@@ -68,7 +68,7 @@ export default function AdminCampaigns() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin'));
useBackButton(() => navigate('/admin', { replace: true }));
const [deleteConfirm, setDeleteConfirm] = useState<number | null>(null);

View File

@@ -366,7 +366,7 @@ export default function AdminDashboard() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin'));
useBackButton(() => navigate('/admin', { replace: true }));
const [stats, setStats] = useState<DashboardStats | null>(null);
const [loading, setLoading] = useState(true);

View File

@@ -73,7 +73,7 @@ export default function AdminPaymentMethodEdit() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin/payment-methods'));
useBackButton(() => navigate('/admin/payment-methods', { replace: true }));
// Fetch payment methods
const { data: methods, isLoading } = useQuery({

View File

@@ -240,7 +240,7 @@ export default function AdminPaymentMethods() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin'));
useBackButton(() => navigate('/admin', { replace: true }));
const [methods, setMethods] = useState<PaymentMethodConfig[]>([]);
const [orderChanged, setOrderChanged] = useState(false);

View File

@@ -29,7 +29,7 @@ export default function AdminPayments() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin'));
useBackButton(() => navigate('/admin', { replace: true }));
const [page, setPage] = useState(1);
const [methodFilter, setMethodFilter] = useState<string>('');

View File

@@ -56,7 +56,7 @@ export default function AdminPromoGroupCreate() {
const queryClient = useQueryClient();
const isEdit = !!id;
useBackButton(() => navigate('/admin/promo-groups'));
useBackButton(() => navigate('/admin/promo-groups', { replace: true }));
// Form state
const [name, setName] = useState('');

View File

@@ -62,7 +62,7 @@ export default function AdminPromoGroups() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin'));
useBackButton(() => navigate('/admin', { replace: true }));
const [deleteConfirm, setDeleteConfirm] = useState<number | null>(null);

View File

@@ -76,7 +76,7 @@ export default function AdminPromoOfferSend() {
const navigate = useNavigate();
const queryClient = useQueryClient();
useBackButton(() => navigate('/admin/promo-offers'));
useBackButton(() => navigate('/admin/promo-offers', { replace: true }));
const [selectedTemplateId, setSelectedTemplateId] = useState<number | null>(null);
const [sendMode, setSendMode] = useState<'segment' | 'user'>('segment');

View File

@@ -22,7 +22,7 @@ export default function AdminPromoOfferTemplateEdit() {
const { id } = useParams<{ id: string }>();
const queryClient = useQueryClient();
useBackButton(() => navigate('/admin/promo-offers'));
useBackButton(() => navigate('/admin/promo-offers', { replace: true }));
// Form state
const [name, setName] = useState('');

View File

@@ -109,7 +109,7 @@ export default function AdminPromoOffers() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin'));
useBackButton(() => navigate('/admin', { replace: true }));
const [activeTab, setActiveTab] = useState<'templates' | 'logs'>('templates');

View File

@@ -51,7 +51,7 @@ export default function AdminPromocodeCreate() {
const isEdit = !!id;
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin/promocodes'));
useBackButton(() => navigate('/admin/promocodes', { replace: true }));
// Form state
const [code, setCode] = useState('');

View File

@@ -89,7 +89,7 @@ export default function AdminPromocodeStats() {
const navigate = useNavigate();
const { id } = useParams<{ id: string }>();
useBackButton(() => navigate('/admin/promocodes'));
useBackButton(() => navigate('/admin/promocodes', { replace: true }));
const {
data: promocode,

View File

@@ -114,7 +114,7 @@ export default function AdminPromocodes() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin'));
useBackButton(() => navigate('/admin', { replace: true }));
const [deleteConfirm, setDeleteConfirm] = useState<number | null>(null);
const [copiedCode, setCopiedCode] = useState<string | null>(null);

View File

@@ -879,7 +879,7 @@ export default function AdminRemnawave() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin'));
useBackButton(() => navigate('/admin', { replace: true }));
// State
const [activeTab, setActiveTab] = useState<TabType>('overview');

View File

@@ -55,7 +55,7 @@ export default function AdminRemnawaveSquadDetail() {
const { uuid } = useParams<{ uuid: string }>();
const navigate = useNavigate();
useBackButton(() => navigate('/admin/remnawave'));
useBackButton(() => navigate('/admin/remnawave', { replace: true }));
// Fetch all squads and find the one we need
const {

View File

@@ -49,7 +49,7 @@ export default function AdminServerEdit() {
const serverId = parseInt(id || '0');
useBackButton(() => navigate('/admin/servers'));
useBackButton(() => navigate('/admin/servers', { replace: true }));
const {
data: server,

View File

@@ -59,7 +59,7 @@ export default function AdminServers() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin'));
useBackButton(() => navigate('/admin', { replace: true }));
// Queries
const { data: serversData, isLoading } = useQuery({

View File

@@ -51,7 +51,7 @@ export default function AdminSettings() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin'));
useBackButton(() => navigate('/admin', { replace: true }));
// State
const [activeSection, setActiveSection] = useState('branding');

View File

@@ -92,7 +92,7 @@ export default function AdminTariffCreate() {
const queryClient = useQueryClient();
const isEdit = !!id;
useBackButton(() => navigate('/admin/tariffs'));
useBackButton(() => navigate('/admin/tariffs', { replace: true }));
// Step: null = type selection, 'period' or 'daily' = form
const [tariffType, setTariffType] = useState<TariffType>(null);

View File

@@ -77,7 +77,7 @@ export default function AdminTariffs() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin'));
useBackButton(() => navigate('/admin', { replace: true }));
// Queries
const { data: tariffsData, isLoading } = useQuery({

View File

@@ -22,7 +22,7 @@ export default function AdminTicketSettings() {
const navigate = useNavigate();
const queryClient = useQueryClient();
useBackButton(() => navigate('/admin/tickets'));
useBackButton(() => navigate('/admin/tickets', { replace: true }));
const {
data: settings,

View File

@@ -124,7 +124,7 @@ export default function AdminTickets() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin'));
useBackButton(() => navigate('/admin', { replace: true }));
const [selectedTicketId, setSelectedTicketId] = useState<number | null>(null);
const [statusFilter, setStatusFilter] = useState<string>('');

View File

@@ -82,7 +82,7 @@ export default function AdminUserDetail() {
const navigate = useNavigate();
const { id } = useParams<{ id: string }>();
useBackButton(() => navigate('/admin/users'));
useBackButton(() => navigate('/admin/users', { replace: true }));
const localeMap: Record<string, string> = { ru: 'ru-RU', en: 'en-US', zh: 'zh-CN', fa: 'fa-IR' };
const locale = localeMap[i18n.language] || 'ru-RU';

View File

@@ -399,7 +399,7 @@ export default function AdminUsers() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin'));
useBackButton(() => navigate('/admin', { replace: true }));
const [users, setUsers] = useState<UserListItem[]>([]);
const [stats, setStats] = useState<UsersStatsResponse | null>(null);

View File

@@ -292,7 +292,7 @@ export default function AdminWheel() {
const { capabilities } = usePlatform();
// Use native Telegram back button in Mini App
useBackButton(() => navigate('/admin'));
useBackButton(() => navigate('/admin', { replace: true }));
const [activeTab, setActiveTab] = useState<Tab>('settings');
const [expandedPrizeId, setExpandedPrizeId] = useState<number | null>(null);