11import React , { useEffect , useState , useMemo } from 'react' ;
2- import { ArrowRight , Upload , Play , BookOpen , Dna , Activity , Shuffle } from 'lucide-react' ;
2+ import { ArrowRight , Upload , Play , BookOpen , Dna , Activity , Shuffle , Clock , ChevronRight } from 'lucide-react' ;
33import clsx from 'clsx' ;
44import { FEATURED_MOLECULES } from '../data/featuredMolecules' ;
5+ import { getRecentStructures , type RecentStructure } from '../lib/recentStructures' ;
56
67interface LandingOverlayProps {
78 isVisible : boolean ;
@@ -14,6 +15,12 @@ interface LandingOverlayProps {
1415export const LandingOverlay : React . FC < LandingOverlayProps > = ( { isVisible, onDismiss, onUpload, onStartTour, onLoadPdb } ) => {
1516 const [ shouldRender , setShouldRender ] = useState ( isVisible ) ;
1617 const [ isFadingOut , setIsFadingOut ] = useState ( false ) ;
18+ const [ recentStructures , setRecentStructures ] = useState < RecentStructure [ ] > ( [ ] ) ;
19+
20+ // Load recent structures from localStorage each time the overlay opens
21+ useEffect ( ( ) => {
22+ if ( isVisible ) setRecentStructures ( getRecentStructures ( ) ) ;
23+ } , [ isVisible ] ) ;
1724
1825 // Day of year logic for consistent daily rotation
1926 const dailyIndex = useMemo ( ( ) => {
@@ -32,14 +39,12 @@ export const LandingOverlay: React.FC<LandingOverlayProps> = ({ isVisible, onDis
3239 const handleShuffle = ( e : React . MouseEvent ) => {
3340 e . stopPropagation ( ) ;
3441 setIsShuffling ( true ) ;
35- // Animate shuffle
3642 let count = 0 ;
3743 const interval = setInterval ( ( ) => {
3844 setSelectedIndex ( prev => ( prev + 1 ) % FEATURED_MOLECULES . length ) ;
3945 count ++ ;
4046 if ( count > 5 ) {
4147 clearInterval ( interval ) ;
42- // Pick a new random index that is different from current
4348 let newIndex = Math . floor ( Math . random ( ) * FEATURED_MOLECULES . length ) ;
4449 while ( newIndex === selectedIndex && FEATURED_MOLECULES . length > 1 ) {
4550 newIndex = Math . floor ( Math . random ( ) * FEATURED_MOLECULES . length ) ;
@@ -57,20 +62,29 @@ export const LandingOverlay: React.FC<LandingOverlayProps> = ({ isVisible, onDis
5762 setIsFadingOut ( false ) ;
5863 } else {
5964 setIsFadingOut ( true ) ;
60- const timer = setTimeout ( ( ) => setShouldRender ( false ) , 500 ) ; // Match CSS transition
65+ const timer = setTimeout ( ( ) => setShouldRender ( false ) , 500 ) ;
6166 return ( ) => clearTimeout ( timer ) ;
6267 }
6368 } , [ isVisible ] ) ;
6469
6570 if ( ! shouldRender ) return null ;
6671
72+ const timeAgo = ( ts : number ) => {
73+ const m = Math . floor ( ( Date . now ( ) - ts ) / 60000 ) ;
74+ if ( m < 1 ) return 'just now' ;
75+ if ( m < 60 ) return `${ m } m ago` ;
76+ const h = Math . floor ( m / 60 ) ;
77+ if ( h < 24 ) return `${ h } h ago` ;
78+ return `${ Math . floor ( h / 24 ) } d ago` ;
79+ } ;
80+
6781 return (
6882 < div className = { clsx (
6983 "fixed inset-0 z-[100] flex flex-col md:flex-row items-center justify-center md:justify-between p-6 md:p-12 transition-opacity duration-500" ,
7084 isFadingOut ? "opacity-0 pointer-events-none" : "opacity-100" ,
7185 "bg-gradient-to-br from-black/90 via-black/80 to-transparent backdrop-blur-sm"
7286 ) } >
73- { /* Background Interaction Layer (Click to dismiss if clicking empty space) */ }
87+ { /* Background Interaction Layer */ }
7488 < div className = "absolute inset-0 z-0" onClick = { onDismiss } />
7589
7690 { /* Logo */ }
@@ -111,7 +125,7 @@ export const LandingOverlay: React.FC<LandingOverlayProps> = ({ isVisible, onDis
111125 </ button >
112126 </ div >
113127
114- < div className = "pt-8 flex items-center justify-center md:justify-start gap-6 text-sm text-gray-500 animate-in fade-in slide-in-from-bottom-8 duration-700 delay-500" >
128+ < div className = "pt-4 flex items-center justify-center md:justify-start gap-6 text-sm text-gray-500 animate-in fade-in slide-in-from-bottom-8 duration-700 delay-500" >
115129 < button onClick = { onUpload } className = "hover:text-white transition-colors flex items-center gap-2" >
116130 < Upload size = { 16 } /> Upload File
117131 </ button >
@@ -120,11 +134,34 @@ export const LandingOverlay: React.FC<LandingOverlayProps> = ({ isVisible, onDis
120134 < Dna size = { 16 } /> Load Example (2B3P)
121135 </ button >
122136 </ div >
137+
138+ { /* ── Recently Viewed ── */ }
139+ { recentStructures . length > 0 && (
140+ < div className = "pt-2 animate-in fade-in slide-in-from-bottom-8 duration-700 delay-700" >
141+ < div className = "flex items-center gap-2 text-xs text-gray-500 mb-2" >
142+ < Clock size = { 12 } />
143+ < span className = "font-semibold uppercase tracking-wider" > Recently Viewed</ span >
144+ </ div >
145+ < div className = "flex flex-wrap gap-2" >
146+ { recentStructures . map ( r => (
147+ < button
148+ key = { r . id }
149+ onClick = { ( ) => { onLoadPdb ( r . pdbId ?? r . id ) ; onDismiss ( ) ; } }
150+ className = "group flex items-center gap-2 px-3 py-1.5 bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 rounded-full text-xs text-gray-300 hover:text-white transition-all"
151+ >
152+ < Dna size = { 11 } className = "text-blue-400 opacity-70 group-hover:opacity-100" />
153+ < span className = "font-medium truncate max-w-[120px]" > { r . name } </ span >
154+ < span className = "text-gray-600 shrink-0" > { timeAgo ( r . timestamp ) } </ span >
155+ < ChevronRight size = { 10 } className = "text-gray-600 group-hover:text-gray-400 group-hover:translate-x-0.5 transition-transform" />
156+ </ button >
157+ ) ) }
158+ </ div >
159+ </ div >
160+ ) }
123161 </ div >
124162
125163 { /* FEATURED CARD (Right) */ }
126164 < div className = "relative pointer-events-auto w-full max-w-md animate-in fade-in slide-in-from-right-8 duration-1000 delay-500 hidden md:block" >
127- { /* Glass Card - Enlarged */ }
128165 < div className = "bg-black/40 backdrop-blur-xl border border-white/10 p-8 rounded-3xl shadow-2xl relative overflow-hidden group hover:border-white/20 transition-colors transform hover:-translate-y-2 duration-500" >
129166 < div className = "absolute top-0 right-0 p-4 opacity-5 group-hover:opacity-10 transition-opacity scale-150" >
130167 < Activity size = { 180 } />
0 commit comments