@@ -26,12 +26,17 @@ async function getUserProfile(identity: string) {
2626}
2727
2828function App ( ) {
29- const [ search , setSearch ] = useState < string > ( ) ;
29+ const [ isLoading , setIsLoading ] = useState < boolean > ( true ) ;
30+ const [ search , setSearch ] = useState < string > ( 'tsky.dev' ) ;
3031 const [ user , setUser ] = useState < ActorProfile > ( ) ;
3132
3233 useEffect ( ( ) => {
3334 if ( search ) {
34- getUserProfile ( search ) . then ( setUser ) ;
35+ setIsLoading ( true ) ;
36+ getUserProfile ( search ) . then ( ( user ) => {
37+ setUser ( user ) ;
38+ setIsLoading ( false ) ;
39+ } ) ;
3540 }
3641 } , [ search ] ) ;
3742
@@ -72,101 +77,115 @@ function App() {
7277 Search
7378 </ button >
7479 </ form >
75- { user && (
76- < div className = "w-full rounded-xl ring-1 ring-transparent dark:ring-white/5 overflow-hidden drop-shadow-lg dark:drop-shadow-none" >
77- < img
78- src = { user . banner }
79- alt = "banner"
80- className = "h-36 w-full object-cover"
81- />
82- < div className = "bg-white dark:bg-zinc-900 p-4" >
83- < div className = "flex items-end justify-between -mt-16" >
84- < div className = "rounded-full size-28 border-[3px] border-white dark:border-zinc-900 overflow-hidden" >
85- < img
86- src = { user . avatar }
87- alt = "profile-pic"
88- className = "size-full object-cover"
89- />
90- </ div >
91- < a
92- href = { `https://bsky.app/profile/${ user . handle } ` }
93- target = "_blank"
94- rel = "noreferrer"
95- className = "rounded-full"
96- >
97- < button
98- type = "button"
99- className = "whitespace-nowrap cursor-pointer font-semibold h-max transition-all border select-none outline-none px-3 py-2 text-sm flex items-center justify-center rounded-full text-white dark:text-black focus-visible:ring-2 ring-offset-2 ring-offset-white dark:ring-offset-zinc-950 border-transparent bg-blue-500 hover:bg-blue-600 dark:bg-blue-300/90 dark:hover:bg-blue-400/80 ring-blue-300 dark:ring-blue-100"
100- >
101- < svg
102- xmlns = "http://www.w3.org/2000/svg"
103- fill = "none"
104- viewBox = "0 0 24 24"
105- strokeWidth = "3"
106- stroke = "currentColor"
107- className = "size-3.5 mr-1"
108- >
109- < title > Plus Icon</ title >
110- < path
111- strokeLinecap = "round"
112- strokeLinejoin = "round"
113- d = "M12 4.5v15m7.5-7.5h-15"
114- />
115- </ svg >
116- Follow
117- </ button >
118- </ a >
119- </ div >
120- < h1 className = "text-lg font-semibold mt-2" > { user . displayName } </ h1 >
121- < p className = "text-sm text-zinc-500 dark:text-zinc-400" >
122- @{ user . handle }
123- </ p >
124- < div className = "mt-3 flex items-center gap-3 text-sm font-semibold" >
125- < p >
126- { user . followersCount }
127- < span className = "font-normal text-zinc-500 dark:text-zinc-400" >
128- { ' ' }
129- Followers
130- </ span >
131- </ p >
132- < p >
133- { user . followsCount }
134- < span className = "font-normal text-zinc-500 dark:text-zinc-400" >
135- { ' ' }
136- Following
137- </ span >
138- </ p >
139- </ div >
140- < p className = "mt-4" > { user . description } </ p >
141- < div className = "flex items-end justify-between" >
142- < p className = "text-sm text-zinc-400 dark:text-zinc-500" >
143- Made with Tsky
144- </ p >
145- < a
146- href = "https://github.com/tsky-dev/tsky"
147- target = "_blank"
148- rel = "noreferrer"
149- >
150- < svg
151- xmlns = "http://www.w3.org/2000/svg"
152- fill = "none"
153- viewBox = "0 0 320 286"
154- className = "size-8"
155- >
156- < title > Bluesky Icon</ title >
157- < path
158- fill = "rgb(10,122,255)"
159- d = "M69.364 19.146c36.687 27.806 76.147 84.186 90.636 114.439 14.489-30.253 53.948-86.633 90.636-114.439C277.107-.917 320-16.44 320 32.957c0 9.865-5.603 82.875-8.889 94.729-11.423 41.208-53.045 51.719-90.071 45.357 64.719 11.12 81.182 47.953 45.627 84.785-80 82.874-106.667-44.333-106.667-44.333s-26.667 127.207-106.667 44.333c-35.555-36.832-19.092-73.665 45.627-84.785-37.026 6.362-78.648-4.149-90.071-45.357C5.603 115.832 0 42.822 0 32.957 0-16.44 42.893-.917 69.364 19.147Z"
160- />
161- </ svg >
162- </ a >
163- </ div >
164- </ div >
165- </ div >
166- ) }
80+ { isLoading ? < LoadingSkeleton /> : user && < UserCard user = { user } /> }
16781 </ div >
16882 </ main >
16983 ) ;
17084}
17185
17286export default App ;
87+
88+ function LoadingSkeleton ( ) {
89+ return (
90+ < div className = "max-w-lg mx-auto w-full h-96 animate-pulse rounded-md bg-zinc-400/20" />
91+ ) ;
92+ }
93+
94+ function UserCard ( { user } : { user : ActorProfile } ) {
95+ return (
96+ < div className = "w-full rounded-xl ring-1 ring-transparent dark:ring-white/5 overflow-hidden drop-shadow-lg dark:drop-shadow-none" >
97+ { user . banner ? (
98+ < img
99+ src = { user . banner }
100+ alt = "banner"
101+ className = "h-36 w-full object-cover"
102+ />
103+ ) : (
104+ < div className = "h-36 w-full bg-blue-500 dark:bg-blue-300" />
105+ ) }
106+ < div className = "bg-white dark:bg-zinc-900 p-4" >
107+ < div className = "flex items-end justify-between -mt-16" >
108+ < div className = "rounded-full size-28 border-[3px] border-white dark:border-zinc-900 overflow-hidden" >
109+ < img
110+ src = { user . avatar }
111+ alt = "profile-pic"
112+ className = "size-full object-cover"
113+ />
114+ </ div >
115+ < a
116+ href = { `https://bsky.app/profile/${ user . handle } ` }
117+ target = "_blank"
118+ rel = "noreferrer"
119+ className = "rounded-full"
120+ >
121+ < button
122+ type = "button"
123+ className = "whitespace-nowrap cursor-pointer font-semibold h-max transition-all border select-none outline-none px-3 py-2 text-sm flex items-center justify-center rounded-full text-white dark:text-black focus-visible:ring-2 ring-offset-2 ring-offset-white dark:ring-offset-zinc-950 border-transparent bg-blue-500 hover:bg-blue-600 dark:bg-blue-300/90 dark:hover:bg-blue-400/80 ring-blue-300 dark:ring-blue-100"
124+ >
125+ < svg
126+ xmlns = "http://www.w3.org/2000/svg"
127+ fill = "none"
128+ viewBox = "0 0 24 24"
129+ strokeWidth = "3"
130+ stroke = "currentColor"
131+ className = "size-3.5 mr-1"
132+ >
133+ < title > Plus Icon</ title >
134+ < path
135+ strokeLinecap = "round"
136+ strokeLinejoin = "round"
137+ d = "M12 4.5v15m7.5-7.5h-15"
138+ />
139+ </ svg >
140+ Follow
141+ </ button >
142+ </ a >
143+ </ div >
144+ < h1 className = "text-lg font-semibold mt-2" > { user . displayName } </ h1 >
145+ < p className = "text-sm text-zinc-500 dark:text-zinc-400" >
146+ @{ user . handle }
147+ </ p >
148+ < div className = "mt-3 flex items-center gap-3 text-sm font-semibold" >
149+ < p >
150+ { user . followersCount }
151+ < span className = "font-normal text-zinc-500 dark:text-zinc-400" >
152+ { ' ' }
153+ Followers
154+ </ span >
155+ </ p >
156+ < p >
157+ { user . followsCount }
158+ < span className = "font-normal text-zinc-500 dark:text-zinc-400" >
159+ { ' ' }
160+ Following
161+ </ span >
162+ </ p >
163+ </ div >
164+ < p className = "mt-4" > { user . description } </ p >
165+ < div className = "flex items-end justify-between" >
166+ < p className = "text-sm text-zinc-400 dark:text-zinc-500" >
167+ Made with Tsky
168+ </ p >
169+ < a
170+ href = "https://github.com/tsky-dev/tsky"
171+ target = "_blank"
172+ rel = "noreferrer"
173+ >
174+ < svg
175+ xmlns = "http://www.w3.org/2000/svg"
176+ fill = "none"
177+ viewBox = "0 0 320 286"
178+ className = "size-8"
179+ >
180+ < title > Bluesky Icon</ title >
181+ < path
182+ fill = "rgb(10,122,255)"
183+ d = "M69.364 19.146c36.687 27.806 76.147 84.186 90.636 114.439 14.489-30.253 53.948-86.633 90.636-114.439C277.107-.917 320-16.44 320 32.957c0 9.865-5.603 82.875-8.889 94.729-11.423 41.208-53.045 51.719-90.071 45.357 64.719 11.12 81.182 47.953 45.627 84.785-80 82.874-106.667-44.333-106.667-44.333s-26.667 127.207-106.667 44.333c-35.555-36.832-19.092-73.665 45.627-84.785-37.026 6.362-78.648-4.149-90.071-45.357C5.603 115.832 0 42.822 0 32.957 0-16.44 42.893-.917 69.364 19.147Z"
184+ />
185+ </ svg >
186+ </ a >
187+ </ div >
188+ </ div >
189+ </ div >
190+ ) ;
191+ }
0 commit comments