Skip to content

Commit 5bbab24

Browse files
Merge pull request #91 from priyansh-narang2308/feat/video-learning
[WOC] Add Video links with in-app player and smart search
2 parents e256537 + ca8b7f6 commit 5bbab24

5 files changed

Lines changed: 251 additions & 1 deletion

File tree

app/_components/TheoryTooltip.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export default function TheoryTooltip({ id }) {
2424
</button>
2525

2626
{isVisible && (
27-
<div className="absolute z-50 bottom-full left-1/2 -translate-x-1/2 mb-3 w-72 p-4 rounded-2xl bg-white/90 backdrop-blur-xl border border-white/20 shadow-[0_8px_32px_0_rgba(31,38,135,0.15)] transition-all duration-200 ease-out transform opacity-100 scale-100">
27+
<div className="absolute z-[100] bottom-full left-1/2 -translate-x-1/2 mb-3 w-72 p-4 rounded-2xl bg-white/90 backdrop-blur-xl border border-white/20 shadow-[0_8px_32px_0_rgba(31,38,135,0.15)] transition-all duration-200 ease-out transform opacity-100 scale-100">
2828
<div className="relative">
2929
<h5 className="font-bold text-gray-900 text-sm mb-1 uppercase tracking-wider">
3030
{data.title}

app/_components/VideoAcademy.js

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import { usePathname } from "next/navigation";
5+
import { Video, X, Youtube, Play, WifiOff, AlertCircle, Loader2 } from "lucide-react";
6+
import { videoLibrary } from "@/app/_data/videoLibrary";
7+
import { useEffect } from "react";
8+
9+
const VideoAcademy = () => {
10+
const [isOpen, setIsOpen] = useState(false);
11+
const [isOnline, setIsOnline] = useState(true);
12+
const [isChecking, setIsChecking] = useState(false);
13+
const [connectionError, setConnectionError] = useState(false);
14+
const pathname = usePathname();
15+
16+
useEffect(() => {
17+
const handleStatusChange = () => {
18+
setIsOnline(navigator.onLine);
19+
if (navigator.onLine) setConnectionError(false);
20+
};
21+
22+
window.addEventListener("online", handleStatusChange);
23+
window.addEventListener("offline", handleStatusChange);
24+
25+
setIsOnline(navigator.onLine);
26+
27+
return () => {
28+
window.removeEventListener("online", handleStatusChange);
29+
window.removeEventListener("offline", handleStatusChange);
30+
};
31+
}, []);
32+
33+
const verifyConnectivity = async () => {
34+
setIsChecking(true);
35+
setConnectionError(false);
36+
37+
// Even if navigator.onLine is true, we verify if YouTube is reachable
38+
try {
39+
const controller = new AbortController();
40+
const timeoutId = setTimeout(() => controller.abort(), 5000);
41+
42+
await fetch("https://www.youtube.com/favicon.ico", {
43+
mode: "no-cors",
44+
signal: controller.signal,
45+
cache: 'no-store'
46+
});
47+
48+
clearTimeout(timeoutId);
49+
setIsOpen(true);
50+
} catch (error) {
51+
setConnectionError(true);
52+
setIsOpen(true); // Open anyway to show the error message
53+
} finally {
54+
setIsChecking(false);
55+
}
56+
};
57+
58+
if (pathname === "/" || pathname.startsWith("/auth")) return null;
59+
60+
const getCategory = () => {
61+
if (pathname.includes("/create/gp")) return "gp";
62+
if (pathname.includes("/create/pso")) return "pso";
63+
if (pathname.includes("/create/ml")) return "ml";
64+
if (pathname.includes("/create/non-gp")) return "ea";
65+
return null;
66+
};
67+
68+
const currentCategory = getCategory();
69+
const videoData = currentCategory ? videoLibrary[currentCategory] : videoLibrary.common;
70+
71+
return (
72+
<>
73+
<button
74+
onClick={verifyConnectivity}
75+
disabled={isChecking}
76+
className="fixed top-3 right-3 md:top-6 md:right-6 z-50 flex items-center gap-2 bg-black hover:bg-gray-800 text-white font-bold py-2.5 px-5 rounded-full shadow-xl transition-all duration-300 transform hover:scale-105 active:scale-95 border border-white/20 disabled:opacity-70 disabled:cursor-not-allowed"
77+
id="video-academy-button"
78+
>
79+
{isChecking ? (
80+
<Loader2 size={18} className="animate-spin text-yellow-400" />
81+
) : (
82+
<Video size={18} />
83+
)}
84+
<span className="hidden md:inline text-sm">
85+
{isChecking ? "Verifying..." : "Video Academy"}
86+
</span>
87+
</button>
88+
89+
{isOpen && (
90+
<div
91+
className="fixed inset-0 bg-black/60 backdrop-blur-md z-[60] transition-opacity duration-300"
92+
onClick={() => setIsOpen(false)}
93+
/>
94+
)}
95+
96+
<div
97+
className={`fixed top-0 right-0 h-full w-full sm:w-[450px] bg-white z-[70] shadow-2xl transform transition-transform duration-500 ease-in-out flex flex-col ${isOpen ? "translate-x-0" : "translate-x-full"}`}
98+
>
99+
<div className="p-6 border-b flex items-center justify-between bg-black text-white">
100+
<div className="flex items-center gap-3">
101+
<div className="bg-yellow-400 p-1.5 rounded-lg text-black">
102+
<Video size={22} fill="currentColor" />
103+
</div>
104+
<h2 className="text-xl font-bold font-[family-name:var(--font-geist-mono)] tracking-tight text-white">
105+
<span className="text-yellow-400">EVOLVE</span> Academy
106+
</h2>
107+
</div>
108+
<button
109+
onClick={() => setIsOpen(false)}
110+
className="p-2 hover:bg-white/10 rounded-full transition-colors"
111+
>
112+
<X size={24} />
113+
</button>
114+
</div>
115+
116+
<div className="flex-1 overflow-y-auto p-6 font-[family-name:var(--font-geist-sans)]">
117+
118+
<div className="mb-8">
119+
<h3 className="font-bold mb-4 flex items-center gap-2 text-gray-800 uppercase tracking-widest text-xs">
120+
<span className="w-1.5 h-1.5 bg-yellow-400 rounded-full"></span>
121+
Recommended Intro
122+
</h3>
123+
124+
125+
<div className="rounded-xl overflow-hidden shadow-xl border border-gray-200 bg-black">
126+
{!isOnline || connectionError ? (
127+
<div className="aspect-video w-full bg-gray-900 flex flex-col items-center justify-center p-6 text-center">
128+
<WifiOff size={48} className="text-gray-600 mb-4" />
129+
<h4 className="text-white font-bold mb-2">No Internet Access</h4>
130+
<p className="text-gray-400 text-xs">
131+
Video tutorials require an active internet connection to load from YouTube.
132+
</p>
133+
<button
134+
onClick={verifyConnectivity}
135+
className="mt-4 text-yellow-400 text-xs font-bold hover:underline flex items-center gap-1"
136+
>
137+
<AlertCircle size={14} /> Retry connection
138+
</button>
139+
</div>
140+
) : (
141+
<div className="aspect-video w-full">
142+
<iframe
143+
className="w-full h-full"
144+
src={`https://www.youtube.com/embed/${videoData.featured.id}`}
145+
title={videoData.featured.title}
146+
frameBorder="0"
147+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
148+
allowFullScreen
149+
></iframe>
150+
</div>
151+
)}
152+
<div className="p-4 bg-white">
153+
<h4 className="font-bold text-gray-900 text-sm mb-1">{videoData.featured.title}</h4>
154+
<p className="text-xs text-gray-500 mb-2">{videoData.featured.channel}</p>
155+
<p className="text-xs text-gray-600 leading-relaxed">{videoData.featured.description}</p>
156+
</div>
157+
</div>
158+
</div>
159+
160+
<div className="mb-8">
161+
{(!isOnline || connectionError) ? (
162+
<div className="p-6 bg-red-50 rounded-xl border border-red-100 text-center">
163+
<div className="flex justify-center mb-3">
164+
<AlertCircle className="text-red-500" size={24} />
165+
</div>
166+
<h4 className="font-bold text-gray-900 mb-2">Offline Mode</h4>
167+
<p className="text-sm text-gray-600">
168+
It seems you're offline or YouTube is unreachable. Please check your connection to explore more tutorials.
169+
</p>
170+
</div>
171+
) : (
172+
<div className="p-6 bg-gray-50 rounded-xl border border-gray-200 text-center">
173+
<h4 className="font-bold text-gray-900 mb-2">Want to learn more?</h4>
174+
<p className="text-sm text-gray-600 mb-4">
175+
Explore more tutorials and advanced concepts on YouTube.
176+
</p>
177+
<a
178+
href={`https://www.youtube.com/results?search_query=${encodeURIComponent(videoData.searchQuery)}`}
179+
target="_blank"
180+
rel="noopener noreferrer"
181+
className="inline-flex items-center gap-2 bg-[#FF0000] text-white hover:bg-[#D90000] px-6 py-3 rounded-full font-bold text-sm transition-all shadow-lg hover:shadow-xl transform hover:-translate-y-0.5"
182+
>
183+
<Youtube size={18} />
184+
Search on YouTube
185+
</a>
186+
</div>
187+
)}
188+
</div>
189+
190+
</div>
191+
</div>
192+
</>
193+
);
194+
};
195+
196+
export default VideoAcademy;

app/_data/theory.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ export const theoryData = {
6969
explanation:
7070
"Mathematical functions used to evaluate optimization performance. Each function (like Ackley, Rastrigin, or Rosenbrock) represents a unique 'landscape' with different challenges for the algorithm to solve.",
7171
},
72+
psoBenchmark: {
73+
title: "Evaluation (Benchmark) Function",
74+
explanation:
75+
"The mathematical landscape the particles are searching. Spheres are simple and unimodal, while Ackley or Rastrigin functions are complex and multimodal, testing the swarm's ability to escape local optima.",
76+
},
7277
weights: {
7378
title: "Weights",
7479
explanation:

app/_data/videoLibrary.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
export const videoLibrary = {
2+
common: {
3+
featured: {
4+
title: "Evolutionary Algorithms",
5+
id: "L--IxUH4fac",
6+
channel: "Dr. Shahin Rostami",
7+
description: "A foundational look at how simulated evolution solves complex optimization problems.",
8+
},
9+
searchQuery: "evolutionary algorithms evolutionary computation explained"
10+
},
11+
ea: {
12+
featured: {
13+
title: "Genetic Algorithm Steps",
14+
id: "uQj5UNhCPuo",
15+
channel: "Kie Codes",
16+
description: "A step-by-step breakdown of population, selection, and reproduction.",
17+
},
18+
searchQuery: "evolutionary algorithms tutorial python"
19+
},
20+
gp: {
21+
featured: {
22+
title: "Intro to Genetic Programming",
23+
id: "tTMpKrKkYXo",
24+
channel: "John Koza",
25+
description: "The pioneer of GP explains how to evolve computer programs.",
26+
},
27+
searchQuery: "genetic programming tutorial explanation"
28+
},
29+
pso: {
30+
featured: {
31+
title: "Particle Swarm Optimization",
32+
id: "vhSBqk6SAB4",
33+
channel: "Premature Abstraction",
34+
description: "An intuitive guide to the velocity and position update rules of swarms.",
35+
},
36+
searchQuery: "particle swarm optimization explained visualization"
37+
},
38+
ml: {
39+
featured: {
40+
title: "Evolutionary Algorithms for Machine Learning",
41+
id: "nXjVZ-fYXys",
42+
channel: "Data Science in your pocket",
43+
description: "Learn how EA can fine-tune datasets and models for better performance.",
44+
},
45+
searchQuery: "evolutionary algorithms for machine learning feature selection hyperparameter tuning"
46+
}
47+
};

app/layout.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import localFont from "next/font/local";
22
import "./globals.css";
33
import { PublicEnvScript } from "next-runtime-env";
4+
import VideoAcademy from "./_components/VideoAcademy";
45
import ClientLayout from "./components/ClientLayout";
56
import HelpDocsButton from "./_components/HelpDocsButton";
67

@@ -54,6 +55,7 @@ export default function RootLayout({ children }) {
5455
<body
5556
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
5657
>
58+
<VideoAcademy />
5759
<ClientLayout>
5860
{children}
5961
<HelpDocsButton />

0 commit comments

Comments
 (0)