Version Bump 1.0.40
All checks were successful
Build and Release / build-and-release (push) Successful in 19m24s
All checks were successful
Build and Release / build-and-release (push) Successful in 19m24s
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@discord-clone/shared",
|
||||
"private": true,
|
||||
"version": "1.0.39",
|
||||
"version": "1.0.40",
|
||||
"type": "module",
|
||||
"main": "src/App.jsx",
|
||||
"dependencies": {
|
||||
|
||||
@@ -493,27 +493,47 @@ const ServerSettingsModal = ({ onClose }) => {
|
||||
</div>
|
||||
);
|
||||
|
||||
const isCurrentUserAdmin = members.find(m => m.id === userId)?.roles?.some(r => r.name === 'Owner');
|
||||
|
||||
const handleDeleteUser = async (targetUserId, targetUsername) => {
|
||||
if (!confirm(`Are you sure you want to delete "${targetUsername}" and ALL their messages? This cannot be undone.`)) return;
|
||||
try {
|
||||
const result = await convex.mutation(api.auth.deleteUser, {
|
||||
requestingUserId: userId,
|
||||
targetUserId,
|
||||
});
|
||||
if (!result.success) {
|
||||
alert(result.error || 'Failed to delete user.');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Delete user error:', e);
|
||||
alert('Failed to delete user. See console.');
|
||||
}
|
||||
};
|
||||
|
||||
const renderMembersTab = () => (
|
||||
<div>
|
||||
<h2 style={{ color: 'var(--header-primary)' }}>Members</h2>
|
||||
{members.map(m => (
|
||||
<div key={m.id} style={{ display: 'flex', alignItems: 'center', padding: '10px', borderBottom: '1px solid var(--border-subtle)' }}>
|
||||
<div style={{ width: 32, height: 32, borderRadius: '50%', background: '#5865F2', marginRight: 10, display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--header-primary)' }}>
|
||||
{m.username[0].toUpperCase()}
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ color: 'var(--header-primary)', fontWeight: 'bold' }}>{m.username}</div>
|
||||
<div style={{ display: 'flex', gap: 4 }}>
|
||||
{m.roles?.map(r => (
|
||||
<span key={r._id} style={{ fontSize: 10, background: r.color, color: 'var(--header-primary)', padding: '2px 4px', borderRadius: 4 }}>
|
||||
{r.name}
|
||||
</span>
|
||||
))}
|
||||
{members.map(m => {
|
||||
const isOwner = m.roles?.some(r => r.name === 'Owner');
|
||||
const isSelf = m.id === userId;
|
||||
return (
|
||||
<div key={m.id} style={{ display: 'flex', alignItems: 'center', padding: '10px', borderBottom: '1px solid var(--border-subtle)' }}>
|
||||
<div style={{ width: 32, height: 32, borderRadius: '50%', background: '#5865F2', marginRight: 10, display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--header-primary)' }}>
|
||||
{m.username[0].toUpperCase()}
|
||||
</div>
|
||||
</div>
|
||||
{canManageRoles && (
|
||||
<div style={{ display: 'flex', gap: 4 }}>
|
||||
{editableRoles.map(r => {
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ color: 'var(--header-primary)', fontWeight: 'bold' }}>{m.username}</div>
|
||||
<div style={{ display: 'flex', gap: 4 }}>
|
||||
{m.roles?.map(r => (
|
||||
<span key={r._id} style={{ fontSize: 10, background: r.color, color: 'var(--header-primary)', padding: '2px 4px', borderRadius: 4 }}>
|
||||
{r.name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
|
||||
{canManageRoles && editableRoles.map(r => {
|
||||
const hasRole = m.roles?.some(ur => ur._id === r._id);
|
||||
return (
|
||||
<button
|
||||
@@ -529,10 +549,28 @@ const ServerSettingsModal = ({ onClose }) => {
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{isCurrentUserAdmin && !isSelf && !isOwner && (
|
||||
<button
|
||||
onClick={() => handleDeleteUser(m.id, m.username)}
|
||||
style={{
|
||||
background: 'transparent', border: 'none', color: 'var(--header-secondary)',
|
||||
cursor: 'pointer', fontSize: 14, padding: '4px 8px', marginLeft: 8,
|
||||
borderRadius: 4, opacity: 0.5, transition: 'opacity 0.15s, color 0.15s',
|
||||
}}
|
||||
onMouseEnter={(e) => { e.currentTarget.style.opacity = '1'; e.currentTarget.style.color = '#ed4245'; }}
|
||||
onMouseLeave={(e) => { e.currentTarget.style.opacity = '0.5'; e.currentTarget.style.color = 'var(--header-secondary)'; }}
|
||||
title={`Delete ${m.username}`}
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M15 3.999V2H9V3.999H3V5.999H21V3.999H15Z" />
|
||||
<path d="M5 6.99902V18.999C5 20.101 5.897 20.999 7 20.999H17C18.103 20.999 19 20.101 19 18.999V6.99902H5ZM11 17H9V11H11V17ZM15 17H13V11H15V17Z" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
@@ -1296,18 +1296,17 @@ const Sidebar = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const generalChannel = channels.find((c) => c.name === "general");
|
||||
const targetChannelId = generalChannel ? generalChannel._id : activeChannel;
|
||||
|
||||
if (!targetChannelId) {
|
||||
alert("No channel selected.");
|
||||
return;
|
||||
// Bundle all server channel keys (not DM keys) so new users get access to everything
|
||||
const serverChannelIds = new Set(channels.map((c) => c._id));
|
||||
const allServerKeys = {};
|
||||
for (const [chId, key] of Object.entries(channelKeys || {})) {
|
||||
if (serverChannelIds.has(chId)) {
|
||||
allServerKeys[chId] = key;
|
||||
}
|
||||
}
|
||||
|
||||
const targetKey = channelKeys?.[targetChannelId];
|
||||
|
||||
if (!targetKey) {
|
||||
alert("Error: You don't have the key for this channel yet, so you can't invite others.");
|
||||
if (Object.keys(allServerKeys).length === 0) {
|
||||
alert("Error: You don't have any channel keys to share.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1315,7 +1314,7 @@ const Sidebar = ({
|
||||
const inviteCode = globalThis.crypto.randomUUID();
|
||||
const inviteSecret = randomHex(32);
|
||||
|
||||
const payload = JSON.stringify({ [targetChannelId]: targetKey });
|
||||
const payload = JSON.stringify(allServerKeys);
|
||||
const encrypted = await crypto.encryptData(payload, inviteSecret);
|
||||
const blob = JSON.stringify({ c: encrypted.content, t: encrypted.tag, iv: encrypted.iv });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user