update community sidebar

This commit is contained in:
2026-09-22 08:14:56 +07:00
parent 29f3c74d06
commit 7e7d13cbfc
9 changed files with 691 additions and 584 deletions
+98
View File
@@ -0,0 +1,98 @@
# 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.