Link a member's empty role list to the space's roles (#653)
This commit is contained in:
parent
a7cb5fe4d6
commit
5c0617fbf1
5 changed files with 40 additions and 9 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 @@
|
|||
<Icon icon={AltArrowLeft} />
|
||||
Go back
|
||||
</Button>
|
||||
<Button class="button button-primary" onclick={submit} disabled={loading}>
|
||||
<Spinner {loading}>Save changes</Spinner>
|
||||
</Button>
|
||||
{#if roles.length > 0}
|
||||
<Button class="button button-primary" onclick={submit} disabled={loading}>
|
||||
<Spinner {loading}>Save changes</Spinner>
|
||||
</Button>
|
||||
{:else if $canManageRoles}
|
||||
<Button class="button button-primary" onclick={manageRoles}>
|
||||
<Icon icon={UsersGroup} />
|
||||
Manage Roles
|
||||
</Button>
|
||||
{/if}
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
|
|
|
|||
|
|
@ -150,6 +150,11 @@ export const deriveUserIsSpaceOwner = (url: string) =>
|
|||
([$user, $relay]) => $user.pubkey === $relay?.pubkey,
|
||||
)
|
||||
|
||||
export const deriveUserCanManageRoles = (url: string) =>
|
||||
derived(deriveSpaceSupportedMethods(url), $methods =>
|
||||
["createrole", "editrole", "deleterole"].some(method => $methods.includes(method)),
|
||||
)
|
||||
|
||||
export const deriveUserCanCreateRoom = (url: string) =>
|
||||
derived(
|
||||
[
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
import SpaceAdmins from "@app/components/SpaceAdmins.svelte"
|
||||
import SpaceRoles from "@app/components/SpaceRoles.svelte"
|
||||
import SpaceMembersBanned from "@app/components/SpaceMembersBanned.svelte"
|
||||
import {deriveSpaceSupportedMethods} from "@app/management"
|
||||
import {deriveSpaceSupportedMethods, deriveUserCanManageRoles} from "@app/management"
|
||||
import {deriveSpaceMemberRoles} from "@app/roles"
|
||||
import {relayMemberLists, relayRoles} from "@app/core"
|
||||
import {deriveDisplaysByPubkey} from "@app/social"
|
||||
|
|
@ -33,9 +33,7 @@
|
|||
const members = $relayMemberLists.forUrl(url)
|
||||
const memberRoles = deriveSpaceMemberRoles(url)
|
||||
const supportedMethods = deriveSpaceSupportedMethods(url)
|
||||
const canManageRoles = $derived(
|
||||
["createrole", "editrole", "deleterole"].some(method => $supportedMethods.includes(method)),
|
||||
)
|
||||
const canManageRoles = deriveUserCanManageRoles(url)
|
||||
const canListBans = $derived($supportedMethods.includes("listbannedpubkeys"))
|
||||
const canListAdmins = $derived($supportedMethods.includes("listmethodassignees"))
|
||||
|
||||
|
|
@ -116,7 +114,7 @@
|
|||
<Icon icon={AddCircle} />
|
||||
Invite people
|
||||
</Button>
|
||||
{#if canManageRoles || canListBans || canListAdmins}
|
||||
{#if $canManageRoles || canListBans || canListAdmins}
|
||||
<div class="relative">
|
||||
<Button
|
||||
class="button button-neutral button-sm button-square"
|
||||
|
|
@ -129,7 +127,7 @@
|
|||
<ul
|
||||
transition:fly
|
||||
class="menu bg-surface absolute right-0 z-popover mt-2 w-48 gap-1 rounded-2xl p-2">
|
||||
{#if canManageRoles}
|
||||
{#if $canManageRoles}
|
||||
<li>
|
||||
<Button onclick={manageRoles}>
|
||||
<Icon icon={UsersGroup} />
|
||||
|
|
|
|||
Loading…
Reference in a new issue