11import { Spring } from "@follow/components/constants/spring.js"
2- import { FollowIcon } from "@follow/components/icons/follow.jsx"
32import { Avatar , AvatarFallback , AvatarImage } from "@follow/components/ui/avatar/index.jsx"
43import { ActionButton , Button } from "@follow/components/ui/button/index.js"
54import { LoadingCircle } from "@follow/components/ui/loading/index.jsx"
@@ -229,102 +228,112 @@ export const UserProfileModalContent: FC<SubscriptionModalContentProps> = ({ use
229228type PickedUser = ReturnType < typeof pickUserData >
230229
231230const UserInfo = ( { userInfo } : { userInfo : PickedUser } ) => {
232- const { t } = useTranslation ( )
233231 const whoami = useWhoami ( )
234232 const follow = useFollow ( )
233+
234+ // It's a string value when it's from the store
235+ if ( typeof userInfo . socialLinks === "string" ) {
236+ userInfo . socialLinks = JSON . parse ( userInfo . socialLinks )
237+ }
238+
235239 return (
236- < div className = "border-fill bg-material-medium border-b p-6" >
237- < div className = "flex items-start gap-4" >
238- < Avatar className = "size-20 shrink-0" >
239- < AvatarImage
240- src = { replaceImgUrlIfNeed ( userInfo . image || undefined ) }
241- className = "bg-material-ultra-thick"
242- />
243- < AvatarFallback className = "text-3xl uppercase" >
244- { userInfo . name ?. slice ( 0 , 2 ) }
245- </ AvatarFallback >
246- </ Avatar >
247- < div className = "min-w-0 flex-1" >
248- < h1 className = "text-text text-2xl font-bold" > { userInfo . name } </ h1 >
249- < p
250- className = { cn (
251- "text-text-secondary mt-1 text-sm" ,
252- userInfo . handle ? "visible" : "hidden select-none" ,
240+ < div className = "border-fill from-material-medium to-material-thin relative flex flex-col border-r bg-gradient-to-br p-8" >
241+ < div className = "flex h-full" >
242+ < div className = "flex flex-1 flex-col gap-4" >
243+ < div className = "flex items-center gap-4" >
244+ < div className = "relative" >
245+ < Avatar className = "size-16 shrink-0 shadow-lg ring-4 ring-white/10" >
246+ < AvatarImage
247+ src = { replaceImgUrlIfNeed ( userInfo . image || undefined ) }
248+ className = "bg-material-ultra-thick"
249+ />
250+ < AvatarFallback className = "from-blue to-purple bg-gradient-to-br text-4xl font-bold uppercase text-white" >
251+ { userInfo . name ?. slice ( 0 , 2 ) }
252+ </ AvatarFallback >
253+ </ Avatar >
254+
255+ { whoami ?. id !== userInfo . id && (
256+ < Button
257+ buttonClassName = "absolute -bottom-1 -right-1 size-6 rounded-full"
258+ onClick = { ( ) => {
259+ follow ( {
260+ url : `rsshub://follow/profile/${ userInfo . id } ` ,
261+ isList : false ,
262+ } )
263+ } }
264+ size = "sm"
265+ >
266+ < i className = "i-mgc-add-cute-re size-4" />
267+ </ Button >
268+ ) }
269+ </ div >
270+ < div className = "flex-1" >
271+ < h1 className = "text-text max-w-[200px] truncate text-xl font-bold tracking-tight" >
272+ { userInfo . name }
273+ </ h1 >
274+ < p
275+ className = { cn (
276+ "text-text-secondary text-sm font-medium" ,
277+ userInfo . handle ? "visible" : "hidden select-none" ,
278+ ) }
279+ >
280+ @{ userInfo . handle }
281+ </ p >
282+ </ div >
283+ </ div >
284+
285+ < div className = "space-y-2" >
286+ { userInfo . bio && (
287+ < p className = "text-text-secondary text-sm leading-relaxed" > { userInfo . bio } </ p >
253288 ) }
254- >
255- @{ userInfo . handle }
256- </ p >
257- { whoami ?. id !== userInfo . id && (
258- < Button
259- onClick = { ( ) => {
260- follow ( {
261- url : `rsshub://follow/profile/${ userInfo . id } ` ,
262- isList : false ,
263- } )
264- } }
265- buttonClassName = "mt-3"
266- size = "sm"
267- >
268- < FollowIcon className = "mr-1 size-3" />
269- { t ( "feed_form.follow" ) }
270- </ Button >
271- ) }
289+ { userInfo . website && (
290+ < a
291+ href = { userInfo . website }
292+ target = "_blank"
293+ rel = "noopener noreferrer"
294+ className = "text-text-secondary hover:text-accent group inline-flex items-center gap-2 text-sm leading-relaxed transition-colors"
295+ >
296+ < span className = "truncate" > { userInfo . website . replace ( / ^ h t t p s ? : \/ \/ / , "" ) } </ span >
297+ < i className = "i-mgc-external-link-cute-re size-3 opacity-60 transition-opacity group-hover:opacity-100" />
298+ </ a >
299+ ) }
300+ </ div >
272301 </ div >
273- </ div >
274- </ div >
275- )
276- }
277302
278- const UserSocialSection = ( { user } : { user : PickedUser } ) => {
279- const { t } = useTranslation ( )
280- if ( ! user || ( ! user . bio && ! user . website && ! user . socialLinks ) ) return null
281-
282- return (
283- < div className = "border-fill bg-material-thin border-b px-6 py-4" >
284- < h3 className = "text-text mb-3 text-sm font-medium uppercase tracking-wide" >
285- { t ( "user_profile.about" ) }
286- </ h3 >
287- { user . bio && < p className = "text-text-secondary mb-3 text-sm leading-relaxed" > { user . bio } </ p > }
288- < div className = "space-y-2" >
289- { user . website && (
290- < a
291- href = { user . website }
292- target = "_blank"
293- rel = "noopener noreferrer"
294- className = "text-accent hover:text-accent/80 inline-flex items-center gap-2 text-sm transition-colors"
295- >
296- < i className = "i-mgc-link-cute-re text-base" />
297- < span className = "truncate" > { user . website . replace ( / ^ h t t p s ? : \/ \/ / , "" ) } </ span >
298- </ a >
299- ) }
300- { user . socialLinks && (
301- < div className = "flex flex-wrap items-center gap-3" >
302- { Object . entries ( user . socialLinks ) . map ( ( [ platform , id ] ) => {
303- if ( ! id || ! ( platform in socialIconClassNames ) ) return null
303+ { userInfo . socialLinks && Object . values ( userInfo . socialLinks ) . filter ( Boolean ) . length > 0 && (
304+ < div className = "mt-auto space-y-3" >
305+ < p className = "text-text-secondary text-xs font-medium uppercase tracking-wide" >
306+ Social Media
307+ </ p >
308+ < div className = "flex flex-wrap gap-2" >
309+ { Object . entries ( userInfo . socialLinks ) . map ( ( [ platform , id ] ) => {
310+ if ( ! id || ! ( platform in socialIconClassNames ) || typeof id !== "string" )
311+ return null
304312
305- return (
306- < Tooltip key = { platform } >
307- < TooltipTrigger asChild >
308- < a
309- href = { getSocialLink ( platform as keyof typeof socialIconClassNames , id ) }
310- target = "_blank"
311- rel = "noopener noreferrer"
312- className = "text-text-secondary hover:text-accent group flex items-center justify-center rounded-full transition-colors"
313- >
314- < i
315- className = { cn (
316- socialIconClassNames [ platform as keyof typeof socialIconClassNames ] ,
317- "text-lg" ,
318- ) }
319- />
320- </ a >
321- </ TooltipTrigger >
322- < TooltipContent className = "text-sm" >
323- { socialCopyMap [ platform as keyof typeof socialCopyMap ] }
324- </ TooltipContent >
325- </ Tooltip >
326- )
327- } ) }
313+ return (
314+ < Tooltip key = { platform } >
315+ < TooltipTrigger asChild >
316+ < a
317+ href = { getSocialLink ( platform as keyof typeof socialIconClassNames , id ) }
318+ target = "_blank"
319+ rel = "noopener noreferrer"
320+ className = "bg-material-ultra-thin/50 hover:bg-accent/10 group flex size-10 items-center justify-center rounded-lg backdrop-blur-sm transition-all duration-200 hover:scale-110"
321+ >
322+ < i
323+ className = { cn (
324+ socialIconClassNames [ platform as keyof typeof socialIconClassNames ] ,
325+ "text-text-secondary group-hover:text-accent text-base transition-colors" ,
326+ ) }
327+ />
328+ </ a >
329+ </ TooltipTrigger >
330+ < TooltipContent className = "text-xs font-medium" >
331+ { socialCopyMap [ platform as keyof typeof socialCopyMap ] }
332+ </ TooltipContent >
333+ </ Tooltip >
334+ )
335+ } ) }
336+ </ div >
328337 </ div >
329338 ) }
330339 </ div >
@@ -338,7 +347,7 @@ const Subscriptions = ({ userId }: { userId: string }) => {
338347
339348 return (
340349 < >
341- < div className = "-mb-4 mt-4 flex items-center justify-between" >
350+ < div className = "-mb-4 flex items-center justify-between" >
342351 < h2 className = "text-text text-lg font-semibold" > { t ( "user_profile.subscriptions" ) } </ h2 >
343352 < ActionButton
344353 tooltip = { t ( "user_profile.toggle_item_style" ) }
@@ -394,12 +403,11 @@ const Content = ({ userInfo }: { userInfo: PickedUser }) => {
394403 < Fragment >
395404 < UserInfo userInfo = { userInfo } />
396405 < ScrollArea . ScrollArea
397- rootClassName = "grow max-w-full w-full"
406+ rootClassName = "grow max-w-full w-full bg-material-ultra-thin/30 "
398407 viewportClassName = "[&>div]:!flex [&>div]:flex-col"
399408 >
400- < UserSocialSection user = { userInfo } />
401409 { ! lists . isLoading && (
402- < div className = "@container flex flex-col px-6 py-4 " >
410+ < div className = "@container flex flex-col space-y-4 px-8 py-6 " >
403411 { /* Lists Section */ }
404412 { lists . data && lists . data . length > 0 && < Lists lists = { lists . data } /> }
405413 { /* Subscriptions Section */ }
0 commit comments