Add pull to refresh functionality in Layout component: integrate usePullToRefresh hook, implement visual indicator for refresh state, and ensure compatibility with mobile menu interactions for improved user experience.

This commit is contained in:
PEDZEO
2026-01-20 03:35:58 +03:00
parent 4c821a0f55
commit 2d8a0bd63a
2 changed files with 132 additions and 0 deletions

View File

@@ -15,6 +15,7 @@ import { themeColorsApi } from '../../api/themeColors'
import { promoApi } from '../../api/promo'
import { useTheme } from '../../hooks/useTheme'
import { useTelegramWebApp } from '../../hooks/useTelegramWebApp'
import { usePullToRefresh } from '../../hooks/usePullToRefresh'
// Fallback branding from environment variables
const FALLBACK_NAME = import.meta.env.VITE_APP_NAME || 'Cabinet'
@@ -132,6 +133,12 @@ export default function Layout({ children }: LayoutProps) {
const [userPhotoUrl, setUserPhotoUrl] = useState<string | null>(null)
const { isFullscreen, safeAreaInset, contentSafeAreaInset } = useTelegramWebApp()
// Pull to refresh (disabled when mobile menu is open)
const { isPulling, pullDistance, isRefreshing, progress } = usePullToRefresh({
disabled: mobileMenuOpen,
threshold: 80,
})
// Fetch enabled themes from API - same source of truth as AdminSettings
const { data: enabledThemes } = useQuery({
queryKey: ['enabled-themes'],
@@ -328,6 +335,30 @@ export default function Layout({ children }: LayoutProps) {
{/* Animated Background */}
<AnimatedBackground />
{/* Pull to refresh indicator */}
{(isPulling || isRefreshing) && (
<div
className="fixed left-1/2 -translate-x-1/2 z-[100] flex items-center justify-center transition-all duration-200"
style={{
top: `calc(${Math.max(pullDistance, isRefreshing ? 40 : 0)}px + env(safe-area-inset-top, 0px) + 0.5rem)`,
opacity: isRefreshing ? 1 : progress,
}}
>
<div className={`w-10 h-10 rounded-full bg-dark-800 border border-dark-700 shadow-lg flex items-center justify-center ${isRefreshing ? 'animate-pulse' : ''}`}>
<svg
className={`w-5 h-5 text-accent-400 transition-transform duration-200 ${isRefreshing ? 'animate-spin' : ''}`}
style={{ transform: isRefreshing ? undefined : `rotate(${progress * 360}deg)` }}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path strokeLinecap="round" strokeLinejoin="round" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
</div>
</div>
)}
{/* Header */}
<header
className="sticky top-0 z-50 glass border-b border-dark-800/50"