Peercord/Peercord Source/src/components/OnlineUsers.jsx
0% [█ █ █ █ █ █ █ █ █ █] 100% 29e61f07f2 Full source
2026-06-14 21:28:04 -05:00

111 lines
5.3 KiB
JavaScript

import React from 'react';
import { network, ADMIN_PUBLIC_KEY } from '../p2p/index.js';
export default function OnlineUsers({ onlinePeers, knownUsers, dms, myKey, profile, activeView, servers, serverMembers, onClose }) {
const handleSendRequest = (e, peer) => {
e.stopPropagation();
network.sendDMRequest(peer.key, { displayName: peer.displayName, username: peer.username, avatar: peer.avatar });
};
const isCustomServer = activeView !== 'dms';
const serverObj = isCustomServer ? servers.find(s => s.topicHex === activeView) : null;
const isGroupChat = serverObj?.isGroupChat;
const currentMembers = isCustomServer ? new Set(serverMembers[activeView] ||[]) : null;
if (isCustomServer && serverObj) {
currentMembers.add(serverObj.owner);
currentMembers.add(myKey);
}
const me = { key: myKey, displayName: profile.displayName, username: profile.username, avatar: profile.avatar };
const allOnlinePeers = [me, ...onlinePeers];
const filteredOnlinePeers = isCustomServer ? allOnlinePeers.filter(p => currentMembers.has(p.key)) : allOnlinePeers;
const onlineKeys = new Set(filteredOnlinePeers.map(p => p.key));
const offlineUsers =[];
if (isCustomServer && currentMembers) {
currentMembers.forEach(key => {
if (!onlineKeys.has(key) && key !== myKey) {
const known = knownUsers.find(u => u.key === key);
if (known) offlineUsers.push(known);
else offlineUsers.push({ key, displayName: 'Unknown User', username: 'unknown', avatar: null });
}
});
} else {
offlineUsers.push(...knownUsers.filter(u => !onlineKeys.has(u.key) && u.key !== myKey));
}
const renderUser = (peer, isOnline) => {
const dmState = dms[peer.key]?.status;
let isPlatformAdmin = peer.key === ADMIN_PUBLIC_KEY;
let isServerOwner = isCustomServer && !isGroupChat && serverObj?.owner === peer.key;
let isGroupCreator = isGroupChat && serverObj?.owner === peer.key;
return (
<div key={peer.key} className={`flex items-center justify-between group cursor-pointer hover:bg-panel p-2 rounded ${!isOnline ? 'opacity-60 hover:opacity-100' : ''}`}>
<div className="flex items-center gap-3 overflow-hidden">
<div className="relative shrink-0 w-8 h-8">
<div className={`w-full h-full rounded-md flex items-center justify-center text-white text-xs font-bold overflow-hidden ${peer.avatar ? 'bg-transparent' : 'bg-indigo-500'}`}>
{peer.avatar ? (
<img src={peer.avatar} alt="avatar" className="w-full h-full object-cover" />
) : (
peer.displayName.substring(0, 2).toUpperCase()
)}
</div>
<div className={`absolute -bottom-1 -right-1 w-3.5 h-3.5 rounded-full border-[3px] border-surface ${isOnline ? 'bg-green-500' : 'bg-gray-500'}`}></div>
</div>
<div className="flex flex-col overflow-hidden">
<span className="text-text text-sm truncate flex items-center gap-1">
{peer.displayName} {peer.key === myKey && <span className="text-muted text-xs ml-1">(You)</span>}
{isPlatformAdmin && <span title="Platform Admin" className="text-yellow-500 ml-1">👑</span>}
{isServerOwner && <span title="Hub Owner" className="text-yellow-500 ml-1">👑</span>}
{isGroupCreator && <span title="Group Creator" className="text-muted ml-1">👑</span>}
</span>
<span className="text-muted text-[10px] truncate">@{peer.username}</span>
</div>
</div>
{!dmState && peer.key !== myKey && (
<button
onClick={(e) => handleSendRequest(e, peer)}
className="opacity-0 group-hover:opacity-100 bg-accent hover:opacity-90 text-white p-1.5 rounded-full transition-all shrink-0"
title="Send Contact Request"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H5c-2.2 0-4 1.8-4 4v2"></path><circle cx="8.5" cy="7" r="4"></circle><line x1="20" y1="8" x2="20" y2="14"></line><line x1="23" y1="11" x2="17" y2="11"></line></svg>
</button>
)}
</div>
);
};
return (
<div className="w-full h-full flex flex-col p-4 overflow-y-auto">
<div className="flex justify-between items-center mb-4">
<div className="text-xs font-bold text-muted uppercase">
{isGroupChat ? 'Members' : 'Online'} {filteredOnlinePeers.length}
</div>
<button onClick={onClose} className="text-muted hover:text-text">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
</button>
</div>
<div className="space-y-1 mb-6">
{filteredOnlinePeers.map(peer => renderUser(peer, true))}
</div>
{offlineUsers.length > 0 && (
<>
<div className="text-xs font-bold text-muted uppercase mb-2">
Offline {offlineUsers.length}
</div>
<div className="space-y-1">
{offlineUsers.map(peer => renderUser(peer, false))}
</div>
</>
)}
</div>
);
}