diff --git a/e2e/USER_STORIES.md b/e2e/USER_STORIES.md
index a529a1f1..798b67eb 100644
--- a/e2e/USER_STORIES.md
+++ b/e2e/USER_STORIES.md
@@ -1451,6 +1451,8 @@ Acceptance:
- Searching the directory for the role's name filters to members holding it.
- Deleting a role warns that members keep their space membership, and confirming
removes it from the list; bob sees no "Manage Roles" option.
+- A member's "Edit roles" in a space with no roles opens the roles list rather
+ than ending there.
### US-094 — Invite people to a space
diff --git a/e2e/specs/admin.spec.ts b/e2e/specs/admin.spec.ts
index 2a0609e1..cc9665fb 100644
--- a/e2e/specs/admin.spec.ts
+++ b/e2e/specs/admin.spec.ts
@@ -290,6 +290,18 @@ test("US-093 create roles and assign them", async ({seed, as}) => {
await expect(memberCard(admin, "Bob Barnacle")).toBeVisible()
await expect(memberCard(admin, "Bob Barnacle").locator(".role-badge")).toHaveCount(0)
+ // A space with no roles left offers the roles list from the member's own dialog
+ await memberCard(admin, "Bob Barnacle").getByRole("button").last().click()
+ await menuItem(admin, "Edit roles").click()
+
+ const bobsRoles = dialog(admin, "Edit Member")
+
+ await expect(bobsRoles.getByText("This space has no roles yet.")).toBeVisible()
+
+ await bobsRoles.getByRole("button", {name: "Manage Roles"}).click()
+
+ await expect(emptyState).toBeVisible()
+
const bob = await as(users.bob, spacePath(url) + "/directory")
await expect(memberCard(bob, "Carol Cutter")).toBeVisible()
diff --git a/src/app/components/SpaceMemberRoles.svelte b/src/app/components/SpaceMemberRoles.svelte
index f51a57bd..2679f253 100644
--- a/src/app/components/SpaceMemberRoles.svelte
+++ b/src/app/components/SpaceMemberRoles.svelte
@@ -3,6 +3,7 @@
import {sortBy} from "@welshman/lib"
import {RelayRoles} from "@welshman/app"
import AltArrowLeft from "@assets/icons/alt-arrow-left.svg?dataurl"
+ import UsersGroup from "@assets/icons/users-group-rounded.svg?dataurl"
import Spinner from "@lib/components/Spinner.svelte"
import Button from "@lib/components/Button.svelte"
import Icon from "@lib/components/Icon.svelte"
@@ -13,8 +14,11 @@
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
import ModalFooter from "@lib/components/ModalFooter.svelte"
import RoleItem from "@app/components/RoleItem.svelte"
+ import SpaceRoles from "@app/components/SpaceRoles.svelte"
import {app, profiles, relayManagement} from "@app/core"
+ import {deriveUserCanManageRoles} from "@app/management"
import {deriveSpaceMemberRoles} from "@app/roles"
+ import {pushModal} from "@app/modal"
import {pushToast} from "@app/toast"
type Props = {
@@ -30,9 +34,12 @@
const profileDisplay = $profiles.display(pubkey, [url]).$
const memberRoles = deriveSpaceMemberRoles(url)
const initial = new Set(get(memberRoles).get(pubkey) ?? [])
+ const canManageRoles = deriveUserCanManageRoles(url)
const back = () => history.back()
+ const manageRoles = () => pushModal(SpaceRoles, {url})
+
const toggle = (id: string) => {
const next = new Set(selected)
@@ -114,8 +121,15 @@