From 546b4c2e8d0b4adeed3d1af5faca7c95a9966495 Mon Sep 17 00:00:00 2001
From: aster <137767097+aster-void@users.noreply.github.com>
Date: Sun, 18 Jan 2026 21:58:52 +0900
Subject: [PATCH] feat: add joinable channels with join/leave functionality
Implement Slack-style channel discovery and membership:
- Add 'default' channel type (auto-join, cannot leave)
- Add joined/memberCount fields to channel API response
- Add POST /channels/:id/join and /leave endpoints
- Auto-join new org members to default channels
- Filter sidebar to show only joined channels
- Add ChannelBrowser modal for discovering public channels
- Add Leave option to channel context menu
---
.../components/channels/ChannelBrowser.svelte | 135 ++++++++++++++++++
.../channels/ChannelContextMenu.svelte | 25 +++-
.../components/channels/ChannelGroup.svelte | 4 +
.../components/channels/ChannelItem.svelte | 4 +
.../channels/ChannelList.controller.svelte.ts | 42 +++++-
.../components/channels/ChannelList.svelte | 18 +++
.../src/components/channels/modals.svelte.ts | 13 ++
apps/server/src/db/channels.ts | 2 +-
apps/server/src/domains/channels/routes.ts | 108 +++++++++++++-
.../src/domains/organizations/members-add.ts | 27 +++-
packages/api-client/src/types.ts | 4 +-
11 files changed, 373 insertions(+), 9 deletions(-)
create mode 100644 apps/desktop/src/components/channels/ChannelBrowser.svelte
diff --git a/apps/desktop/src/components/channels/ChannelBrowser.svelte b/apps/desktop/src/components/channels/ChannelBrowser.svelte
new file mode 100644
index 0000000..f866bf9
--- /dev/null
+++ b/apps/desktop/src/components/channels/ChannelBrowser.svelte
@@ -0,0 +1,135 @@
+
+
+
diff --git a/apps/desktop/src/components/channels/ChannelContextMenu.svelte b/apps/desktop/src/components/channels/ChannelContextMenu.svelte
index 9c90608..135a241 100644
--- a/apps/desktop/src/components/channels/ChannelContextMenu.svelte
+++ b/apps/desktop/src/components/channels/ChannelContextMenu.svelte
@@ -2,17 +2,20 @@
import ChevronRight from "@lucide/svelte/icons/chevron-right";
import FolderInput from "@lucide/svelte/icons/folder-input";
import FolderOutput from "@lucide/svelte/icons/folder-output";
+ import LogOut from "@lucide/svelte/icons/log-out";
import Pencil from "@lucide/svelte/icons/pencil";
import Trash2 from "@lucide/svelte/icons/trash-2";
- import type { ChannelGroup } from "@packages/api-client";
+ import type { Channel, ChannelGroup } from "@packages/api-client";
interface Props {
x: number;
y: number;
+ channelType: Channel["type"];
currentGroupId: string | null;
groups: ChannelGroup[];
onEdit: () => void;
onDelete: () => void;
+ onLeave?: () => void;
onMoveToGroup: (groupId: string | null) => void;
onClose: () => void;
}
@@ -20,14 +23,18 @@
const {
x,
y,
+ channelType,
currentGroupId,
groups,
onEdit,
onDelete,
+ onLeave,
onMoveToGroup,
onClose,
}: Props = $props();
+ const canLeave = $derived(channelType !== "default");
+
let showMoveSubmenu = $state(false);
function handleEdit() {
@@ -40,6 +47,11 @@
onClose();
}
+ function handleLeave() {
+ onLeave?.();
+ onClose();
+ }
+
function handleSelect(groupId: string | null) {
onMoveToGroup(groupId);
onClose();
@@ -129,6 +141,17 @@
{/if}
+ {#if canLeave}
+
+ {/if}
+