Skip to content

Commit f3c19f5

Browse files
committed
Update
1 parent 6fd8578 commit f3c19f5

File tree

14 files changed

+1440
-230
lines changed

14 files changed

+1440
-230
lines changed

package-lock.json

Lines changed: 34 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
"lint": "next lint"
1010
},
1111
"dependencies": {
12-
"framer-motion": "^11.11.11",
12+
"@tailwindcss/line-clamp": "^0.4.4",
13+
"framer-motion": "^11.18.2",
1314
"next": "15.0.2",
1415
"next-intl": "^3.24.0",
1516
"next-themes": "^0.4.3",

src/app/[locale]/layout.tsx

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ import { Footer } from "../_client/components/layout/footer";
1010
import { ToastContainer } from "react-toastify";
1111
import 'react-toastify/dist/ReactToastify.css';
1212
import { ThemeProvider } from "next-themes";
13-
13+
import { PageTransitionProvider } from "../_client/components/layout/PageTransitionProvider";
1414

1515
export const metadata: Metadata = {
16-
title: "Create Next App",
17-
description: "Generated by create next app",
16+
title: "VDA - YouTube Video Downloader",
17+
description: "Download YouTube videos in various formats quickly and easily",
1818
};
1919

2020
type LayoutProps = {
@@ -34,20 +34,71 @@ const Layout = async({ children, params }: LayoutProps) => {
3434
const messages = await getMessages();
3535

3636
return (
37-
<html suppressHydrationWarning lang={locale}>
38-
<body className={`${ inter_sans } font-inter bg-body dark:bg-dark_body`}>
39-
<ThemeProvider attribute="class">
40-
<NextIntlClientProvider messages={messages}>
41-
<Header />
42-
{children}
43-
<ToastContainer autoClose={3000} />
44-
<Footer />
45-
</NextIntlClientProvider>
46-
</ThemeProvider>
47-
</body>
48-
</html>
37+
<html suppressHydrationWarning lang={locale}>
38+
<body
39+
className={`${inter_sans} font-inter bg-body dark:bg-dark_body transition-colors duration-300`}
40+
suppressHydrationWarning
41+
>
42+
<ThemeProvider
43+
attribute="class"
44+
defaultTheme="system"
45+
enableSystem
46+
disableTransitionOnChange={false}
47+
>
48+
<NextIntlClientProvider messages={messages}>
49+
<PageTransitionProvider>
50+
{/* Main App Structure */}
51+
<div className="min-h-screen flex flex-col">
52+
<Header />
53+
54+
{/* Main Content Area */}
55+
<main className="flex-1 relative">
56+
{children}
57+
</main>
58+
59+
<Footer />
60+
</div>
61+
62+
{/* Toast Notifications with Enhanced Styling */}
63+
<ToastContainer
64+
position="top-right"
65+
autoClose={3000}
66+
hideProgressBar={false}
67+
newestOnTop={false}
68+
closeOnClick
69+
rtl={false}
70+
pauseOnFocusLoss
71+
draggable
72+
pauseOnHover
73+
theme="colored"
74+
className="toast-container"
75+
toastClassName="toast-item"
76+
bodyClassName="toast-body"
77+
progressClassName="toast-progress"
78+
/>
79+
80+
{/* Global Loading Overlay for Large Transitions */}
81+
<div id="global-loading" className="hidden fixed inset-0 z-50 bg-body dark:bg-dark_body bg-opacity-80 backdrop-blur-sm">
82+
<div className="flex items-center justify-center min-h-screen">
83+
<div className="bg-white dark:bg-dark_heading rounded-2xl p-8 shadow-2xl max-w-sm w-full mx-4">
84+
<div className="text-center">
85+
<div className="w-16 h-16 mx-auto mb-4 border-4 border-purple_main border-t-transparent rounded-full animate-spin"></div>
86+
<h3 className="text-lg font-semibold text-heading_main dark:text-dark_heading_main mb-2">
87+
Loading Page
88+
</h3>
89+
<p className="text-base_one dark:text-dark_base_one text-sm">
90+
Please wait while we prepare your content...
91+
</p>
92+
</div>
93+
</div>
94+
</div>
95+
</div>
96+
</PageTransitionProvider>
97+
</NextIntlClientProvider>
98+
</ThemeProvider>
99+
</body>
100+
</html>
49101
);
50102
}
51103

52-
53104
export default Layout
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"use client"
2+
import { useEffect, useState } from 'react'
3+
4+
interface HydrationBoundaryProps {
5+
children: React.ReactNode
6+
fallback?: React.ReactNode
7+
}
8+
9+
export const HydrationBoundary = ({
10+
children,
11+
fallback = null
12+
}: HydrationBoundaryProps) => {
13+
const [hasMounted, setHasMounted] = useState(false)
14+
15+
useEffect(() => {
16+
setHasMounted(true)
17+
}, [])
18+
19+
// Prevent hydration mismatch by not rendering on server
20+
if (!hasMounted) {
21+
return <>{fallback}</>
22+
}
23+
24+
return <>{children}</>
25+
}
26+
27+
// Hook to check if component is mounted (client-side)
28+
export const useHasMounted = () => {
29+
const [hasMounted, setHasMounted] = useState(false)
30+
31+
useEffect(() => {
32+
setHasMounted(true)
33+
}, [])
34+
35+
return hasMounted
36+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
"use client"
2+
import { usePathname } from '@/i18n/routing'
3+
import { AnimatePresence, motion } from 'framer-motion'
4+
import { useEffect, useState } from 'react'
5+
import { useRouter } from '@/i18n/routing'
6+
7+
interface PageTransitionProviderProps {
8+
children: React.ReactNode
9+
}
10+
11+
export const PageTransitionProvider = ({ children }: PageTransitionProviderProps) => {
12+
const pathname = usePathname()
13+
const router = useRouter()
14+
const [isLoading, setIsLoading] = useState(false)
15+
const [hasMounted, setHasMounted] = useState(false)
16+
17+
// Handle hydration
18+
useEffect(() => {
19+
setHasMounted(true)
20+
}, [])
21+
22+
// Listen for route changes
23+
useEffect(() => {
24+
const handleStart = () => setIsLoading(true)
25+
const handleComplete = () => setIsLoading(false)
26+
27+
// Since we're using Next.js App Router, we'll handle this differently
28+
// We'll use a timeout to simulate the loading state
29+
if (isLoading) {
30+
const timer = setTimeout(() => {
31+
setIsLoading(false)
32+
}, 300)
33+
return () => clearTimeout(timer)
34+
}
35+
}, [pathname, isLoading])
36+
37+
// Don't render transitions on server-side
38+
if (!hasMounted) {
39+
return <div className="min-h-screen">{children}</div>
40+
}
41+
42+
const pageVariants = {
43+
initial: {
44+
opacity: 0,
45+
y: 20,
46+
scale: 0.98
47+
},
48+
in: {
49+
opacity: 1,
50+
y: 0,
51+
scale: 1
52+
},
53+
out: {
54+
opacity: 0,
55+
y: -20,
56+
scale: 0.98
57+
}
58+
}
59+
60+
const pageTransition = {
61+
type: "tween",
62+
ease: "anticipate",
63+
duration: 0.4
64+
}
65+
66+
const loadingVariants = {
67+
initial: { width: "0%" },
68+
animate: { width: "100%" },
69+
exit: { width: "100%" }
70+
}
71+
72+
return (
73+
<>
74+
{/* Loading Bar */}
75+
<AnimatePresence>
76+
{isLoading && (
77+
<motion.div
78+
className="fixed top-0 left-0 right-0 z-50 h-1 bg-gradient-to-r from-purple_main via-blue-500 to-purple_main"
79+
initial="initial"
80+
animate="animate"
81+
exit="exit"
82+
variants={loadingVariants}
83+
transition={{ duration: 0.3 }}
84+
/>
85+
)}
86+
</AnimatePresence>
87+
88+
{/* Page Content with Transitions */}
89+
<AnimatePresence mode="wait" initial={false}>
90+
<motion.div
91+
key={pathname}
92+
initial="initial"
93+
animate="in"
94+
exit="out"
95+
variants={pageVariants}
96+
transition={pageTransition}
97+
className="min-h-screen"
98+
>
99+
{children}
100+
</motion.div>
101+
</AnimatePresence>
102+
103+
{/* Loading Overlay */}
104+
<AnimatePresence>
105+
{isLoading && (
106+
<motion.div
107+
initial={{ opacity: 0 }}
108+
animate={{ opacity: 1 }}
109+
exit={{ opacity: 0 }}
110+
transition={{ duration: 0.2 }}
111+
className="fixed inset-0 z-40 bg-body dark:bg-dark_body bg-opacity-50 backdrop-blur-sm flex items-center justify-center"
112+
>
113+
<div className="bg-white dark:bg-dark_heading rounded-2xl p-8 shadow-2xl">
114+
<div className="flex items-center gap-4">
115+
<div className="w-8 h-8 border-4 border-purple_main border-t-transparent rounded-full animate-spin"></div>
116+
<span className="text-heading_main dark:text-dark_heading_main font-medium">
117+
Loading...
118+
</span>
119+
</div>
120+
</div>
121+
</motion.div>
122+
)}
123+
</AnimatePresence>
124+
</>
125+
)
126+
}

src/app/_client/components/layout/footer/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export const Footer = () =>{
7474
</div>
7575
<div className="lg:basis-full p-4">
7676
<div className="border-t-2 pt-16 border-t-gray-200 dark:border-t-gray-800">
77-
<p className="font-light text-sm text-base_one dark:text-dark_base_one">Copyright © 2022 All Rights Reseved.</p>
77+
<p className="font-light text-sm text-base_one dark:text-dark_base_one">Copyright © { new Date().getFullYear() } All Rights Reseved.</p>
7878
</div>
7979
</div>
8080
</footer>

0 commit comments

Comments
 (0)