MostZappedNote.tsx (3179B)
1 import { MotionValue, circOut, easeInOut, easeOut, motion, useMotionValue, useScroll, useTime, useTransform } from "framer-motion"; 2 import { PurpleIcon } from "../../icons/PurpleIcon"; 3 import { ArrowDown, ZapIcon } from "lucide-react"; 4 import { NostrNoteView, ParsedNote } from "@/components/note/NostrNoteView"; 5 import { Npub2024InReviewStats } from "@/pages/purple/2024-in-review/[npub]"; 6 import { cn } from "@/lib/utils"; 7 import { useRef } from "react"; 8 import { Orbitron } from "next/font/google"; 9 import NumberFlow from '@number-flow/react' 10 import { useEffect, useState } from "react"; 11 import { useInterval } from "usehooks-ts"; 12 13 const orbitron = Orbitron({ subsets: ['latin'] }) 14 15 export function MostZappedNote({ stats, className, style }: { stats: Npub2024InReviewStats, className?: string, style?: React.CSSProperties }) { 16 const time = useTime() 17 const ref = useRef<HTMLDivElement>(null) 18 const { scrollYProgress } = useScroll({ 19 target: ref 20 }) 21 const [animatedZapAmount, setAnimatedZapAmount] = useState(10000); 22 const zapProgress = useTransform( 23 scrollYProgress, 24 [0.4, 0.6], 25 [ 26 0.0, 27 1.0 28 ], 29 { 30 clamp: true, 31 ease: circOut 32 } 33 ) 34 const headingOpacity = useTransform( 35 scrollYProgress, 36 [0, 0.4], 37 [0, 1], 38 { 39 clamp: true, 40 ease: easeInOut 41 } 42 ) 43 const secondaryContentOpacity = useTransform( 44 scrollYProgress, 45 [0.4, 0.6], 46 [0, 1], 47 { 48 clamp: true, 49 ease: easeInOut 50 } 51 ) 52 53 useInterval(() => { 54 if (zapProgress.get() > 0.0001) { 55 setAnimatedZapAmount(stats.most_zapped_post_sats) 56 } 57 else { 58 setAnimatedZapAmount(10000) 59 } 60 }, 500) 61 62 return <div 63 ref={ref} 64 className={cn("container z-30 mx-auto px-4 pt-12 h-full min-h-screen flex flex-col gap-y-4 justify-center items-center", className)} 65 style={style} 66 > 67 <motion.h2 68 className="text-4xl md:text-6xl text-center text-yellow-50 drop-shadow font-semibold break-keep tracking-tight z-30" 69 style={{ 70 textShadow: "0 0 60px #facc15", 71 opacity: headingOpacity, 72 }} 73 > 74 Your most zapped note received 75 </motion.h2> 76 <motion.h3 77 className="flex items-center gap-4 text-3xl md:text-8xl text-center text-white font-semibold break-keep tracking-tight z-30" 78 style={{ 79 opacity: secondaryContentOpacity, 80 }} 81 > 82 <motion.span 83 style={{ opacity: secondaryContentOpacity }} 84 > 85 <ZapIcon className="w-8 h-8 md:w-16 md:h-16 text-amber-300" /> 86 </motion.span> 87 <NumberFlow 88 value={animatedZapAmount} 89 continuous={true} 90 transformTiming={{ duration: 1500, easing: "ease-out" }} 91 opacityTiming={{ duration: 1500, easing: "ease-out" }} 92 trend={1} 93 /> 94 <motion.span 95 className="text-amber-300" 96 style={{ opacity: secondaryContentOpacity }} 97 > 98 sats 99 </motion.span> 100 </motion.h3> 101 <motion.div 102 className="space-y-4 z-30 w-full max-w-lg" 103 style={{ opacity: secondaryContentOpacity }} 104 > 105 <NostrNoteView note={stats.most_zapped_post} className="w-full max-w-lg" /> 106 </motion.div> 107 </div> 108 }