Files
coop/docs/community-sidebar-plan.md
T
2026-09-22 08:14:56 +07:00

4.4 KiB

Community sidebar

When a community is opened from the sidebar, the sidebar's content becomes the community's own channel/member browser. Going back restores the normal tabs.

+------------------+--------------------------------+
| [<-] community   |                                |
| banner           |                                |
| v Channels  3    |   messages                     |
|   # general      |                                |
|   # random       |                                |
| v Admins    1    |                                |
|   @owner         |                                |
| v Members   2    +--------------------------------+
|   @alice         |   [ composer ]                 |
|   @bob           |                                |
+------------------+--------------------------------+

Current state

  • community_ui::CommunityPanel renders its own 220px left column (channels + members) next to the timeline.
  • Sidebar renders the Recents/Chats/Communities tabs, and Sidebar::open_community records the community as recent and asks CommunityRegistry to emit CommunityEvent::Open, which Workspace turns into a center dock panel.
  • Community already folds channels (state().channels), members, the control roster (control().roles), the community icon, and the banner (metadata.banner).

Plan

1. Backend: community crate

crates/community/src/community.rs

  • Add CommunityEvent::Close(CommunityId) and CommunityEvent::Channel(CommunityId, ChannelId).
  • Add active: Option<ChannelId> to Community, with:
    • pub fn active_channel(&self) -> Option<ChannelId> — the selected channel, defaulting to the first one.
    • pub fn set_active_channel(&mut self, ChannelId, &mut Context<Self>) — stores it and emits Channel.
  • Add banner: Option<PathBuf> resolved from metadata.banner the same way the icon already is, exposed as pub fn banner(&self) -> Option<PathBuf>.

crates/community/src/lib.rs

  • Add CommunityRegistry::emit_close(&mut self, CommunityId, &mut Window, &mut Context<Self>), mirroring emit_community, emitting Close.

crates/community/src/sync.rs

  • Rename resolve_icon to resolve_image (it takes any ImageRef).

2. community_ui: panel becomes timeline-only

  • Drop the internal channel/member column and its helpers.
  • Resolve the shown channel from Community::active_channel on every load and reload, and subscribe to CommunityEvent::Channel to follow sidebar clicks (deferred, because the community is being updated when it emits).

3. workspace: sidebar renders the community

crates/workspace/src/sidebar/mod.rs

  • New state: community: Option<WeakEntity<Community>> and three section flags.
  • open_community also sets self.community; new close_community clears it and asks the registry to emit Close.
  • render picks between the existing tabs and render_community.
  • render_user keeps the account button as usual and adds a back button on the right when a community is open.
  • render_community pins the optional banner image and scrolls everything below it: collapsible Channels, Admins (members where control().roles.is_staff), and Members sections. Every row is a TreeRow — the component the sidebar's own lists use — so height, padding, typography, hover, and selection match, and each row is wrapped flex_shrink_0 so the list scrolls instead of squashing.
  • Observe the registry so member/roster changes re-render the lists.

crates/workspace/src/lib.rs

  • Remember the opened CommunityPanel so CommunityEvent::Close can close it: activate it, focus the group, then dispatch ClosePanel.

4. community: a list refresh keeps entities

CommunityRegistry::track used to rebuild every community, so an open panel and a browsing sidebar went stale on any list signal — and sync::load merges join material into a loaded CommunityState, so the state really can change. track now keeps the entity for an id it still tracks and hands it the new state through Community::adopt, which also cancels a fold in flight that would otherwise write the pre-adoption state back. Observers move to a HashMap<CommunityId, Subscription> so the survivors keep theirs, and the ids the list dropped lose their observer and their synced key.

Out of scope

Moderation, invites, community management, channel icons, unread badges, member search, and persisting the selected channel across restarts.