add online users avatar

This commit is contained in:
hjlarry 2025-09-06 11:19:47 +08:00
parent 20320f3a27
commit 2e11b1298e
2 changed files with 65 additions and 30 deletions

View File

@ -8,6 +8,7 @@ export type AvatarProps = {
size?: number
className?: string
textClassName?: string
backgroundColor?: string
}
const Avatar = ({
name,
@ -15,9 +16,18 @@ const Avatar = ({
size = 30,
className,
textClassName,
backgroundColor,
}: AvatarProps) => {
const avatarClassName = 'shrink-0 flex items-center rounded-full bg-primary-600'
const style = { width: `${size}px`, height: `${size}px`, fontSize: `${size}px`, lineHeight: `${size}px` }
const avatarClassName = backgroundColor
? 'shrink-0 flex items-center rounded-full'
: 'shrink-0 flex items-center rounded-full bg-primary-600'
const style = {
width: `${size}px`,
height: `${size}px`,
fontSize: `${size}px`,
lineHeight: `${size}px`,
...(backgroundColor && !avatar ? { backgroundColor } : {}),
}
const [imgError, setImgError] = useState(false)
const handleError = () => {

View File

@ -3,6 +3,16 @@ import Avatar from '@/app/components/base/avatar'
import { useCollaboration } from '../collaboration/hooks/use-collaboration'
import { useStore } from '../store'
import cn from '@/utils/classnames'
import { ChevronDown } from '@/app/components/base/icons/src/vender/solid/arrows'
const getUserColor = (id: string) => {
const colors = ['#3B82F6', '#EF4444', '#10B981', '#F59E0B', '#8B5CF6', '#EC4899', '#06B6D4', '#84CC16']
const hash = id.split('').reduce((a, b) => {
a = ((a << 5) - a) + b.charCodeAt(0)
return a & a
}, 0)
return colors[Math.abs(hash) % colors.length]
}
const OnlineUsers = () => {
const appId = useStore(s => s.appId)
@ -11,38 +21,53 @@ const OnlineUsers = () => {
if (!onlineUsers || onlineUsers.length === 0)
return null
// Show max 2 avatars directly, rest as count
const visibleUsers = onlineUsers.slice(0, 2)
const remainingCount = onlineUsers.length - 2
// Display logic:
// 1-3 users: show all avatars
// 4+ users: show 2 avatars + count + arrow
const shouldShowCount = onlineUsers.length >= 4
const maxVisible = shouldShowCount ? 2 : 3
const visibleUsers = onlineUsers.slice(0, maxVisible)
const remainingCount = onlineUsers.length - maxVisible
return (
<div className="flex items-center -space-x-2">
{visibleUsers.map((user, index) => (
<div
key={`${user.sid}-${index}`}
className="relative"
style={{ zIndex: visibleUsers.length - index }}
>
<Avatar
name={user.username || 'User'}
avatar={user.avatar}
size={28}
className="ring-2 ring-white"
/>
</div>
))}
{remainingCount > 0 && (
<div
className={cn(
'flex items-center justify-center',
'h-7 w-7 rounded-full bg-gray-300',
'text-xs font-medium text-gray-700',
'ring-2 ring-white',
<div className="flex items-center rounded-full bg-white px-1 py-1">
<div className="flex items-center">
<div className="flex items-center -space-x-2">
{visibleUsers.map((user, index) => {
const userColor = getUserColor(user.user_id)
return (
<div
key={`${user.sid}-${index}`}
className="relative"
style={{ zIndex: visibleUsers.length - index }}
>
<Avatar
name={user.username || 'User'}
avatar={user.avatar}
size={28}
className="ring-2 ring-white"
backgroundColor={userColor}
/>
</div>
)
})}
{remainingCount > 0 && (
<div
className={cn(
'flex h-7 w-7 items-center justify-center',
'rounded-full bg-gray-300',
'text-xs font-medium text-gray-700',
'ring-2 ring-white',
)}
>
+{remainingCount}
</div>
)}
>
+{remainingCount}
</div>
)}
{remainingCount > 0 && (
<ChevronDown className="ml-1 h-3 w-3 text-gray-500" />
)}
</div>
</div>
)
}