fix: adapt referral network for Telegram MiniApp safe areas

- Extract useHeaderHeight() hook — single source of truth for header
  height across AppShell, ReferralNetwork, and TicketNotificationBell
- Portal top offset now computed dynamically from TG safe area insets
  instead of hardcoded top-16 (fixes overlap with TG header bar)
- Two-row mobile layout: title + selector on separate rows so dropdown
  gets full width on small screens
- Add MOBILE_HEADER_HEIGHT_PX and DESKTOP_HEADER_HEIGHT_PX to constants
- Fix TicketNotificationBell using hardcoded 45px for all platforms
This commit is contained in:
Fringg
2026-03-20 04:23:45 +03:00
parent 2780898d1c
commit 33486a09d0
5 changed files with 47 additions and 26 deletions

View File

@@ -6,7 +6,7 @@ import { ticketNotificationsApi } from '../api/ticketNotifications';
import { useAuthStore } from '../store/auth';
import { useToast } from './Toast';
import { useWebSocket, WSMessage } from '../hooks/useWebSocket';
import { useTelegramSDK } from '../hooks/useTelegramSDK';
import { useHeaderHeight } from '../hooks/useHeaderHeight';
import type { TicketNotification } from '../types';
const BellIcon = () => (
@@ -37,12 +37,7 @@ export default function TicketNotificationBell({ isAdmin = false }: TicketNotifi
const { showToast } = useToast();
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const { isFullscreen, safeAreaInset, contentSafeAreaInset } = useTelegramSDK();
// Calculate dropdown top position (account for fullscreen safe area + TG buttons)
const dropdownTop = isFullscreen
? Math.max(safeAreaInset.top, contentSafeAreaInset.top) + 45 + 64 // safe area + TG buttons + header
: 64; // default header height
const { mobile: dropdownTop } = useHeaderHeight();
// Show toast for WebSocket notification
const showWSNotificationToast = useCallback(
@@ -259,10 +254,8 @@ export default function TicketNotificationBell({ isAdmin = false }: TicketNotifi
{/* Dropdown */}
{isOpen && (
<div
className={`fixed left-4 right-4 z-50 mt-0 w-auto animate-scale-in overflow-hidden rounded-2xl border border-dark-700/50 bg-dark-900/95 shadow-2xl shadow-black/30 backdrop-blur-xl sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:mt-2 sm:w-96 ${
!isFullscreen ? 'top-16' : ''
}`}
style={isFullscreen ? { top: `${dropdownTop}px` } : undefined}
className="fixed left-4 right-4 z-50 mt-0 w-auto animate-scale-in overflow-hidden rounded-2xl border border-dark-700/50 bg-dark-900/95 shadow-2xl shadow-black/30 backdrop-blur-xl sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:mt-2 sm:w-96"
style={{ top: dropdownTop }}
>
{/* Header */}
<div className="flex items-center justify-between border-b border-dark-700/50 bg-dark-800/30 px-4 py-3">

View File

@@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
import { useAuthStore } from '@/store/auth';
import { useHaptic } from '@/platform';
import { useTelegramSDK } from '@/hooks/useTelegramSDK';
import { useHeaderHeight } from '@/hooks/useHeaderHeight';
import { useTheme } from '@/hooks/useTheme';
import { useBranding } from '@/hooks/useBranding';
import { useFeatureFlags } from '@/hooks/useFeatureFlags';
@@ -13,7 +14,6 @@ import { useScrollRestoration } from '@/hooks/useScrollRestoration';
import { themeColorsApi } from '@/api/themeColors';
import { isLogoPreloaded } from '@/api/branding';
import { cn } from '@/lib/utils';
import { UI } from '@/config/constants';
import WebSocketNotifications from '@/components/WebSocketNotifications';
import CampaignBonusNotifier from '@/components/CampaignBonusNotifier';
@@ -198,6 +198,7 @@ export function AppShell({ children }: AppShellProps) {
const logout = useAuthStore((state) => state.logout);
const { isFullscreen, safeAreaInset, contentSafeAreaInset, platform, isMobile } =
useTelegramSDK();
const { mobile: headerHeight } = useHeaderHeight();
const haptic = useHaptic();
const { toggleTheme, isDark } = useTheme();
@@ -269,14 +270,7 @@ export function AppShell({ children }: AppShellProps) {
haptic.impact('light');
};
// Calculate header height based on fullscreen mode (only on mobile Telegram)
// On iOS: contentSafeAreaInset.top includes status bar + dynamic island + Telegram header
// On Android: safeAreaInset.top only includes status bar, need to add Telegram header height (~48px)
const telegramHeaderHeight =
platform === 'android' ? UI.TELEGRAM_HEADER_ANDROID_PX : UI.TELEGRAM_HEADER_IOS_PX;
const headerHeight = isMobileFullscreen
? 64 + Math.max(safeAreaInset.top, contentSafeAreaInset.top) + telegramHeaderHeight
: 64;
// headerHeight comes from useHeaderHeight() — accounts for TG safe area in fullscreen
return (
<div className="min-h-screen">