Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/app/[username]/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import toast, { Toaster } from 'react-hot-toast';
import { items } from './exportTools';
import { selects } from './selectTools';
import { checks } from './checkboxTools';
import Loader from "@/components/Loader";

export default function previewCard({ params }) {
const username = params.username;
Expand All @@ -16,6 +17,7 @@ export default function previewCard({ params }) {
const configCalled = useRef(false);
const userConfigRef = useRef({});
const [imageUrl, setImageUrl] = useState("");
const [loading, setLoading] = useState(false);

const defaultConfig = {
theme: "dark",
Expand Down Expand Up @@ -151,8 +153,11 @@ export default function previewCard({ params }) {
const fetchImage = async () => {
if (isUrlComplete()) {
const url = `${process.env.NEXT_PUBLIC_BASE_URL}/${username}/image?${getUrlParams(config)}`;
// Start loading until the image element confirms onLoad
setLoading(true);
setImageUrl(url);
} else {
setLoading(false);
Comment thread
palchhinparihar marked this conversation as resolved.
setImageUrl("");
}
};
Expand All @@ -163,13 +168,16 @@ export default function previewCard({ params }) {

return (
<div className='min-h-screen min-w-[100%] px-5 text-white relative flex flex-col gap-2'>
{loading && <Loader message="Generating preview..." />}
<div className="flex md:gap-10 items-center justify-center mb-2 md:h-[360px]">
<div className="w-[620px] lg:w-[720px] mt-6 md:h-[360px] flex">
{imageUrl && (
<img
src={imageUrl}
alt={`Background image of ${username}`}
title={`Background preview for ${username}`}
onLoad={() => setLoading(false)}
onError={() => { setLoading(false); toast.error("Failed to load image."); }}
Comment thread
palchhinparihar marked this conversation as resolved.
/>
)}
</div>
Expand Down
14 changes: 14 additions & 0 deletions src/components/Loader.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react'

const Loader = ({ message = 'Loading...' }) => {
return (
<div className='fixed inset-0 flex items-center justify-center bg-black/40 backdrop-blur-sm pointer-events-none z-50'>
Comment thread
palchhinparihar marked this conversation as resolved.
<div className='flex flex-col items-center gap-4'>
<div className='h-12 w-12 rounded-full border-4 border-purple-500 border-t-transparent animate-spin' />
<p className='text-sm md:text-base text-gray-200 font-medium text-center animate-pulse'>{message}</p>
</div>
</div>
)
}

export default Loader
42 changes: 29 additions & 13 deletions src/pages/Home.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import { useRouter } from 'next/navigation'
import toast, { Toaster } from 'react-hot-toast'
import Typewriter from 'typewriter-effect'
import Link from 'next/link'
import Loader from '@/components/Loader'

function Home() {
const [username, setUserName] = useState('')
const [showContributors, setShowContributors] = useState(false)
const [loading, setLoading] = useState(false)
const router = useRouter()
const inputRef = useRef(null)

Expand All @@ -26,6 +28,7 @@ function Home() {

if (username.trim()) {
try {
setLoading(true)
const response = await fetch('/api/user', {
method: 'POST',
headers: {
Expand All @@ -37,6 +40,7 @@ function Home() {
if (response.ok) {
const data = await response.json()
if (data.exists) {
// Show loader while navigating to profile page
router.push(`/${username.trim()}`)
Comment thread
palchhinparihar marked this conversation as resolved.
} else {
toast.error('Username does not exist on GitHub')
Expand All @@ -46,6 +50,9 @@ function Home() {
}
} catch (error) {
toast.error('An unexpected error occurred. Please try again.')
} finally {
// Keep loading state true briefly to allow route transition loading UI to appear
setTimeout(() => setLoading(false), 800)
}
Comment thread
palchhinparihar marked this conversation as resolved.
} else {
toast.error('Please enter a username.')
Expand Down Expand Up @@ -108,29 +115,38 @@ function Home() {
value={username}
onChange={handleUser}
ref={inputRef}
disabled={loading}
/>

{/* Submit button */}
<button
type='submit'
disabled={!username.trim()}
className='bg-purple-500 text-white p-3 rounded-full hover:bg-purple-600 focus:outline-none focus:ring-4 focus:ring-purple-500 focus:ring-opacity-50 transition duration-200 flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed'
disabled={!username.trim() || loading}
aria-busy={loading}
className='relative bg-purple-500 text-white p-3 rounded-full hover:bg-purple-600 focus:outline-none focus:ring-4 focus:ring-purple-500 focus:ring-opacity-50 transition duration-200 flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed'
>
<svg
xmlns='http://www.w3.org/2000/svg'
className='h-5 w-5'
fill='none'
viewBox='0 0 24 24'
stroke='currentColor'
strokeWidth='2'
>
<path strokeLinecap='round' strokeLinejoin='round' d='M9 5l7 7-7 7' />
</svg>
{loading ? (
<span className='h-5 w-5 rounded-full border-2 border-white border-t-transparent animate-spin' />
) : (
<svg
xmlns='http://www.w3.org/2000/svg'
className='h-5 w-5'
fill='none'
viewBox='0 0 24 24'
stroke='currentColor'
strokeWidth='2'
>
<path strokeLinecap='round' strokeLinejoin='round' d='M9 5l7 7-7 7' />
</svg>
)}
</button>
</div>
</form>
</>
</main>
{loading && (
<Loader message='Verifying username...' />
)}
{/* Footer */}
<footer className='p-4 text-center text-gray-400'>
Open source ❀️ |{' '}
Expand All @@ -154,4 +170,4 @@ function Home() {
)
}

export default Home
export default Home