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:
2026-07-02 08:25:11 -04:00
parent 31cb295a24
commit d0da35e816
+44 -32
View File
@@ -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>