Discover button: room directory with expandable room details #48

Open
opened 2026-10-05 22:36:14 +00:00 by robocub · 2 comments
Member

A Discover button in the space sidebar, right under the + (add space) button, that opens a room directory like Element's "Explore rooms".

Upstream check (2026-10-05): commetchat/commet has no issue, PR or branch for a room directory. The closest is #297 (general UI ideas), which doesn't cover discovery. Today Commet can only join a room by pasting an alias (JoinRoomView).

List (Element-style)

  • Sidebar compass button under + that opens an Explore page (desktop and mobile layouts).
  • Search box (filter.generic_search_term) and infinite scroll (since / next_batch).
  • Type filter: All / Rooms / Spaces, done on the server with filter.room_types ([null] / ["m.space"]). Tuwunel supports this.
  • Server chooser: one compact button showing the current server; it opens a popover with a search field and three sections:
    • Your server (always first, the default)
    • Saved: servers the user added, most recently used first, each removable with ×
    • Suggested: a short curated list (matrix.org, nether.im, plus a few common community servers). Dismissible, and hidden while searching.
      Typing a name that isn't in the list offers "Browse " right away, and "Save" keeps it. The popover only shows a few rows per section and the search narrows the rest, so a long saved list never crowds the page (unlike Element's growing dropdown). Browsing goes through your own homeserver (server=). Each suggested default has to pass the federation check before it ships.
  • Distinct states, each with its own message (never a generic error):
    • Remote server doesn't share its directory over federation (e.g. Synapse without allow_public_rooms_over_federation): "example.org doesn't share its room directory with other servers."
    • Your own server doesn't let even its members browse its directory: a separate warning that points at the server admin.
    • Server unreachable / doesn't exist.
    • Directory is empty (a valid answer, not an error).
      Exact error codes per homeserver (Synapse, Tuwunel) are to be confirmed by testing; the mapping lives in one place and is unit-tested.
  • Account picker when more than one account is signed in.
  • Rows show avatar, name, alias, short topic, member count and badges (space, encrypted, guests).
  • Sort: Most members only in v1 (decided). It's the server's own order: the spec has no sort parameter, and Synapse and Tuwunel both sort by joined members.

Expand-in-place details

Clicking a row smoothly expands it downward (the list stays in place); clicking again collapses it back to one row. Only one row is open at a time.
The expanded part shows:

  • the banner (page.codeberg.everypizza.room.banner)
  • the full topic, aliases and join rule
  • for spaces: the child rooms and how many there are (from /hierarchy)
  • Join / View buttons

Details load lazily, for that one room only, when the row opens, so the list itself stays a single cheap /publicRooms query.
Open question: can we read a banner from a room we haven't joined? This needs testing on Tuwunel. If we can't, the row just shows no banner.

v1 keeps a source-agnostic DirectoryEntry and a DirectorySource interface, so a full card/showcase view can still be added later.

Testing

  • Unit: pagination, filter and result mapping against a fake source.
  • Widget: loading, empty and error states, expand/collapse, and Join.
  • Integration (ci/integration-tests harness, local Tuwunel): publish a room and a space (with a banner) to the directory via the API, open Discover, filter, search, expand the space and check its children, join, and check the room shows up in the room list.

Branch: feat/discover.

