added way to delete channels
This commit is contained in:
parent
a90a9f0a0d
commit
4ae6004239
21
db.ts
21
db.ts
|
|
@ -386,15 +386,20 @@ export function deleteChannelFromDb(id: string): void {
|
|||
|
||||
// Queue persistence functions
|
||||
export function saveChannelQueue(channelId: string, trackIds: string[]): void {
|
||||
// Delete existing queue
|
||||
db.query("DELETE FROM channel_queue WHERE channel_id = ?").run(channelId);
|
||||
db.query("BEGIN").run();
|
||||
try {
|
||||
db.query("DELETE FROM channel_queue WHERE channel_id = ?").run(channelId);
|
||||
|
||||
// Insert new queue
|
||||
const insert = db.query(
|
||||
"INSERT INTO channel_queue (channel_id, track_id, position) VALUES (?, ?, ?)"
|
||||
);
|
||||
for (let i = 0; i < trackIds.length; i++) {
|
||||
insert.run(channelId, trackIds[i], i);
|
||||
const insert = db.query(
|
||||
"INSERT INTO channel_queue (channel_id, track_id, position) VALUES (?, ?, ?)"
|
||||
);
|
||||
for (let i = 0; i < trackIds.length; i++) {
|
||||
insert.run(channelId, trackIds[i], i);
|
||||
}
|
||||
db.query("COMMIT").run();
|
||||
} catch (e) {
|
||||
db.query("ROLLBACK").run();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,28 @@
|
|||
}
|
||||
};
|
||||
|
||||
// Delete a channel
|
||||
M.deleteChannel = async function(channelId) {
|
||||
const channel = M.channels?.find(c => c.id === channelId);
|
||||
if (!channel) return;
|
||||
if (channel.isDefault) {
|
||||
M.showToast("Cannot delete default channel");
|
||||
return;
|
||||
}
|
||||
if (!confirm(`Delete channel "${channel.name}"?`)) return;
|
||||
try {
|
||||
const res = await fetch(`/api/channels/${channelId}`, { method: "DELETE" });
|
||||
if (!res.ok) {
|
||||
const err = await res.json();
|
||||
M.showToast(err.error || "Failed to delete channel");
|
||||
return;
|
||||
}
|
||||
M.showToast(`Channel "${channel.name}" deleted`);
|
||||
} catch (e) {
|
||||
M.showToast("Failed to delete channel");
|
||||
}
|
||||
};
|
||||
|
||||
// New channel creation with slideout input
|
||||
M.createNewChannel = async function() {
|
||||
const header = M.$("#channels-panel .panel-header");
|
||||
|
|
@ -129,14 +151,29 @@
|
|||
const listenersHtml = Object.entries(counts).map(([name, count]) =>
|
||||
`<div class="listener">${name}${count > 1 ? ` <span class="listener-mult">x${count}</span>` : ""}</div>`
|
||||
).join("");
|
||||
|
||||
// Show delete button for non-default channels if user is admin or creator
|
||||
const canDelete = !ch.isDefault && M.currentUser &&
|
||||
(M.currentUser.isAdmin || ch.createdBy === M.currentUser.id);
|
||||
const deleteBtn = canDelete ? `<button class="btn-delete-channel" title="Delete channel">×</button>` : "";
|
||||
|
||||
div.innerHTML = `
|
||||
<div class="channel-header">
|
||||
<span class="channel-name">${ch.name}</span>
|
||||
${deleteBtn}
|
||||
<span class="listener-count">${ch.listenerCount}</span>
|
||||
</div>
|
||||
<div class="channel-listeners">${listenersHtml}</div>
|
||||
`;
|
||||
div.querySelector(".channel-header").onclick = () => M.switchChannel(ch.id);
|
||||
const headerEl = div.querySelector(".channel-header");
|
||||
headerEl.querySelector(".channel-name").onclick = () => M.switchChannel(ch.id);
|
||||
const delBtn = headerEl.querySelector(".btn-delete-channel");
|
||||
if (delBtn) {
|
||||
delBtn.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
M.deleteChannel(ch.id);
|
||||
};
|
||||
}
|
||||
container.appendChild(div);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ h3 { font-size: 0.8rem; color: #666; margin-bottom: 0.3rem; text-transform: uppe
|
|||
#channels-list .channel-header:hover { background: #222; }
|
||||
#channels-list .channel-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.8rem; }
|
||||
#channels-list .listener-count { font-size: 0.65rem; color: #666; flex-shrink: 0; margin-left: 0.3rem; }
|
||||
#channels-list .btn-delete-channel { background: none; border: none; color: #666; font-size: 0.9rem; cursor: pointer; padding: 0 0.2rem; line-height: 1; opacity: 0; transition: opacity 0.15s; }
|
||||
#channels-list .channel-header:hover .btn-delete-channel { opacity: 1; }
|
||||
#channels-list .btn-delete-channel:hover { color: #e44; }
|
||||
#channels-list .channel-listeners { display: flex; flex-direction: column; margin-left: 0.5rem; border-left: 1px solid #333; padding-left: 0.3rem; }
|
||||
#channels-list .listener { font-size: 0.65rem; color: #aaa; padding: 0.05rem 0; position: relative; }
|
||||
#channels-list .listener::before { content: ""; position: absolute; left: -0.3rem; top: 50%; width: 0.2rem; height: 1px; background: #333; }
|
||||
|
|
|
|||
12
server.ts
12
server.ts
|
|
@ -475,6 +475,18 @@ serve({
|
|||
if (!user.is_admin && channel.createdBy !== user.id) {
|
||||
return Response.json({ error: "Access denied" }, { status: 403 });
|
||||
}
|
||||
|
||||
// Move connected clients to default channel before deleting
|
||||
const defaultChannel = [...channels.values()].find(c => c.isDefault);
|
||||
if (defaultChannel && channel.clients.size > 0) {
|
||||
for (const ws of channel.clients) {
|
||||
channel.removeClient(ws);
|
||||
ws.data.channelId = defaultChannel.id;
|
||||
defaultChannel.addClient(ws);
|
||||
ws.send(JSON.stringify({ type: "switched", channelId: defaultChannel.id }));
|
||||
}
|
||||
}
|
||||
|
||||
channels.delete(channelId);
|
||||
deleteChannelFromDb(channelId);
|
||||
broadcastChannelList();
|
||||
|
|
|
|||
Loading…
Reference in New Issue