1+ import { useState , useEffect , useMemo } from 'react' ;
2+ import { useLanyard } from 'use-lanyard' ;
3+ import { useAutoAnimate } from '@formkit/auto-animate/react' ;
4+ import rawGames from '../assets/gameList.json' ;
5+
6+ const gameList = rawGames as { [ key : string ] : string } ;
7+
8+ const INTERNAL_ASSETS_LINK = "https://cdn.discordapp.com/app-assets/" ;
9+ const INTERNAL_ICONS_LINK = "https://cdn.discordapp.com/app-icons/" ;
10+ const EXTERNAL_ASSETS_LINK = "https://media.discordapp.net/external/" ;
11+
12+ const formatTime = ( milliseconds : number ) => {
13+ const totalSeconds = Math . floor ( milliseconds / 1000 ) ;
14+ const hours = Math . floor ( totalSeconds / 3600 ) ;
15+ const minutes = Math . floor ( ( totalSeconds % 3600 ) / 60 ) ;
16+ const seconds = totalSeconds % 60 ;
17+
18+ const paddedMinutes = minutes < 10 ? `0${ minutes } ` : minutes ;
19+ const paddedSeconds = seconds < 10 ? `0${ seconds } ` : seconds ;
20+
21+ if ( hours > 0 ) return `${ hours } :${ paddedMinutes } :${ paddedSeconds } ` ;
22+ return `${ paddedMinutes } :${ paddedSeconds } ` ;
23+ } ;
24+
25+ const LanyardStatus = ( ) => {
26+ const [ parent ] = useAutoAnimate ( ) ;
27+ const [ visible , setVisible ] = useState ( false ) ;
28+ const [ currentTime , setCurrentTime ] = useState ( new Date ( ) . getTime ( ) ) ;
29+
30+ const { data : status } = useLanyard ( "342874998375186432" ) ;
31+
32+ useEffect ( ( ) => {
33+ const timer = setTimeout ( ( ) => setVisible ( true ) , 500 ) ;
34+
35+ const interval = setInterval ( ( ) => {
36+ setCurrentTime ( new Date ( ) . getTime ( ) ) ;
37+ } , 1000 ) ;
38+
39+ return ( ) => {
40+ clearTimeout ( timer ) ;
41+ clearInterval ( interval ) ;
42+ } ;
43+ } , [ ] ) ;
44+
45+ const activity = useMemo ( ( ) => {
46+ if ( ! status ?. activities || status . activities . length === 0 ) return null ;
47+ return status . activities [ status . activities . length - 1 ] ;
48+ } , [ status ] ) ;
49+
50+ const handleExternalLinks = ( image : string | undefined ) => {
51+ if ( ! image ) return null ;
52+ return image . startsWith ( "mp:external/" )
53+ ? `${ EXTERNAL_ASSETS_LINK } ${ image . replace ( "mp:external/" , "" ) } `
54+ : `${ INTERNAL_ASSETS_LINK } ${ activity ?. application_id } /${ image } .png` ;
55+ } ;
56+
57+ const largeImage = useMemo ( ( ) => {
58+ if ( ! activity ) return null ;
59+ return gameList [ activity . application_id || "" ]
60+ ? `${ INTERNAL_ICONS_LINK } ${ activity . application_id } /${ gameList [ activity . application_id as keyof typeof gameList ] } .png`
61+ : handleExternalLinks ( activity . assets ?. large_image ) ;
62+ } , [ activity ] ) ;
63+
64+ const smallImage = useMemo ( ( ) => {
65+ return handleExternalLinks ( activity ?. assets ?. small_image ) ;
66+ } , [ activity ] ) ;
67+
68+ if ( ! status || ! visible || ! activity ) return < div ref = { parent } /> ;
69+
70+ return (
71+ < div ref = { parent } >
72+ { status . spotify && status . spotify . timestamps ?. end ? (
73+ < div className = "m-2 rounded-lg bg-viola-100 p-8 shadow-lg" >
74+ < h2 className = "mb-2 text-2xl font-bold" > Status</ h2 >
75+ < h3 className = "text-md mb-2 font-bold" > Listening to Spotify</ h3 >
76+
77+ < div className = "flex items-center space-x-4" >
78+ < img src = { status . spotify . album_art_url as string } alt = "Album Art" className = "h-16 w-16 rounded" />
79+ < div >
80+ < h4 className = "w-32 truncate text-sm md:w-48 lg:w-32 xl:w-32 2xl:w-48" title = { status . spotify . song } >
81+ { status . spotify . song }
82+ </ h4 >
83+ < p className = "w-32 truncate text-xs md:w-48 lg:w-32 xl:w-32 2xl:w-48" title = { status . spotify . artist as string } >
84+ by { status . spotify . artist }
85+ </ p >
86+ < p className = "w-32 truncate text-xs md:w-48 lg:w-32 xl:w-32 2xl:w-48" title = { status . spotify . album as string } >
87+ on { status . spotify . album }
88+ </ p >
89+ </ div >
90+ </ div >
91+
92+ < div className = "mt-2 flex items-center" >
93+ < span className = "relative inline-block text-left tabular-nums" >
94+ < span aria-hidden = "true" className = "invisible block" > 00:00</ span >
95+ < span className = "absolute inset-0" >
96+ { formatTime ( Math . min ( currentTime - status . spotify . timestamps . start , status . spotify . timestamps . end - status . spotify . timestamps . start ) ) }
97+ </ span >
98+ </ span >
99+
100+ < div className = "mx-1 h-3 flex-1 overflow-hidden rounded-xl bg-slate-300" >
101+ < div
102+ className = "h-full rounded-xl bg-viola-400"
103+ style = { {
104+ width : `${ ( ( currentTime - status . spotify . timestamps . start ) / ( status . spotify . timestamps . end - status . spotify . timestamps . start ) ) * 100 } %`
105+ } }
106+ > </ div >
107+ </ div >
108+
109+ < span className = "relative inline-block text-right tabular-nums" >
110+ < span aria-hidden = "true" className = "invisible block" > 00:00</ span >
111+ < span className = "absolute inset-0" >
112+ { formatTime ( status . spotify . timestamps . end - status . spotify . timestamps . start ) }
113+ </ span >
114+ </ span >
115+ </ div >
116+ </ div >
117+ ) : (
118+ // General Activity Layout
119+ < div className = "m-2 rounded-lg bg-viola-100 p-8 shadow-lg" >
120+ < h2 className = "mb-4 text-2xl font-bold" > Status</ h2 >
121+ < div className = "flex items-center space-x-4" >
122+ < div className = "relative flex-shrink-0" >
123+ { largeImage && (
124+ < img src = { largeImage } alt = "Large Activity Icon" className = "h-16 w-16 rounded" />
125+ ) }
126+ { ! largeImage && ! smallImage && activity . emoji ?. name && (
127+ < span className = "text-3xl" > { activity . emoji . name } </ span >
128+ ) }
129+ { smallImage && ! largeImage && (
130+ < img src = { smallImage } alt = "Activity Icon" className = "h-16 w-16 rounded" />
131+ ) }
132+ { smallImage && largeImage && (
133+ < img src = { smallImage } alt = "Small Activity Icon" className = "ring-3 absolute bottom-0 right-0 h-6 w-6 rounded" />
134+ ) }
135+ </ div >
136+
137+ < div >
138+ < h3 className = "text-sm font-bold" > { activity . name } </ h3 >
139+ < p className = "text-xs" > { activity . state } </ p >
140+ < p className = "text-xs" > { activity . details } </ p >
141+ < p className = "text-xs" > { formatTime ( currentTime - activity . created_at ) } elapsed</ p >
142+ </ div >
143+ </ div >
144+ </ div >
145+ ) }
146+ </ div >
147+ ) ;
148+ } ;
149+
150+ export default LanyardStatus ;
0 commit comments