A **Discover** button in the space sidebar, right under the `+` (add space) button, that opens a room directory like Element's "Explore rooms". Upstream check (2026-10-05): commetchat/commet has no issue, PR or branch for a room directory. The closest is #297 (general UI ideas), which doesn't cover discovery. Today Commet can only join a room by pasting an alias (`JoinRoomView`). ## List (Element-style) - Sidebar compass button under `+` that opens an Explore page (desktop and mobile layouts). - Search box (`filter.generic_search_term`) and infinite scroll (`since` / `next_batch`). - Type filter: **All / Rooms / Spaces**, done on the server with `filter.room_types` (`[null]` / `["m.space"]`). Tuwunel supports this. - Server chooser: one compact button showing the current server; it opens a popover with a search field and three sections: - **Your server** (always first, the default) - **Saved**: servers the user added, most recently used first, each removable with × - **Suggested**: a short curated list (matrix.org, nether.im, plus a few common community servers). Dismissible, and hidden while searching. Typing a name that isn't in the list offers "Browse <server>" right away, and "Save" keeps it. The popover only shows a few rows per section and the search narrows the rest, so a long saved list never crowds the page (unlike Element's growing dropdown). Browsing goes through your own homeserver (`server=`). Each suggested default has to pass the federation check before it ships. - Distinct states, each with its own message (never a generic error): - **Remote server doesn't share its directory over federation** (e.g. Synapse without `allow_public_rooms_over_federation`): "example.org doesn't share its room directory with other servers." - **Your own server doesn't let even its members browse its directory**: a separate warning that points at the server admin. - **Server unreachable / doesn't exist**. - **Directory is empty** (a valid answer, not an error). Exact error codes per homeserver (Synapse, Tuwunel) are to be confirmed by testing; the mapping lives in one place and is unit-tested. - Account picker when more than one account is signed in. - Rows show avatar, name, alias, short topic, member count and badges (space, encrypted, guests). - Sort: **Most members** only in v1 (decided). It's the server's own order: the spec has no sort parameter, and Synapse and Tuwunel both sort by joined members. ## Expand-in-place details Clicking a row smoothly expands it downward (the list stays in place); clicking again collapses it back to one row. Only one row is open at a time. The expanded part shows: - the banner (`page.codeberg.everypizza.room.banner`) - the full topic, aliases and join rule - for spaces: the child rooms and how many there are (from `/hierarchy`) - Join / View buttons Details load lazily, for that one room only, when the row opens, so the list itself stays a single cheap `/publicRooms` query. Open question: can we read a banner from a room we haven't joined? This needs testing on Tuwunel. If we can't, the row just shows no banner. v1 keeps a source-agnostic `DirectoryEntry` and a `DirectorySource` interface, so a full card/showcase view can still be added later. ## Testing - Unit: pagination, filter and result mapping against a fake source. - Widget: loading, empty and error states, expand/collapse, and Join. - Integration (`ci/integration-tests` harness, local Tuwunel): publish a room and a space (with a banner) to the directory via the API, open Discover, filter, search, expand the space and check its children, join, and check the room shows up in the room list. Branch: `feat/discover`.
Author
Member

Probe results (2026-10-05, nether.autos fixture, Tuwunel 1.8.x)

Two fresh users: A published a public space (world-readable, with a banner and one child) and a public room (shared history, with a banner). B is not a member of either.

Own directory, as B: paging, room_types ["m.space"] / [null] and generic_search_term all filter on the server correctly. Unauthenticated /publicRooms also returns 200.

Expanded-row details for rooms B hasn't joined:

