Spaces UI - #12
Draft
calebroseland wants to merge 18 commits into
Draft
Conversation
Soft-merge of MM-69892 (PR #7, nang2049): the editor slice in webapp_globals and a stub PageEditor mounted at the page/draft routes, so the spaces-API wiring can build around the editor and avoid later conflicts. Applied as a net diff onto master since PR #7 was cut from the pre-merge scaffolding branch. Two divergences resolved: - Dropped PR #7's navigation.ts change: master's merged draft-route fix already exposes the isDraft the editor consumes (single [DOCS_DRAFT_ROUTE, DOCS_ROUTE] match instead of two useRouteMatch calls). - webapp_globals: took PR #7's superset React type imports; kept master's useBootstrapDocs in docs_root (3-way merged cleanly with the isDraft add).
Replace the mock data source with an API-backed DocsDataSource over the
plugin's /api/v1 routes (server/api.go), following the Playbooks
fetch(url, Client4.getOptions()) idiom. Spaces are created, listed
(team-scoped by backing-channel membership), and left against the real
server; the switcher fans out per team for cross-team results.
- client/rest: doGet/doPost/doDelete + paginated listAll ({items,has_more})
- data: async DocsDataSource + apiDataSource; remove mock fixtures
- store: fetchSpaces/fetchAllSpaces/createSpace/leaveSpace async thunks;
drop the team_id hashing stand-in (the server scopes lists by team)
- opaque server ids: drop slug-as-id and isSlugAvailable; the slug stays a
client-only vanity field, format-validated only
- recent: client-side view history (data/recent_spaces) as the seam for a
later server last_viewed_at proxy; page count omitted for MVP
- leave space: wired to member removal, navigates home when viewing it
- visibility stays client-only, maps to server view_access later (PR #10)
Addresses PR #2 feedback (team_id hashing, slug assumption, leaveSpace
wiring). Targets current master; forward-compatible with PR #10.
Adds the docs.editor.* ids from the stub editor and drops the removed url.taken and page-placeholder ids plus the stale favorites ids.
The slug/URL field became dead weight once spaces route by opaque server id: it was collected and format-validated but never sent or used. Remove it from the create-space modal and the form/schema — slug is gone from CreateSpaceInput, the form values, and the Zod schema (with its Url* errors and slugify helper). The UrlInput form-control component and its test are kept for reuse. Re-extract en.json (drops the url.* messages).
Custom emoji/icon picking is deferred, so a space without a custom icon renders a generic compass glyph (utils/space_icon: <SpaceIcon>, using the product's file-text-outline) everywhere a space icon shows — sidebar, home cards, main content, switcher, and the create-space name field — so a freshly created space never appears blank. A space with a custom emoji renders that character. Switcher: rename the "Recent docs" group to "Recent", and dedupe — a space already listed under Recent is no longer repeated under "Your spaces".
Reverts 13b2db3 (soft-merge of PR #7's stub editor): the mounted editor wasn't working. Drops the page_editor component, the editor slice from webapp_globals, and the pageId->PageEditor routing in docs_main_content (back to the space/page placeholder). Later work on the touched files (SpaceIcon, useRecordSpaceView) is preserved. Re-extract en.json.
Implements the space main-content view (Figma Space Home) as a new space_view component set, replacing the routed-space placeholder: - SpaceTitleBar: favorite, space icon + title + menu, member count, space details, Share - PageBar: page-tree toggle + "Pages", "Updated <relative>" (host Timestamp), comments, Edit, overflow, expand - PageHero: rounded banner with the space icon/title, description (with a placeholder when empty), and a pages/members/views stats row - Page body: a placeholder until the editor is mounted Uses Mattermost theme tokens and compass icons. Data-less bits are honest placeholders for now: the stat counts and title-bar member count render an em dash (await the pages/members/views APIs), member avatars are omitted, and the controls are visual scaffolding wired in later passes.
Feeds the Space Home stats and title-bar member count with real data.
- data: add listSpaceMembers over GET /spaces/{id}/members; normalize the
page-list summaries (no body) to Page for the store
- store: new spaceMembers slice (user ids per space) + fetchSpaceMembers;
fetchPages now stores pages for the count (reused by the page tree later)
- selectors: getSpaceMemberIds; page count derives from getPagesForSpace
- hooks: useSpaceStats loads both on space mount and returns the counts
- space view: real PAGES and MEMBERS counts in the hero and the title-bar
member count
Views has no server source yet, so that stat stays an em dash.
Space view build-out plus a raw-<button> cleanup. - buttons: replace the raw <button>s in the space title/page bars with the shared Button — icon buttons use emphasis="quaternary" + the compass `btn-icon` class (the core pattern), Edit uses SecondaryButton - avatars: expose the host Avatar via webapp_globals (as a wrapper that no-ops when the host doesn't publish it, so callers need no null check); useSpaceMemberProfiles resolves member ids to profiles (fetching missing ones); MemberAvatars renders the overlapping stack + "+N" in the hero - share: ShareSpaceModal — real member list with avatars/names and a functional Copy link; the add-people search, per-member role, and space visibility/access are scaffolding (capabilities + view_access land with PR #10). Wired to the title-bar Share button.
Adds the space page-tree panel (Figma) as a middle column in the space view, backed by the real page + move APIs. - store: buildPageTree (parent_id/sort_order, orphans as roots) + buildDescendantMap; reindexAfterMove (0-based sibling renumber mirroring the server) + MOVED_PAGE; movePage thunk (optimistic move, reconcile on success, re-fetch on failure) and createPage thunk - data: DocsDataSource.movePage (PATCH .../move) and createPage (POST); restPatch - collapse: per-user localStorage tracker (data/collapsed_pages) + useCollapsedPages, mirroring the recent-spaces seam - ui: PageTreePanel (264px column: Pages header, add-page, tree) and the recursive PageTreeNode (disclosure chevron, page/folder glyph, indent, active highlight, navigate/collapse) - dnd: pragmatic-drag-and-drop reused from spaces_sidebar — a 3-zone hitbox (reorder above/below, reparent onto center), descendant-drop guard, and a monitor resolving the drop into (parent_id, sibling_index) - layout: space view is now the title bar over [tree panel][content column]; the page-bar Pages button toggles the panel - tests: buildPageTree/descendants and reindexAfterMove/MOVED_PAGE Design's role/visibility remain PR #10 scaffolding; the page body is still a placeholder until the editor is mounted.
Adds a real debounced people-search pipeline and a Base UI Autocomplete combobox for the Share modal, gated on a named permission so nothing misleading ships. There is no server add-member API yet (roles/view-access + capabilities land with PR #10), so a live "add people" control could only hold selections client-side. The picker renders only when canManageMembers. - store/permissions: getSpacePermissions(state, spaceId) selector + useSpacePermissions(spaceId) hook — the get/use split from core's channel_bookmarks/utils — returning named SpacePermissions {canManageMembers}; the seam PR #10's capability set drops into - hooks/user_search: useUserSearch — 300ms-debounced searchProfiles scoped to the current team, mapped to MemberProfile, excludes already-shown ids - share_space_modal/people_picker: Base UI Autocomplete (server-driven list, host Avatar rows, empty/searching states) - share_space_modal: render the picker only when canManageMembers
- permissions: a DEV override forces canManageMembers on so the add-people picker is exercisable before PR #10's capability set exists. The get/use permission hooks are unchanged; revert the default to false (and wire the real capability source) before shipping. - space title bar: the member count is now a button that opens the Share modal, the same as the Share button.
The shared Button is accent-only: every emphasis (quaternary etc.) colors with --button-bg, and its rule follows .btn-icon in core's _buttons.scss, so `<Button emphasis='quaternary' className='btn-icon'>` renders the canonical icon markup but in accent blue instead of neutral grey. Add a :global override that raises .btn-icon's specificity over the emphasis so icon buttons stay neutral (center-channel-color), loaded as a side effect via the button wrapper so it applies everywhere. Marked PORT-TO-CORE — fold into core's .btn-icon rule and delete when it lands.
Button styling: - Pages toggle and Edit are neutral ghost buttons now (not accent-blue / bordered secondary). Edit is an icon+label ghost (pencil + "Edit"). - Members count and the favorite star use btn-xs, closer to core's channel-header favorite toggle; Members reads neutral. - Generalize the port-to-core neutral treatment to also cover labeled quaternary ghost buttons via a `docs-btn-neutral` marker, alongside the existing `.btn-icon` rule (renamed button_icon_neutral -> button_neutral). Rename components/form-controls -> components/form_controls (underscore, matching the rest of the tree), updating all imports.
btn-quaternary hovers with an accent-blue wash; the neutral icon/label ghost buttons now use the grey channel-header hover instead.
Space management from the title bar, backed by the real update/archive APIs.
- data/store: updateSpace (PATCH /spaces/{id}) and deleteSpace (DELETE)
data-source methods + thunks; UpdateSpacePatch type
- Space Settings modal (space_settings_modal): tabbed (Info / Permissions /
Configuration / Archive). Info edits name+description via updateSpace;
Archive confirms then deleteSpace + goHome. Permissions/Configuration are
PR #10 scaffolding (view_access, roles, external sharing).
- Space Info panel (space_info): read-only RHS mirroring core's Channel Info
— icon/title, description, members (avatars), page count + created time;
renders as the right column of the space view.
- title bar: the space name opens a dropdown menu (Space info, Members, Copy
link, Space settings, Leave, Archive; Settings/Archive gated on
canManageMembers). The details button is now the Info-panel toggle.
Built via subagents from an on-disk plan; reviewed and gates re-run here.
Neutral toolbar buttons now mirror core's .btn-icon / channel-header icon: grey base, light-grey hover, light-blue press (:active, declared after :hover so a hovered press reads blue), and persistent blue when toggled on (.active). The Info button gets `active` while its panel is open (alongside aria-pressed) so the toggled state reads deep blue.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Space CRUD UI, Space Shell, Page Tree, etc
Ticket Link