fix: channel/group rename now works
- Input field no longer nested inside a button/clickable div - Snapshot state before clearing editing mode to avoid stale closures - Log errors instead of silently swallowing them
This commit is contained in:
@@ -190,15 +190,19 @@ export function ChannelList() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const commitEditChannel = async () => {
|
const commitEditChannel = async () => {
|
||||||
if (!editingChannelId || !editingChannelName.trim()) { setEditingChannelId(null); return; }
|
const id = editingChannelId;
|
||||||
|
const name = editingChannelName.trim();
|
||||||
|
setEditingChannelId(null);
|
||||||
|
if (!id || !name) return;
|
||||||
try {
|
try {
|
||||||
const updated = await api.patch<{ id: string; server_id: string; name: string; type: string; category: string | null; position: number; group_id?: string | null }>(
|
const updated = await api.patch<{ id: string; server_id: string; name: string; type: string; category: string | null; position: number; group_id?: string | null }>(
|
||||||
`/servers/${activeServerId}/channels/${editingChannelId}`,
|
`/servers/${activeServerId}/channels/${id}`,
|
||||||
{ name: editingChannelName.trim() }
|
{ name }
|
||||||
);
|
);
|
||||||
updateChannel(updated as any);
|
updateChannel(updated as any);
|
||||||
} catch { /* ignore */ }
|
} catch (err) {
|
||||||
setEditingChannelId(null);
|
console.error('Rename channel failed:', err);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteChannel = async (channelId: string, channelName: string) => {
|
const deleteChannel = async (channelId: string, channelName: string) => {
|
||||||
@@ -224,12 +228,16 @@ export function ChannelList() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const commitEditGroup = async () => {
|
const commitEditGroup = async () => {
|
||||||
if (!editingGroupId || !editingGroupName.trim()) { setEditingGroupId(null); return; }
|
const id = editingGroupId;
|
||||||
try {
|
const name = editingGroupName.trim();
|
||||||
await api.patch(`/servers/${activeServerId}/groups/${editingGroupId}`, { name: editingGroupName.trim() });
|
|
||||||
refreshGroups();
|
|
||||||
} catch { /* ignore */ }
|
|
||||||
setEditingGroupId(null);
|
setEditingGroupId(null);
|
||||||
|
if (!id || !name) return;
|
||||||
|
try {
|
||||||
|
await api.patch(`/servers/${activeServerId}/groups/${id}`, { name });
|
||||||
|
refreshGroups();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Rename group failed:', err);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteGroup = async (groupId: string, groupName: string) => {
|
const deleteGroup = async (groupId: string, groupName: string) => {
|
||||||
@@ -253,6 +261,19 @@ export function ChannelList() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={channel.id} className="group" onContextMenu={(e) => handleChannelContextMenu(e, channel)}>
|
<div key={channel.id} className="group" onContextMenu={(e) => handleChannelContextMenu(e, channel)}>
|
||||||
|
{isEditing ? (
|
||||||
|
<div className="w-full text-left px-2 py-1 rounded-sm flex items-center gap-2">
|
||||||
|
<span className="text-gb-fg-f">#</span>
|
||||||
|
<input
|
||||||
|
autoFocus
|
||||||
|
value={editingChannelName}
|
||||||
|
onChange={(e) => setEditingChannelName(e.target.value)}
|
||||||
|
onBlur={commitEditChannel}
|
||||||
|
onKeyDown={(e) => { if (e.key === 'Enter') commitEditChannel(); if (e.key === 'Escape') setEditingChannelId(null); }}
|
||||||
|
className="flex-1 bg-gb-bg-t text-gb-fg px-1 py-0.5 text-sm outline-none border border-gb-orange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveChannel(channel.id)}
|
onClick={() => setActiveChannel(channel.id)}
|
||||||
className={`w-full text-left px-2 py-1 rounded-sm flex items-center gap-2 ${
|
className={`w-full text-left px-2 py-1 rounded-sm flex items-center gap-2 ${
|
||||||
@@ -262,19 +283,7 @@ export function ChannelList() {
|
|||||||
<span className="text-gb-fg-f">
|
<span className="text-gb-fg-f">
|
||||||
{channel.type === 'forum' ? '■' : channel.type === 'calendar' ? '○' : channel.type === 'docs' ? '☰' : channel.type === 'list' ? '☑' : '#'}
|
{channel.type === 'forum' ? '■' : channel.type === 'calendar' ? '○' : channel.type === 'docs' ? '☰' : channel.type === 'list' ? '☑' : '#'}
|
||||||
</span>
|
</span>
|
||||||
{isEditing ? (
|
|
||||||
<input
|
|
||||||
autoFocus
|
|
||||||
value={editingChannelName}
|
|
||||||
onChange={(e) => setEditingChannelName(e.target.value)}
|
|
||||||
onBlur={commitEditChannel}
|
|
||||||
onKeyDown={(e) => { if (e.key === 'Enter') commitEditChannel(); if (e.key === 'Escape') setEditingChannelId(null); }}
|
|
||||||
className="flex-1 bg-gb-bg-t text-gb-fg px-1 py-0.5 text-sm outline-none border border-gb-orange"
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<span className="truncate flex-1">{channel.name}</span>
|
<span className="truncate flex-1">{channel.name}</span>
|
||||||
)}
|
|
||||||
{hasUnread(channel.id) && (
|
{hasUnread(channel.id) && (
|
||||||
<span className="text-gb-orange text-xs font-bold" title="Unread messages">●</span>
|
<span className="text-gb-orange text-xs font-bold" title="Unread messages">●</span>
|
||||||
)}
|
)}
|
||||||
@@ -297,6 +306,7 @@ export function ChannelList() {
|
|||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -335,13 +345,9 @@ export function ChannelList() {
|
|||||||
const isEditingGroup = editingGroupId === grp.id;
|
const isEditingGroup = editingGroupId === grp.id;
|
||||||
return (
|
return (
|
||||||
<div key={'grp:' + grp.id} className="mb-3">
|
<div key={'grp:' + grp.id} className="mb-3">
|
||||||
<div
|
|
||||||
className="text-gb-fg-t text-xs uppercase mb-1 cursor-pointer select-none hover:text-gb-fg flex items-center"
|
|
||||||
onClick={() => { toggleCollapsed(activeServerId || '', grp.id); setToggleTick(t => t + 1); }}
|
|
||||||
onContextMenu={(e) => handleGroupContextMenu(e, grp)}
|
|
||||||
>
|
|
||||||
<span className="mr-1">{collapsed ? '[+]' : '[-]'}</span>
|
|
||||||
{isEditingGroup ? (
|
{isEditingGroup ? (
|
||||||
|
<div className="text-gb-fg-t text-xs uppercase mb-1 flex items-center">
|
||||||
|
<span className="mr-1">{collapsed ? '[+]' : '[-]'}</span>
|
||||||
<input
|
<input
|
||||||
autoFocus
|
autoFocus
|
||||||
value={editingGroupName}
|
value={editingGroupName}
|
||||||
@@ -349,12 +355,18 @@ export function ChannelList() {
|
|||||||
onBlur={commitEditGroup}
|
onBlur={commitEditGroup}
|
||||||
onKeyDown={(e) => { if (e.key === 'Enter') commitEditGroup(); if (e.key === 'Escape') setEditingGroupId(null); }}
|
onKeyDown={(e) => { if (e.key === 'Enter') commitEditGroup(); if (e.key === 'Escape') setEditingGroupId(null); }}
|
||||||
className="flex-1 bg-gb-bg-t text-gb-fg px-1 py-0.5 text-xs outline-none border border-gb-orange"
|
className="flex-1 bg-gb-bg-t text-gb-fg px-1 py-0.5 text-xs outline-none border border-gb-orange"
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
|
||||||
<span>{grp.name}</span>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
className="text-gb-fg-t text-xs uppercase mb-1 cursor-pointer select-none hover:text-gb-fg flex items-center"
|
||||||
|
onClick={() => { toggleCollapsed(activeServerId || '', grp.id); setToggleTick(t => t + 1); }}
|
||||||
|
onContextMenu={(e) => handleGroupContextMenu(e, grp)}
|
||||||
|
>
|
||||||
|
<span className="mr-1">{collapsed ? '[+]' : '[-]'}</span>
|
||||||
|
<span>{grp.name}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
<>
|
<>
|
||||||
<div className="text-gb-fg-f text-xs mb-1">---</div>
|
<div className="text-gb-fg-f text-xs mb-1">---</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user