local world-readable local shared-history remote (matrix.org, world-readable)
banner via /state/<type>/ ✅ 200 ❌ 404 ❌ 404 (our server isn't in the room; no federated peeking)
/v1/room_summary ✅ ✅ ✅ name/topic/avatar/alias/members/type/version
/v1/hierarchy (spaces) ✅ children n/a ✅ children

So: summary + hierarchy always work, but the banner is best-effort. We get it only for world-readable rooms our homeserver is already in. Neither summary nor hierarchy carries it (children_state is only m.space.child). The row design has to look finished without a banner.

Remote directories (?server=, through our Tuwunel):

  • 200: matrix.org, mozilla.org, gnome.org, fedora.im, tchncs.de
  • 502 M_CONNECTION_FAILED: kde.org, envs.net, nether.im (its allow_public_room_directory_over_federation = false) and nonexistent.invalid

⇒ Tuwunel returns the same error for "doesn't share its directory" and "doesn't exist", so the client has to tell them apart itself. On a 502, check whether the server is a live Matrix server (.well-known/matrix/server, or federation /version at the resolved host): if it is, say "doesn't share its room directory"; otherwise "couldn't reach". Synapse may answer differently; to be checked with the Synapse integration fixture, which is also where the "your own server won't show you its directory" case (403 from your own homeserver) gets tested.

Suggested defaults (verified): matrix.org, mozilla.org, gnome.org, fedora.im, tchncs.de. Not kde.org or envs.net. nether.im only once it shares its directory over federation (operator decision: flip the Tuwunel setting).

## Probe results (2026-10-05, nether.autos fixture, Tuwunel 1.8.x) Two fresh users: A published a public space (world-readable, with a banner and one child) and a public room (shared history, with a banner). B is not a member of either. **Own directory, as B:** paging, `room_types` `["m.space"]` / `[null]` and `generic_search_term` all filter on the server correctly. Unauthenticated `/publicRooms` also returns 200. **Expanded-row details for rooms B hasn't joined:** | | local world-readable | local shared-history | remote (matrix.org, world-readable) | |---|---|---|---| | banner via `/state/<type>/` | ✅ 200 | ❌ 404 | ❌ 404 (our server isn't in the room; no federated peeking) | | `/v1/room_summary` | ✅ | ✅ | ✅ name/topic/avatar/alias/members/type/version | | `/v1/hierarchy` (spaces) | ✅ children | n/a | ✅ children | So: summary + hierarchy always work, but **the banner is best-effort**. We get it only for world-readable rooms our homeserver is already in. Neither summary nor hierarchy carries it (`children_state` is only `m.space.child`). The row design has to look finished without a banner. **Remote directories (`?server=`, through our Tuwunel):** - 200: matrix.org, mozilla.org, gnome.org, fedora.im, tchncs.de - 502 `M_CONNECTION_FAILED`: kde.org, envs.net, **nether.im** (its `allow_public_room_directory_over_federation = false`) and `nonexistent.invalid` ⇒ **Tuwunel returns the same error for "doesn't share its directory" and "doesn't exist"**, so the client has to tell them apart itself. On a 502, check whether the server is a live Matrix server (`.well-known/matrix/server`, or federation `/version` at the resolved host): if it is, say "doesn't share its room directory"; otherwise "couldn't reach". Synapse may answer differently; to be checked with the Synapse integration fixture, which is also where the "your own server won't show you its directory" case (403 from your own homeserver) gets tested. **Suggested defaults (verified):** matrix.org, mozilla.org, gnome.org, fedora.im, tchncs.de. Not kde.org or envs.net. **nether.im** only once it shares its directory over federation (operator decision: flip the Tuwunel setting).
Author
Member

Design update (operator, 2026-10-05)

  • nether.im is left out of the suggested servers for now (its directory isn't shared over federation).
  • Multi-account directory sourcing. When several accounts are signed in, browsing a server's directory uses the best account for it, not just the account you'll join with:
    • Choosing the fetching account: if one of your accounts lives on server X, X's directory is fetched through that account (a local query, which works even when X doesn't share its directory over federation). Otherwise it's fetched through the joining account's homeserver as usual (?server=X).
    • Server chooser: every homeserver you have an account on appears under "Your servers" with a small "via @you:x" hint. No setup needed.
    • Join as: the account picker on the page chooses who joins. Joining a room found through another account uses room id + via: [X], so it works across servers.
    • Failure case: a room can be listed yet refuse members from other servers (m.federate: false, or X not federating with the joining server). The directory listing doesn't reveal this, so it shows up as a join error. We catch it and suggest "Join as @you:x" when you have an account there.
    • Each loaded result keeps which account fetched it. The expanded row's summary/hierarchy calls go through the same account, so details work for rooms only that account can see.
  • Tests: unit-test the fetching-account choice (own-server match, fallback, several accounts on the same server) and the join-error → "Join as" suggestion. The integration test gets a two-account case once the harness can run two homeservers (Tuwunel + Synapse, or the nether.autos fixture).
## Design update (operator, 2026-10-05) - **nether.im is left out of the suggested servers** for now (its directory isn't shared over federation). - **Multi-account directory sourcing.** When several accounts are signed in, browsing a server's directory uses the best account for it, not just the account you'll join with: - **Choosing the fetching account:** if one of your accounts lives on server X, X's directory is fetched through *that* account (a local query, which works even when X doesn't share its directory over federation). Otherwise it's fetched through the joining account's homeserver as usual (`?server=X`). - **Server chooser:** every homeserver you have an account on appears under "Your servers" with a small "via @you:x" hint. No setup needed. - **Join as:** the account picker on the page chooses who joins. Joining a room found through another account uses room id + `via: [X]`, so it works across servers. - **Failure case:** a room can be listed yet refuse members from other servers (`m.federate: false`, or X not federating with the joining server). The directory listing doesn't reveal this, so it shows up as a join error. We catch it and suggest "Join as @you:x" when you have an account there. - Each loaded result keeps which account fetched it. The expanded row's summary/hierarchy calls go through the same account, so details work for rooms only that account can see. - Tests: unit-test the fetching-account choice (own-server match, fallback, several accounts on the same server) and the join-error → "Join as" suggestion. The integration test gets a two-account case once the harness can run two homeservers (Tuwunel + Synapse, or the nether.autos fixture).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
nether/vommet#48
No description provided.