File size: 4,299 Bytes
6cf508e f50471e 6cf508e 77b8fea eaa9a6e 6cf508e 515abf6 6cf508e 515abf6 6cf508e 515abf6 6cf508e 515abf6 6cf508e 515abf6 6cf508e 515abf6 6cf508e eaa9a6e 6cf508e eaa9a6e 6cf508e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | 'use client';
import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { WEB_VERSION } from '@/lib/config';
// Create Context
const LoadingContext = createContext<{
loading: boolean;
setLoading: (state: boolean) => void;
} | null>(null);
// Custom Hook
export function useLoading() {
const context = useContext(LoadingContext);
if (!context) {
throw new Error('useLoading must be used within a LoadingProvider');
}
return context;
}
// Provider Component
export function LoadingProvider({ children }: { children: ReactNode }) {
const [loading, setLoading] = useState(false);
const [message, setMessage] = useState('Getting things ready for you');
useEffect(() => {
let interval: NodeJS.Timeout;
let dotCount = 0;
function loadingMessage() {
setMessage(`Getting things ready for you ${'.'.repeat(dotCount)}`);
dotCount = (dotCount + 1) % 4;
}
interval = setInterval(loadingMessage, 300);
return () => clearInterval(interval);
}, []);
return (
<LoadingContext.Provider value={{ loading, setLoading }}>
<AnimatePresence mode="wait">
{loading && (
<motion.div
initial={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.6, ease: 'easeInOut' }}
className="fixed inset-0 z-50 flex items-center justify-center bg-gray-900"
>
<div className="relative flex flex-col items-center">
{/* Logo Animation */}
<motion.div
initial={{ scale: 0.5, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ duration: 0.6, ease: 'easeOut' }}
className="mb-8"
>
<div className="w-44 h-44 relative ">
<div className="animate-pulse absolute inset-0 bg-gradient-to-r from-purple-600 to-pink-600 rounded-2xl" />
{/* Inner Background */}
<div className="animate-pulse absolute inset-1 bg-gray-800 rounded-xl" />
{/* Text */}
<div className="absolute inset-0 flex-col items-center text-center flex justify-items-center justify-center">
<span className="text-4xl font-bold bg-gradient-to-r from-purple-500 to-pink-500 bg-clip-text text-transparent drop-shadow-lg">
NEXORA
</span>
<p className="text-gray-300 font-mono">{WEB_VERSION}</p>
</div>
</div>
</motion.div>
{/* Loading Bar */}
<motion.div
initial={{ width: 0 }}
animate={{ width: '150px' }}
transition={{ duration: 1.5, repeat: Infinity, ease: 'easeInOut' }}
className="animate-pulse h-1 bg-gradient-to-r m from-purple-500 to-pink-500 rounded-full"
/>
{/* Loading Text */}
<motion.p
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.5, ease: 'easeOut' }}
className="mt-6 text-gray-300 text-sm font-mono tracking-wide"
>
{message}
</motion.p>
</div>
</motion.div>
)}
</AnimatePresence>
{children}
</LoadingContext.Provider>
);
}
|