optimizations
This commit is contained in:
101
resources/js/components/social/SuggestedUsersWidget.jsx
Normal file
101
resources/js/components/social/SuggestedUsersWidget.jsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import axios from 'axios'
|
||||
import FollowButton from './FollowButton'
|
||||
|
||||
export default function SuggestedUsersWidget({
|
||||
title = 'Suggested Users',
|
||||
limit = 4,
|
||||
isLoggedIn = false,
|
||||
excludeUsername = null,
|
||||
initialUsers = null,
|
||||
}) {
|
||||
const [users, setUsers] = useState(Array.isArray(initialUsers) ? initialUsers : [])
|
||||
const [loading, setLoading] = useState(!Array.isArray(initialUsers) && isLoggedIn)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoggedIn || Array.isArray(initialUsers)) {
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
let cancelled = false
|
||||
|
||||
axios.get('/api/users/suggestions', { params: { limit } })
|
||||
.then(({ data }) => {
|
||||
if (cancelled) return
|
||||
const nextUsers = Array.isArray(data?.data) ? data.data : []
|
||||
setUsers(nextUsers)
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) setUsers([])
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) setLoading(false)
|
||||
})
|
||||
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [initialUsers, isLoggedIn, limit])
|
||||
|
||||
const visibleUsers = users
|
||||
.filter((user) => user?.username && user.username !== excludeUsername)
|
||||
.slice(0, limit)
|
||||
|
||||
if (!isLoggedIn) return null
|
||||
if (!loading && visibleUsers.length === 0) return null
|
||||
|
||||
return (
|
||||
<div className="rounded-2xl border border-white/[0.07] bg-white/[0.03] overflow-hidden">
|
||||
<div className="flex items-center gap-2 px-4 py-3 border-b border-white/[0.05]">
|
||||
<i className="fa-solid fa-compass text-slate-500 fa-fw text-[13px]" />
|
||||
<span className="text-[11px] font-semibold uppercase tracking-widest text-slate-500">{title}</span>
|
||||
</div>
|
||||
|
||||
<div className="px-4 py-3 space-y-3">
|
||||
{loading ? [1, 2, 3].map((key) => (
|
||||
<div key={key} className="animate-pulse flex items-center gap-3">
|
||||
<div className="h-10 w-10 rounded-full bg-white/10" />
|
||||
<div className="min-w-0 flex-1 space-y-2">
|
||||
<div className="h-2.5 w-24 rounded bg-white/10" />
|
||||
<div className="h-2 w-32 rounded bg-white/6" />
|
||||
</div>
|
||||
</div>
|
||||
)) : visibleUsers.map((user) => (
|
||||
<div key={user.id} className="rounded-2xl border border-white/[0.05] bg-white/[0.03] p-3">
|
||||
<div className="flex items-start gap-3">
|
||||
<a href={user.profile_url || `/@${user.username}`} className="shrink-0">
|
||||
<img
|
||||
src={user.avatar_url || '/images/avatar_default.webp'}
|
||||
alt={user.username}
|
||||
className="h-10 w-10 rounded-full object-cover ring-1 ring-white/10"
|
||||
loading="lazy"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<div className="min-w-0 flex-1">
|
||||
<a href={user.profile_url || `/@${user.username}`} className="block truncate text-sm font-semibold text-white/90 hover:text-white">
|
||||
{user.name || user.username}
|
||||
</a>
|
||||
<p className="truncate text-xs text-slate-500">@{user.username}</p>
|
||||
<p className="mt-1 text-xs text-slate-400">{user.context?.follower_overlap?.label || user.context?.shared_following?.label || user.reason}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 flex items-center justify-between gap-3">
|
||||
<span className="text-[11px] text-slate-500">{Number(user.followers_count || 0).toLocaleString()} followers</span>
|
||||
<FollowButton
|
||||
username={user.username}
|
||||
initialFollowing={false}
|
||||
initialCount={Number(user.followers_count || 0)}
|
||||
showCount={false}
|
||||
sizeClassName="px-3 py-1.5 text-xs"
|
||||
className="min-w-[110px]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user