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::CommunityPanelrenders its own 220px left column (channels + members) next to the timeline.Sidebarrenders the Recents/Chats/Communities tabs, andSidebar::open_communityrecords the community as recent and asksCommunityRegistryto emitCommunityEvent::Open, whichWorkspaceturns into a center dock panel.Communityalready 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)andCommunityEvent::Channel(CommunityId, ChannelId). - Add
active: Option<ChannelId>toCommunity, 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 emitsChannel.
- Add
banner: Option<PathBuf>resolved frommetadata.bannerthe same way the icon already is, exposed aspub fn banner(&self) -> Option<PathBuf>.
crates/community/src/lib.rs
- Add
CommunityRegistry::emit_close(&mut self, CommunityId, &mut Window, &mut Context<Self>), mirroringemit_community, emittingClose.
crates/community/src/sync.rs
- Rename
resolve_icontoresolve_image(it takes anyImageRef).
2. community_ui: panel becomes timeline-only
- Drop the internal channel/member column and its helpers.
- Resolve the shown channel from
Community::active_channelon every load and reload, and subscribe toCommunityEvent::Channelto 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_communityalso setsself.community; newclose_communityclears it and asks the registry to emitClose.renderpicks between the existing tabs andrender_community.render_userkeeps the account button as usual and adds a back button on the right when a community is open.render_communitypins the optional banner image and scrolls everything below it: collapsible Channels, Admins (members wherecontrol().roles.is_staff), and Members sections. Every row is aTreeRow— the component the sidebar's own lists use — so height, padding, typography, hover, and selection match, and each row is wrappedflex_shrink_0so 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
CommunityPanelsoCommunityEvent::Closecan close it: activate it, focus the group, then dispatchClosePanel.
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.