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} +