Skip to content

Commit eae971c

Browse files
committed
fix: mobile ui refinements, z-index layering, and video thumbnail reliability
1 parent 859a3f0 commit eae971c

9 files changed

Lines changed: 156 additions & 20 deletions

File tree

.DS_Store

0 Bytes
Binary file not shown.

web/src/app/(main)/live/page.tsx

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import { Radio } from 'lucide-react';
77
import { useAudio } from '@/context/AudioContext';
88
import { useRouter } from 'next/navigation';
99
import { Modal } from '@/components/common/Modal';
10+
import { VideoModal } from '@/components/common/VideoModal';
1011

1112
export default function LivePage() {
1213
const { isPlaying, closePlayer } = useAudio();
1314
const router = useRouter();
1415
const [showAudioConfirm, setShowAudioConfirm] = React.useState(false);
16+
const [selectedVideo, setSelectedVideo] = React.useState<{ id: string; title: string } | null>(null);
1517
// Use a ref to track if we've already checked on mount
1618
const hasCheckedAudio = useRef(false);
1719

@@ -90,11 +92,26 @@ export default function LivePage() {
9092

9193
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
9294
{liveData.past_streams.map((video) => (
93-
<div key={video.id} className="bg-white rounded-lg shadow-sm border border-gray-100 overflow-hidden hover:shadow-md transition group">
94-
<div className="aspect-video bg-gray-100 relative">
95-
{/* Placeholder for actual thumbnail logic */}
96-
<div className="absolute inset-0 flex items-center justify-center text-gray-400">
97-
<VideoPlayer videoId={video.youtube_id} />
95+
<div
96+
key={video.id}
97+
onClick={() => setSelectedVideo({ id: video.youtube_id, title: video.title })}
98+
className="bg-white rounded-lg shadow-sm border border-gray-100 overflow-hidden hover:shadow-md transition group cursor-pointer"
99+
>
100+
<div className="aspect-video bg-gray-100 relative overflow-hidden">
101+
{/* YouTube Thumbnail */}
102+
<img
103+
src={`https://img.youtube.com/vi/${video.youtube_id}/hqdefault.jpg`}
104+
alt={video.title}
105+
className="absolute inset-0 w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
106+
loading="lazy"
107+
/>
108+
{/* Play Button Overlay */}
109+
<div className="absolute inset-0 flex items-center justify-center bg-black/20 group-hover:bg-black/30 transition">
110+
<div className="w-16 h-16 bg-red-600 rounded-full flex items-center justify-center shadow-lg group-hover:scale-110 transition">
111+
<svg className="w-8 h-8 text-white ml-1" fill="currentColor" viewBox="0 0 24 24">
112+
<path d="M8 5v14l11-7z" />
113+
</svg>
114+
</div>
98115
</div>
99116
</div>
100117
<div className="p-4">
@@ -131,6 +148,16 @@ export default function LivePage() {
131148
>
132149
Music is currently playing. Would you like to stop it to watch the live stream?
133150
</Modal>
151+
152+
{/* Video Modal for Past Streams */}
153+
{selectedVideo && (
154+
<VideoModal
155+
isOpen={!!selectedVideo}
156+
onClose={() => setSelectedVideo(null)}
157+
videoId={selectedVideo.id}
158+
title={selectedVideo.title}
159+
/>
160+
)}
134161
</div>
135162
);
136163
}

web/src/app/globals.css

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,34 @@ body {
7373
transform: translateY(0);
7474
}
7575
}
76+
77+
.animate-fadeIn {
78+
animation: fadeIn 0.2s ease-out;
79+
}
80+
81+
.animate-scaleIn {
82+
animation: scaleIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
83+
}
84+
85+
@keyframes fadeIn {
86+
from {
87+
opacity: 0;
88+
}
89+
90+
to {
91+
opacity: 1;
92+
}
93+
}
94+
95+
@keyframes scaleIn {
96+
from {
97+
opacity: 0;
98+
transform: scale(0.95);
99+
}
100+
101+
to {
102+
opacity: 1;
103+
transform: scale(1);
104+
}
105+
}
76106
}

web/src/components/common/Modal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export function Modal({ isOpen, onClose, title, children, actions, flush = false
3333
if (!mounted || !isOpen) return null;
3434

3535
return createPortal(
36-
<div className="fixed inset-0 z-[110] flex items-center justify-center p-4">
36+
<div className="fixed inset-0 z-[1000] flex items-center justify-center p-4">
3737
<div
3838
className="absolute inset-0 bg-black/60 backdrop-blur-[2px] transition-opacity"
3939
onClick={onClose}

web/src/components/common/Notification.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export function Notification({ message, type, onClose, duration = 5000 }: Notifi
3434

3535
return (
3636
<div
37-
className={`fixed bottom-24 md:bottom-8 left-1/2 -translate-x-1/2 z-[100] transition-all duration-500 transform
37+
className={`fixed bottom-24 md:bottom-8 left-1/2 -translate-x-1/2 z-[1100] transition-all duration-500 transform
3838
${isVisible ? 'translate-y-0 opacity-100' : 'translate-y-12 opacity-0'}`}
3939
>
4040
<div className={`${bgColor} text-white px-5 py-3 rounded-2xl shadow-xl flex items-center space-x-3 min-w-[300px] max-w-[90vw] border border-white/20 backdrop-blur-md`}>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
'use client';
2+
3+
import { useEffect } from 'react';
4+
import { X } from 'lucide-react';
5+
6+
interface VideoModalProps {
7+
isOpen: boolean;
8+
onClose: () => void;
9+
videoId: string;
10+
title: string;
11+
}
12+
13+
export function VideoModal({ isOpen, onClose, videoId, title }: VideoModalProps) {
14+
// Prevent body scroll when modal is open
15+
useEffect(() => {
16+
if (isOpen) {
17+
document.body.style.overflow = 'hidden';
18+
} else {
19+
document.body.style.overflow = 'unset';
20+
}
21+
return () => {
22+
document.body.style.overflow = 'unset';
23+
};
24+
}, [isOpen]);
25+
26+
// Close on escape key
27+
useEffect(() => {
28+
const handleEscape = (e: KeyboardEvent) => {
29+
if (e.key === 'Escape') {
30+
onClose();
31+
}
32+
};
33+
34+
if (isOpen) {
35+
window.addEventListener('keydown', handleEscape);
36+
}
37+
38+
return () => {
39+
window.removeEventListener('keydown', handleEscape);
40+
};
41+
}, [isOpen, onClose]);
42+
43+
if (!isOpen) return null;
44+
45+
return (
46+
<div
47+
className="fixed inset-0 z-[1000] flex items-center justify-center bg-black/90 p-4 animate-fadeIn"
48+
onClick={onClose}
49+
>
50+
<div
51+
className="relative w-full max-w-5xl bg-black rounded-lg overflow-hidden shadow-2xl animate-scaleIn"
52+
onClick={(e) => e.stopPropagation()}
53+
>
54+
{/* Close Button */}
55+
<button
56+
onClick={onClose}
57+
className="absolute top-4 right-4 z-10 w-10 h-10 bg-black/50 hover:bg-black/70 rounded-full flex items-center justify-center text-white transition backdrop-blur-sm"
58+
aria-label="Close video"
59+
>
60+
<X className="w-6 h-6" />
61+
</button>
62+
63+
{/* Video Title */}
64+
<div className="absolute top-4 left-4 z-10 bg-black/50 backdrop-blur-sm px-4 py-2 rounded-lg max-w-[calc(100%-8rem)]">
65+
<h3 className="text-white font-bold text-sm md:text-base line-clamp-2">{title}</h3>
66+
</div>
67+
68+
{/* Video Player */}
69+
<div className="relative w-full pb-[56.25%] bg-black">
70+
<iframe
71+
className="absolute top-0 left-0 w-full h-full"
72+
src={`https://www.youtube.com/embed/${videoId}?autoplay=1&rel=0`}
73+
title={title}
74+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
75+
allowFullScreen
76+
/>
77+
</div>
78+
</div>
79+
</div>
80+
);
81+
}

web/src/components/features/ReferenceVideos.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ export default function ReferenceVideos({ videos }: ReferenceVideosProps) {
3535
>
3636
<div className="aspect-video bg-gray-100 rounded-lg overflow-hidden relative shadow-sm border border-gray-200">
3737
<img
38-
src={`https://img.youtube.com/vi/${video.youtube_id}/mqdefault.jpg`}
38+
src={`https://img.youtube.com/vi/${video.youtube_id}/hqdefault.jpg`}
3939
alt="Video thumbnail"
40-
className="w-full h-full object-cover"
40+
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
41+
loading="lazy"
4142
/>
4243
<div className="absolute inset-0 flex items-center justify-center bg-black/20 group-hover:bg-black/40 transition">
4344
<PlayCircle className="w-8 h-8 text-white/90" />

web/src/components/layout/BottomNav.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ export default function BottomNav() {
1818
const { unreadCount } = useInquiry();
1919

2020
return (
21-
<nav className="fixed bottom-0 left-0 right-0 bg-white/80 backdrop-blur-xl border-t border-gray-100 z-[120] md:hidden">
22-
<div className="flex justify-around items-center h-16 pb-safe">
21+
<nav className="fixed bottom-0 left-0 right-0 bg-white/95 backdrop-blur-xl border-t border-gray-100 z-[120] md:hidden shadow-[0_-4px_12px_-4px_rgba(0,0,0,0.05)]">
22+
<div className="flex justify-around items-center h-16 box-content pb-safe">
2323
{NAV_ITEMS.map((item) => {
2424
const Icon = item.icon;
2525
const isActive = pathname === item.href || pathname.startsWith(item.href);
@@ -30,12 +30,12 @@ export default function BottomNav() {
3030
key={item.href}
3131
href={item.href}
3232
prefetch={false}
33-
className={`flex flex-col items-center justify-center w-full h-full space-y-1 relative transition-all select-none active:bg-gray-100/50 ${isActive ? 'text-ochre' : 'text-gray-400'
33+
className={`flex flex-col items-center justify-center w-full h-full space-y-1 relative transition-all select-none active:scale-95 ${isActive ? 'text-ochre' : 'text-gray-400'
3434
}`}
3535
style={{ touchAction: 'manipulation' }}
3636
>
3737
{isActive && (
38-
<div className="absolute inset-x-2 inset-y-1 bg-ochre/5 rounded-xl animate-in fade-in zoom-in duration-300" />
38+
<div className="absolute left-1/2 -translate-x-1/2 w-[90%] max-w-[72px] inset-y-1.5 bg-ochre/10 rounded-2xl animate-in fade-in zoom-in duration-300" />
3939
)}
4040
<div className="relative pointer-events-none z-10">
4141
<Icon className={`w-5 h-5 transition-transform ${isActive ? 'scale-110 drop-shadow-sm' : 'scale-100'}`} />
@@ -45,7 +45,7 @@ export default function BottomNav() {
4545
</span>
4646
)}
4747
</div>
48-
<span className="text-[10px] font-black uppercase tracking-tighter pointer-events-none z-10">{item.label}</span>
48+
<span className="text-[10px] font-black uppercase tracking-tight pointer-events-none z-10">{item.label}</span>
4949
</Link>
5050
);
5151
})}

web/src/data/live_videos.json

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,19 @@
1212
"id": "past-1",
1313
"title": "|| 🅻🅸🆅🅴🔴3 || श्री साई सत्संग भजन संध्या || गीता भवन मंदिर || सोनीपत",
1414
"date": "Streamed 2 weeks ago",
15-
"youtube_id": "9fLqsJFpaqI",
16-
"thumbnail": ""
15+
"youtube_id": "9fLqsJFpaqI"
1716
},
1817
{
1918
"id": "past-2",
2019
"title": "|| 🅻🅸🆅🅴🔴2 || श्री साई सत्संग भजन संध्या || गीता भवन मंदिर || सोनीपत",
2120
"date": "Streamed 2 weeks ago",
22-
"youtube_id": "AQTYzr5iea4",
23-
"thumbnail": ""
21+
"youtube_id": "AQTYzr5iea4"
2422
},
2523
{
2624
"id": "past-3",
2725
"title": "|| 🅻🅸🆅🅴🔴 || श्री साई सत्संग भजन संध्या एवं भव्य पालकी शोभा यात्रा || गीता भवन मंदिर || सोनीपत",
2826
"date": "Streamed 2 weeks ago",
29-
"youtube_id": "-Ha-vIpRJlE",
30-
"thumbnail": ""
27+
"youtube_id": "-Ha-vIpRJlE"
3128
}
3229
]
3330
}

0 commit comments

Comments
 (0)