Skip to content

Commit cb83189

Browse files
committed
Add full-screen loading overlay for auto-fetching in NavSearch component
1 parent 7dfe5cd commit cb83189

1 file changed

Lines changed: 61 additions & 7 deletions

File tree

src/components/NavSearch.tsx

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useState, useRef, useEffect, useCallback } from 'react'
2+
import { createPortal } from 'react-dom'
23
import { useSearchParams } from 'react-router-dom'
34
import { Search, X, Star, Loader2, ExternalLink, MapPin, GitFork, Eye } from 'lucide-react'
45
import { PortfolioPreviewModal } from './PortfolioPreviewModal'
@@ -54,6 +55,8 @@ export function NavSearch({ variant = 'default' }: NavSearchProps) {
5455
const inputRef = useRef<HTMLInputElement>(null)
5556
const containerRef = useRef<HTMLDivElement>(null)
5657
const autoPreviewRef = useRef(searchParams.get('preview'))
58+
// true while auto-fetching from a shared ?preview= link
59+
const [autoLoading, setAutoLoading] = useState(!!autoPreviewRef.current)
5760

5861
const openPreview = () => {
5962
if (state.status !== 'done') return
@@ -132,17 +135,17 @@ export function NavSearch({ variant = 'default' }: NavSearchProps) {
132135
// eslint-disable-next-line react-hooks/exhaustive-deps
133136
}, [])
134137

135-
// Auto-open modal once the auto-fetch completes
138+
// Auto-open modal once the auto-fetch completes; clear loader on done or error
136139
useEffect(() => {
137-
if (
138-
autoPreviewRef.current &&
139-
state.status === 'done' &&
140-
state.user.login.toLowerCase() === autoPreviewRef.current.toLowerCase()
141-
) {
140+
if (!autoLoading) return
141+
if (state.status === 'done') {
142142
setPreviewOpen(true)
143+
setAutoLoading(false)
143144
autoPreviewRef.current = null
145+
} else if (state.status === 'error') {
146+
setAutoLoading(false)
144147
}
145-
}, [state])
148+
}, [state, autoLoading])
146149

147150
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
148151
if (e.key === 'Enter') void search(query)
@@ -341,6 +344,57 @@ export function NavSearch({ variant = 'default' }: NavSearchProps) {
341344
</div>
342345
)}
343346

347+
{/* Full-screen loading overlay — shown while auto-fetching from a shared link */}
348+
{autoLoading && createPortal(
349+
<div className="fixed inset-0 z-[200] flex flex-col items-center justify-center bg-[#050509]">
350+
{/* Ambient glow */}
351+
<div
352+
className="pointer-events-none absolute inset-0"
353+
style={{
354+
background: 'radial-gradient(ellipse 60% 40% at 50% 50%, rgba(59,130,246,0.07) 0%, transparent 70%)',
355+
}}
356+
/>
357+
358+
<div className="relative z-10 flex flex-col items-center gap-6 text-center px-6">
359+
{/* Spinner ring */}
360+
<div className="relative h-16 w-16">
361+
<div className="absolute inset-0 rounded-full border-2 border-blue-500/15" />
362+
<div
363+
className="absolute inset-0 rounded-full border-2 border-transparent border-t-blue-400"
364+
style={{ animation: 'spin 0.9s linear infinite' }}
365+
/>
366+
<div className="absolute inset-[6px] rounded-full border border-cyan-500/20" />
367+
</div>
368+
369+
{/* Label */}
370+
<div className="flex flex-col items-center gap-1.5">
371+
<p className="text-[10px] font-bold uppercase tracking-[0.35em] text-blue-400/70">
372+
Loading portfolio
373+
</p>
374+
<p className="text-lg font-semibold text-zinc-100">
375+
@{searchParams.get('preview') ?? query}
376+
</p>
377+
{state.status === 'loading' && (
378+
<p className="text-xs text-zinc-600 mt-1">Fetching GitHub data…</p>
379+
)}
380+
{state.status === 'error' && (
381+
<p className="text-xs text-red-400 mt-1">{(state as { status: 'error'; message: string }).message}</p>
382+
)}
383+
</div>
384+
385+
{/* Dismiss */}
386+
<button
387+
type="button"
388+
onClick={() => { setAutoLoading(false); setSearchParams({}, { replace: true }) }}
389+
className="mt-2 text-[11px] text-zinc-600 hover:text-zinc-400 transition-colors"
390+
>
391+
Cancel
392+
</button>
393+
</div>
394+
</div>,
395+
document.body
396+
)}
397+
344398
{/* Full-screen portfolio preview modal */}
345399
{previewOpen && state.status === 'done' && (
346400
<PortfolioPreviewModal

0 commit comments

Comments
 (0)