-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- created new builder page - cleaned up old files
- Loading branch information
Showing
19 changed files
with
494 additions
and
135 deletions.
There are no files selected for viewing
128 changes: 128 additions & 0 deletions
128
...js/app/builders/0x5D56b71abE6cA1Dc208Ed85926178f9758fa879c/_components/shooting-stars.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
"use client"; | ||
|
||
import React, { useEffect, useRef, useState } from "react"; | ||
import { cn } from "../_lib/utils"; | ||
|
||
interface ShootingStar { | ||
id: number; | ||
x: number; | ||
y: number; | ||
angle: number; | ||
scale: number; | ||
speed: number; | ||
distance: number; | ||
} | ||
|
||
interface ShootingStarsProps { | ||
minSpeed?: number; | ||
maxSpeed?: number; | ||
minDelay?: number; | ||
maxDelay?: number; | ||
starColor?: string; | ||
trailColor?: string; | ||
starWidth?: number; | ||
starHeight?: number; | ||
className?: string; | ||
} | ||
|
||
const getRandomStartPoint = () => { | ||
const side = Math.floor(Math.random() * 4); | ||
const offset = Math.random() * window.innerWidth; | ||
|
||
switch (side) { | ||
case 0: | ||
return { x: offset, y: 0, angle: 45 }; | ||
case 1: | ||
return { x: window.innerWidth, y: offset, angle: 135 }; | ||
case 2: | ||
return { x: offset, y: window.innerHeight, angle: 225 }; | ||
case 3: | ||
return { x: 0, y: offset, angle: 315 }; | ||
default: | ||
return { x: 0, y: 0, angle: 45 }; | ||
} | ||
}; | ||
export const ShootingStars: React.FC<ShootingStarsProps> = ({ | ||
minSpeed = 10, | ||
maxSpeed = 30, | ||
minDelay = 1200, | ||
maxDelay = 4200, | ||
starColor = "#9E00FF", | ||
trailColor = "#2EB9DF", | ||
starWidth = 10, | ||
starHeight = 1, | ||
className, | ||
}) => { | ||
const [star, setStar] = useState<ShootingStar | null>(null); | ||
const svgRef = useRef<SVGSVGElement>(null); | ||
|
||
useEffect(() => { | ||
const createStar = () => { | ||
const { x, y, angle } = getRandomStartPoint(); | ||
const newStar: ShootingStar = { | ||
id: Date.now(), | ||
x, | ||
y, | ||
angle, | ||
scale: 1, | ||
speed: Math.random() * (maxSpeed - minSpeed) + minSpeed, | ||
distance: 0, | ||
}; | ||
setStar(newStar); | ||
|
||
const randomDelay = Math.random() * (maxDelay - minDelay) + minDelay; | ||
setTimeout(createStar, randomDelay); | ||
}; | ||
|
||
createStar(); | ||
}, [minSpeed, maxSpeed, minDelay, maxDelay]); | ||
|
||
useEffect(() => { | ||
const moveStar = () => { | ||
if (star) { | ||
setStar(prevStar => { | ||
if (!prevStar) return null; | ||
const newX = prevStar.x + prevStar.speed * Math.cos((prevStar.angle * Math.PI) / 180); | ||
const newY = prevStar.y + prevStar.speed * Math.sin((prevStar.angle * Math.PI) / 180); | ||
const newDistance = prevStar.distance + prevStar.speed; | ||
const newScale = 1 + newDistance / 100; | ||
if (newX < -20 || newX > window.innerWidth + 20 || newY < -20 || newY > window.innerHeight + 20) { | ||
return null; | ||
} | ||
return { | ||
...prevStar, | ||
x: newX, | ||
y: newY, | ||
distance: newDistance, | ||
scale: newScale, | ||
}; | ||
}); | ||
} | ||
}; | ||
|
||
const animationFrame = requestAnimationFrame(moveStar); | ||
return () => cancelAnimationFrame(animationFrame); | ||
}, [star]); | ||
|
||
return ( | ||
<svg ref={svgRef} className={cn("w-full h-full absolute inset-0", className)}> | ||
{star && ( | ||
<rect | ||
key={star.id} | ||
x={star.x} | ||
y={star.y} | ||
width={starWidth * star.scale} | ||
height={starHeight} | ||
fill="url(#gradient)" | ||
transform={`rotate(${star.angle}, ${star.x + (starWidth * star.scale) / 2}, ${star.y + starHeight / 2})`} | ||
/> | ||
)} | ||
<defs> | ||
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%"> | ||
<stop offset="0%" style={{ stopColor: trailColor, stopOpacity: 0 }} /> | ||
<stop offset="100%" style={{ stopColor: starColor, stopOpacity: 1 }} /> | ||
</linearGradient> | ||
</defs> | ||
</svg> | ||
); | ||
}; |
113 changes: 113 additions & 0 deletions
113
.../app/builders/0x5D56b71abE6cA1Dc208Ed85926178f9758fa879c/_components/stars-background.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
"use client"; | ||
|
||
import React, { RefObject, useCallback, useEffect, useRef, useState } from "react"; | ||
import { cn } from "../_lib/utils"; | ||
|
||
interface StarProps { | ||
x: number; | ||
y: number; | ||
radius: number; | ||
opacity: number; | ||
twinkleSpeed: number | null; | ||
} | ||
|
||
interface StarBackgroundProps { | ||
starDensity?: number; | ||
allStarsTwinkle?: boolean; | ||
twinkleProbability?: number; | ||
minTwinkleSpeed?: number; | ||
maxTwinkleSpeed?: number; | ||
className?: string; | ||
} | ||
|
||
export const StarsBackground: React.FC<StarBackgroundProps> = ({ | ||
starDensity = 0.00015, | ||
allStarsTwinkle = true, | ||
twinkleProbability = 0.7, | ||
minTwinkleSpeed = 0.5, | ||
maxTwinkleSpeed = 1, | ||
className, | ||
}) => { | ||
const [stars, setStars] = useState<StarProps[]>([]); | ||
const canvasRef: RefObject<HTMLCanvasElement> = useRef<HTMLCanvasElement>(null); | ||
|
||
const generateStars = useCallback( | ||
(width: number, height: number): StarProps[] => { | ||
const area = width * height; | ||
const numStars = Math.floor(area * starDensity); | ||
return Array.from({ length: numStars }, () => { | ||
const shouldTwinkle = allStarsTwinkle || Math.random() < twinkleProbability; | ||
return { | ||
x: Math.random() * width, | ||
y: Math.random() * height, | ||
radius: Math.random() * 0.05 + 0.5, | ||
opacity: Math.random() * 0.5 + 0.5, | ||
twinkleSpeed: shouldTwinkle ? minTwinkleSpeed + Math.random() * (maxTwinkleSpeed - minTwinkleSpeed) : null, | ||
}; | ||
}); | ||
}, | ||
[starDensity, allStarsTwinkle, twinkleProbability, minTwinkleSpeed, maxTwinkleSpeed], | ||
); | ||
|
||
useEffect(() => { | ||
const updateStars = () => { | ||
if (canvasRef.current) { | ||
const canvas = canvasRef.current; | ||
const ctx = canvas.getContext("2d"); | ||
if (!ctx) return; | ||
|
||
const { width, height } = canvas.getBoundingClientRect(); | ||
canvas.width = width; | ||
canvas.height = height; | ||
setStars(generateStars(width, height)); | ||
} | ||
}; | ||
|
||
updateStars(); | ||
|
||
const resizeObserver = new ResizeObserver(updateStars); | ||
if (canvasRef.current) { | ||
resizeObserver.observe(canvasRef.current); | ||
} | ||
|
||
return () => { | ||
if (canvasRef.current) { | ||
resizeObserver.unobserve(canvasRef.current); | ||
} | ||
}; | ||
}, [starDensity, allStarsTwinkle, twinkleProbability, minTwinkleSpeed, maxTwinkleSpeed, generateStars]); | ||
|
||
useEffect(() => { | ||
const canvas = canvasRef.current; | ||
if (!canvas) return; | ||
|
||
const ctx = canvas.getContext("2d"); | ||
if (!ctx) return; | ||
|
||
let animationFrameId: number; | ||
|
||
const render = () => { | ||
ctx.clearRect(0, 0, canvas.width, canvas.height); | ||
stars.forEach(star => { | ||
ctx.beginPath(); | ||
ctx.arc(star.x, star.y, star.radius, 0, Math.PI * 2); | ||
ctx.fillStyle = `rgba(255, 255, 255, ${star.opacity})`; | ||
ctx.fill(); | ||
|
||
if (star.twinkleSpeed !== null) { | ||
star.opacity = 0.5 + Math.abs(Math.sin((Date.now() * 0.001) / star.twinkleSpeed) * 0.5); | ||
} | ||
}); | ||
|
||
animationFrameId = requestAnimationFrame(render); | ||
}; | ||
|
||
render(); | ||
|
||
return () => { | ||
cancelAnimationFrame(animationFrameId); | ||
}; | ||
}, [stars]); | ||
|
||
return <canvas ref={canvasRef} className={cn("h-full w-full absolute inset-0", className)} />; | ||
}; |
117 changes: 117 additions & 0 deletions
117
packages/nextjs/app/builders/_components/BuilderCard.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import { useEffect, useState } from "react"; | ||
import Image from "next/image"; | ||
import Link from "next/link"; | ||
import { normalize } from "path"; | ||
import { getAddress, isAddress } from "viem"; | ||
import { useEnsAvatar, useEnsName } from "wagmi"; | ||
import Arrow_Icon from "~~/components/Arrow_Icon"; | ||
import { Address, BlockieAvatar } from "~~/components/scaffold-eth"; | ||
import { Builder, Mentor } from "~~/types/builders"; | ||
|
||
type Props = { | ||
mentor?: Mentor; | ||
builder?: Builder; | ||
}; | ||
|
||
const banners = [ | ||
"/banner_1.jpg", | ||
"/banner_2.jpg", | ||
"/banner_3.jpg", | ||
"/banner_4.jpg", | ||
"/banner_5.jpg", | ||
"/banner_6.jpg", | ||
"/banner_7.jpg", | ||
]; | ||
|
||
const getRandomBanner = () => { | ||
const randomIndex = Math.floor(Math.random() * banners.length); | ||
return banners[randomIndex]; | ||
}; | ||
|
||
const BuilderCard = ({ mentor, builder }: Props) => { | ||
const [ensAvatar, setEnsAvatar] = useState<string | null>(); | ||
const checkSumAddress = builder?.address ? getAddress(builder.address) : undefined; | ||
const [banner, setBanner] = useState<string>(""); | ||
|
||
useEffect(() => { | ||
setBanner(getRandomBanner()); | ||
}, []); | ||
|
||
const { data: fetchedEns } = useEnsName({ | ||
address: checkSumAddress, | ||
chainId: 1, | ||
query: { | ||
enabled: isAddress(checkSumAddress ?? ""), | ||
}, | ||
}); | ||
|
||
const { data: fetchedEnsAvatar } = useEnsAvatar({ | ||
name: fetchedEns ? normalize(fetchedEns) : undefined, | ||
chainId: 1, | ||
query: { | ||
enabled: Boolean(fetchedEns), | ||
gcTime: 30_000, | ||
}, | ||
}); | ||
|
||
useEffect(() => { | ||
setEnsAvatar(fetchedEnsAvatar); | ||
}, [fetchedEnsAvatar]); | ||
|
||
return ( | ||
<div className="z-10 border rounded-xl h-[15rem] sm:min-w-[22rem] min-w-[17rem] w-full font-light dark:border-zinc-700 border-zinc-400 flex flex-col text-text-zinc-700 dark:text-zinc-300 shadow-md"> | ||
<div className="w-full relative flex flex-col flex-grow-[20] rounded-t-xl"> | ||
{banner ? ( | ||
<Image | ||
src={banner} | ||
alt="banner" | ||
priority={true} | ||
width={1000} | ||
height={1000} | ||
className="rounded-t-xl object-cover max-h-24" | ||
/> | ||
) : ( | ||
<div className="bg-gradient-to-r from-st_cyan/10 to-st_purple/10 rounded-t-xl h-24 w-full" /> | ||
)} | ||
|
||
<div className="rounded-full z-10 top-10 left-5 absolute dark:border-zinc-700 scale-90 sm:scale-100 border-zinc-400 border-4"> | ||
{mentor && <Image src={mentor.image} alt="builder" width={110} height={110} className="rounded-full" />} | ||
|
||
{builder && <BlockieAvatar address={builder.address as `0x${string}`} ensImage={ensAvatar} size={100} />} | ||
</div> | ||
|
||
<div className="text-end"> | ||
<div | ||
className="bg-clip-text text-transparent | ||
dark:bg-[linear-gradient(to_right,theme(colors.indigo.500),theme(colors.indigo.200),theme(colors.rose.500),theme(colors.fuchsia.500),theme(colors.sky.500),theme(colors.indigo.200),theme(colors.indigo.500))] | ||
bg-[linear-gradient(to_right,theme(colors.indigo.500),theme(colors.indigo.800),theme(colors.sky.500),theme(colors.fuchsia.500),theme(colors.sky.500),theme(colors.indigo.800),theme(colors.indigo.500))] bg-[length:200%_auto] animate-gradient font-bold text-2xl" | ||
> | ||
{mentor && <p className="pr-4 sm:pr-8 uppercase">{mentor.name}</p>} | ||
|
||
{builder ? ( | ||
<div className="w-full flex justify-end pt-14 sm:pt-12 pr-4"> | ||
<Address address={builder.address} size="sm" /> | ||
</div> | ||
) : null} | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<hr className="border-t dark:border-zinc-700 border-zinc-400" /> | ||
|
||
<div className="hover:bg-st_cyan/10 rounded-b-xl duration-75 transition-all dark:border-zinc-700 border-zinc-400 flex-grow"> | ||
<Link | ||
href={mentor ? mentor.profileLink : builder ? builder.profileLink : ""} | ||
target="_blank" | ||
className="flex items-center justify-between h-full px-4" | ||
> | ||
<p className="font-medium flex flex-col items-center justify-center">View Profile</p> | ||
|
||
<Arrow_Icon /> | ||
</Link> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default BuilderCard; |
Oops, something went wrong.