forked from coracle/flotilla
Refactor role view models and member grouping
This commit is contained in:
@@ -33,7 +33,7 @@
|
||||
import {pubkeyLink, deriveSpaceBannedPubkeyItems} from "@app/core/state"
|
||||
import {
|
||||
deriveUserHasSpacePermission,
|
||||
deriveSpaceMemberRoleInfo,
|
||||
deriveSpaceMemberRoles,
|
||||
ROOM_PERMISSION_ADD_MEMBER,
|
||||
ROOM_PERMISSION_BAN_USER,
|
||||
} from "@app/core/roles"
|
||||
@@ -59,9 +59,7 @@
|
||||
|
||||
const bannedPubkeys = url ? deriveSpaceBannedPubkeyItems(url) : undefined
|
||||
|
||||
const spaceMemberRoles = url ? deriveSpaceMemberRoleInfo(url) : readable(new Map())
|
||||
|
||||
const assignedRoles = $derived($spaceMemberRoles.get(pubkey)?.roles || [])
|
||||
const assignedRoles = url ? deriveSpaceMemberRoles(url, pubkey) : readable([])
|
||||
|
||||
const isBanned = $derived($bannedPubkeys?.some(item => item.pubkey === pubkey) ?? false)
|
||||
|
||||
@@ -163,12 +161,12 @@
|
||||
{/if}
|
||||
</div>
|
||||
<ProfileInfo {pubkey} {url} />
|
||||
{#if assignedRoles.length > 0}
|
||||
{#if $assignedRoles.length > 0}
|
||||
<div class="card2 card2-sm bg-alt col-3">
|
||||
<h3 class="text-lg font-semibold">Roles</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{#each assignedRoles as role (role.name)}
|
||||
<RoleBadge role={role.name} label={role.label} color={role.color} class="badge-md" />
|
||||
{#each $assignedRoles as role (role.name)}
|
||||
<RoleBadge {role} class="badge-md" />
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script lang="ts">
|
||||
import cx from "classnames"
|
||||
import type {RoleDefinition} from "@app/core/roles"
|
||||
import {roleColorToCSS} from "@app/core/roles"
|
||||
|
||||
type Props = {
|
||||
role: string
|
||||
role: RoleDefinition | string
|
||||
label?: string
|
||||
color?: number
|
||||
class?: string
|
||||
@@ -11,15 +12,21 @@
|
||||
|
||||
const {role, label, color, ...props}: Props = $props()
|
||||
|
||||
const roleName = $derived(typeof role === "string" ? role : role.name)
|
||||
const roleLabel = $derived(
|
||||
label || (typeof role === "string" ? undefined : role.label) || roleName,
|
||||
)
|
||||
const roleColor = $derived(color ?? (typeof role === "string" ? undefined : role.color))
|
||||
|
||||
const style = $derived(
|
||||
color === undefined
|
||||
roleColor === undefined
|
||||
? ""
|
||||
: `color: ${roleColorToCSS(color)}; border-color: ${roleColorToCSS(color)};`,
|
||||
: `color: ${roleColorToCSS(roleColor)}; border-color: ${roleColorToCSS(roleColor)};`,
|
||||
)
|
||||
|
||||
const className = $derived(cx("badge badge-outline badge-sm", props.class))
|
||||
</script>
|
||||
|
||||
<span class={className} {style}>
|
||||
{label || role}
|
||||
{roleLabel}
|
||||
</span>
|
||||
|
||||
@@ -34,10 +34,11 @@
|
||||
import RoomImage from "@app/components/RoomImage.svelte"
|
||||
import {
|
||||
deriveRoomMembers,
|
||||
deriveRoomRoles,
|
||||
deriveRoomRoleDefinitions,
|
||||
deriveUserIsRoomAdmin,
|
||||
deriveHasPermission,
|
||||
sortRolesDesc,
|
||||
getRolePermissionsLabel,
|
||||
getRoleAccessLabel,
|
||||
ROOM_PERMISSION_EDIT_META,
|
||||
} from "@app/core/roles"
|
||||
import {
|
||||
@@ -65,7 +66,7 @@
|
||||
|
||||
const room = deriveRoom(url, h)
|
||||
const members = deriveRoomMembers(url, h)
|
||||
const roomRoles = deriveRoomRoles(url, h)
|
||||
const roleDefinitions = deriveRoomRoleDefinitions(url, h)
|
||||
const userIsAdmin = deriveUserIsRoomAdmin(url, h)
|
||||
const canEditMetadata = deriveHasPermission(url, h, ROOM_PERMISSION_EDIT_META)
|
||||
const membershipStatus = deriveUserRoomMembershipStatus(url, h)
|
||||
@@ -74,19 +75,6 @@
|
||||
const isFavorite = $derived($userRooms.includes(h))
|
||||
const shouldNotify = deriveShouldNotify(url, h)
|
||||
|
||||
const roleRows = $derived.by(() =>
|
||||
sortRolesDesc(
|
||||
Array.from($roomRoles.roles.values()).map(role => ({
|
||||
name: role.name,
|
||||
label: role.label,
|
||||
color: role.color,
|
||||
order: role.order,
|
||||
permissionsLabel: role.permissions.join(", "),
|
||||
accessLabel: Array.from(role.access).join(", "),
|
||||
})),
|
||||
),
|
||||
)
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
const toggleMenu = () => {
|
||||
@@ -278,27 +266,23 @@
|
||||
<span class="text-error">Member list not available from this relay</span>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $userIsAdmin && roleRows.length > 0}
|
||||
{#if $userIsAdmin && $roleDefinitions.length > 0}
|
||||
<div class="card2 card2-sm bg-alt col-4">
|
||||
<strong class="text-lg">Role Definitions</strong>
|
||||
<div class="flex flex-col gap-2">
|
||||
{#each roleRows as role (role.name)}
|
||||
{#each $roleDefinitions as role (role.name)}
|
||||
<div class="rounded-box bg-base-300 p-3 flex flex-col gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<RoleBadge
|
||||
role={role.name}
|
||||
label={role.label}
|
||||
color={role.color}
|
||||
class="badge-md" />
|
||||
<RoleBadge {role} class="badge-md" />
|
||||
{#if role.order !== undefined}
|
||||
<span class="text-xs opacity-70">Order {role.order}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#if role.permissionsLabel}
|
||||
<p class="text-xs opacity-75">Permissions: {role.permissionsLabel}</p>
|
||||
{#if role.permissions.length > 0}
|
||||
<p class="text-xs opacity-75">Permissions: {getRolePermissionsLabel(role)}</p>
|
||||
{/if}
|
||||
{#if role.accessLabel}
|
||||
<p class="text-xs opacity-75">Access: {role.accessLabel}</p>
|
||||
{#if role.access.size > 0}
|
||||
<p class="text-xs opacity-75">Access: {getRoleAccessLabel(role)}</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import {first, removeUndefined, sortBy} from "@welshman/lib"
|
||||
import {waitForThunkError, removeRoomMember} from "@welshman/app"
|
||||
import MenuDots from "@assets/icons/menu-dots.svg?dataurl"
|
||||
import MinusCircle from "@assets/icons/minus-circle.svg?dataurl"
|
||||
@@ -20,12 +19,10 @@
|
||||
import RoleBadge from "@app/components/RoleBadge.svelte"
|
||||
import RoomName from "@app/components/RoomName.svelte"
|
||||
import RoomMembersAdd from "@app/components/RoomMembersAdd.svelte"
|
||||
import type {RoomMember} from "@app/core/roles"
|
||||
import {
|
||||
deriveRoomMembers,
|
||||
deriveRoomRoles,
|
||||
deriveGroupedRoomMembers,
|
||||
deriveHasPermission,
|
||||
sortRolesDesc,
|
||||
ROOM_PERMISSION_ADD_MEMBER,
|
||||
ROOM_PERMISSION_REMOVE_MEMBER,
|
||||
} from "@app/core/roles"
|
||||
@@ -42,7 +39,7 @@
|
||||
|
||||
const room = deriveRoom(url, h)
|
||||
const members = deriveRoomMembers(url, h)
|
||||
const roomRoles = deriveRoomRoles(url, h)
|
||||
const memberGroups = deriveGroupedRoomMembers(url, h)
|
||||
const canAddMembers = deriveHasPermission(url, h, ROOM_PERMISSION_ADD_MEMBER)
|
||||
const canRemoveMembers = deriveHasPermission(url, h, ROOM_PERMISSION_REMOVE_MEMBER)
|
||||
|
||||
@@ -56,58 +53,6 @@
|
||||
menuPubkey = undefined
|
||||
}
|
||||
|
||||
const getResolvedRoles = (member: RoomMember) =>
|
||||
removeUndefined(member.roles.map(roleName => $roomRoles.roles.get(roleName)))
|
||||
|
||||
const getPrimaryRole = (member: RoomMember) => first(sortRolesDesc(getResolvedRoles(member)))
|
||||
|
||||
const memberGroups = $derived.by(() => {
|
||||
const byRole = new Map<
|
||||
string,
|
||||
{
|
||||
key: string
|
||||
label: string
|
||||
color?: number
|
||||
order?: number
|
||||
members: RoomMember[]
|
||||
}
|
||||
>()
|
||||
const defaultGroup = {
|
||||
key: "members",
|
||||
label: "Members",
|
||||
members: [] as RoomMember[],
|
||||
}
|
||||
|
||||
for (const member of $members) {
|
||||
const primaryRole = getPrimaryRole(member)
|
||||
|
||||
if (!primaryRole) {
|
||||
defaultGroup.members.push(member)
|
||||
continue
|
||||
}
|
||||
|
||||
if (!byRole.has(primaryRole.name)) {
|
||||
byRole.set(primaryRole.name, {
|
||||
key: primaryRole.name,
|
||||
label: primaryRole.label || primaryRole.name,
|
||||
color: primaryRole.color,
|
||||
order: primaryRole.order,
|
||||
members: [],
|
||||
})
|
||||
}
|
||||
|
||||
byRole.get(primaryRole.name)!.members.push(member)
|
||||
}
|
||||
|
||||
const groups = sortBy(group => -(group.order ?? -Infinity), Array.from(byRole.values()))
|
||||
|
||||
if (defaultGroup.members.length > 0) {
|
||||
groups.push(defaultGroup)
|
||||
}
|
||||
|
||||
return groups
|
||||
})
|
||||
|
||||
const addMember = () => pushModal(RoomMembersAdd, {url, h})
|
||||
|
||||
const removeMember = (pubkey: string) =>
|
||||
@@ -147,16 +92,12 @@
|
||||
<span class="text-base-content/70">No members yet</span>
|
||||
</div>
|
||||
{:else}
|
||||
{#each memberGroups as group (group.key)}
|
||||
{#each $memberGroups as group (group.key)}
|
||||
<div class="pt-2 pb-1">
|
||||
{#if group.color !== undefined}
|
||||
<RoleBadge
|
||||
role={group.key}
|
||||
label={group.label}
|
||||
color={group.color}
|
||||
class="badge-md" />
|
||||
{#if group.role}
|
||||
<RoleBadge role={group.role} class="badge-md" />
|
||||
{:else}
|
||||
<span class="text-sm font-semibold opacity-75">{group.label}</span>
|
||||
<span class="text-sm font-semibold opacity-75">Members</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#each group.members as member (member.pubkey)}
|
||||
@@ -164,10 +105,10 @@
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="min-w-0 flex-1">
|
||||
<Profile pubkey={member.pubkey} {url} />
|
||||
{#if getResolvedRoles(member).length > 0}
|
||||
{#if member.roles.length > 0}
|
||||
<div class="mt-1 flex flex-wrap gap-1">
|
||||
{#each getResolvedRoles(member) as role (role.name)}
|
||||
<RoleBadge role={role.name} label={role.label} color={role.color} />
|
||||
{#each member.roles as role (role.name)}
|
||||
<RoleBadge {role} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import {sortBy} from "@welshman/lib"
|
||||
import {ManagementMethod} from "@welshman/util"
|
||||
import {manageRelay, displayProfileByPubkey} from "@welshman/app"
|
||||
import MenuDots from "@assets/icons/menu-dots.svg?dataurl"
|
||||
@@ -23,14 +22,13 @@
|
||||
import Profile from "@app/components/Profile.svelte"
|
||||
import SpaceMembersAdd from "@app/components/SpaceMembersAdd.svelte"
|
||||
import SpaceMembersBanned from "@app/components/SpaceMembersBanned.svelte"
|
||||
import type {RoomMember} from "@app/core/roles"
|
||||
import {
|
||||
deriveSpaceMembers,
|
||||
deriveSpaceBannedPubkeyItems,
|
||||
deriveSupportedMethods,
|
||||
} from "@app/core/state"
|
||||
import {
|
||||
deriveSpaceMemberRoleInfo,
|
||||
deriveGroupedSpaceMembers,
|
||||
deriveUserHasSpacePermission,
|
||||
ROOM_PERMISSION_ADD_MEMBER,
|
||||
ROOM_PERMISSION_REMOVE_MEMBER,
|
||||
@@ -47,7 +45,7 @@
|
||||
|
||||
const members = deriveSpaceMembers(url)
|
||||
const bans = deriveSpaceBannedPubkeyItems(url)
|
||||
const spaceMemberRoles = deriveSpaceMemberRoleInfo(url)
|
||||
const memberGroups = deriveGroupedSpaceMembers(url, members)
|
||||
const canAddMember = deriveUserHasSpacePermission(url, ROOM_PERMISSION_ADD_MEMBER)
|
||||
const canBanByPermission = deriveUserHasSpacePermission(url, ROOM_PERMISSION_BAN_USER)
|
||||
const canUnallowByPermission = deriveUserHasSpacePermission(url, ROOM_PERMISSION_REMOVE_MEMBER)
|
||||
@@ -59,72 +57,6 @@
|
||||
$canUnallowByPermission && $supportedMethods.includes(ManagementMethod.UnallowPubkey),
|
||||
)
|
||||
|
||||
type SpaceMemberWithRoles = RoomMember & {
|
||||
roleDefinitions: Array<{name: string; label?: string; color?: number; order?: number}>
|
||||
primaryRole?: {name: string; label?: string; color?: number}
|
||||
sortKey: number
|
||||
}
|
||||
|
||||
const memberGroups = $derived.by(() => {
|
||||
const byRole = new Map<
|
||||
string,
|
||||
{
|
||||
key: string
|
||||
label: string
|
||||
color?: number
|
||||
order?: number
|
||||
members: SpaceMemberWithRoles[]
|
||||
}
|
||||
>()
|
||||
const defaultGroup = {
|
||||
key: "members",
|
||||
label: "Members",
|
||||
members: [] as SpaceMemberWithRoles[],
|
||||
}
|
||||
|
||||
for (const pubkey of $members) {
|
||||
const roleInfo = $spaceMemberRoles.get(pubkey)
|
||||
const member = {
|
||||
pubkey,
|
||||
roles: roleInfo?.roles.map(role => role.name) || [],
|
||||
roleDefinitions: roleInfo?.roles || [],
|
||||
primaryRole: roleInfo?.primaryRole,
|
||||
sortKey: roleInfo?.sortKey ?? -Infinity,
|
||||
}
|
||||
|
||||
if (!member.primaryRole) {
|
||||
defaultGroup.members.push(member)
|
||||
continue
|
||||
}
|
||||
|
||||
const roleName = member.primaryRole.name
|
||||
|
||||
if (!byRole.has(roleName)) {
|
||||
byRole.set(roleName, {
|
||||
key: roleName,
|
||||
label: member.primaryRole.label || roleName,
|
||||
color: member.primaryRole.color,
|
||||
order: member.sortKey,
|
||||
members: [],
|
||||
})
|
||||
}
|
||||
|
||||
byRole.get(roleName)!.members.push(member)
|
||||
}
|
||||
|
||||
const groups = sortBy(group => -(group.order ?? -Infinity), Array.from(byRole.values()))
|
||||
|
||||
for (const group of groups) {
|
||||
group.members = sortBy(member => -member.sortKey, group.members)
|
||||
}
|
||||
|
||||
if (defaultGroup.members.length > 0) {
|
||||
groups.push(defaultGroup)
|
||||
}
|
||||
|
||||
return groups
|
||||
})
|
||||
|
||||
const back = () => history.back()
|
||||
|
||||
const toggleMenu = (pubkey: string) => {
|
||||
@@ -203,16 +135,12 @@
|
||||
<span class="text-base-content/70">No members yet</span>
|
||||
</div>
|
||||
{:else}
|
||||
{#each memberGroups as group (group.key)}
|
||||
{#each $memberGroups as group (group.key)}
|
||||
<div class="pt-2 pb-1">
|
||||
{#if group.color !== undefined}
|
||||
<RoleBadge
|
||||
role={group.key}
|
||||
label={group.label}
|
||||
color={group.color}
|
||||
class="badge-md" />
|
||||
{#if group.role}
|
||||
<RoleBadge role={group.role} class="badge-md" />
|
||||
{:else}
|
||||
<span class="text-sm font-semibold opacity-75">{group.label}</span>
|
||||
<span class="text-sm font-semibold opacity-75">Members</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#each group.members as member (member.pubkey)}
|
||||
@@ -220,10 +148,10 @@
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="min-w-0 flex-1">
|
||||
<Profile pubkey={member.pubkey} {url} />
|
||||
{#if member.roleDefinitions.length > 0}
|
||||
{#if member.roles.length > 0}
|
||||
<div class="mt-1 flex flex-wrap gap-1">
|
||||
{#each member.roleDefinitions as role (role.name)}
|
||||
<RoleBadge role={role.name} label={role.label} color={role.color} />
|
||||
{#each member.roles as role (role.name)}
|
||||
<RoleBadge {role} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
+99
-14
@@ -1,4 +1,4 @@
|
||||
import {derived, readable} from "svelte/store"
|
||||
import {derived, readable, type Readable} from "svelte/store"
|
||||
import {first, memoize, removeUndefined, simpleCache, sortBy, uniq} from "@welshman/lib"
|
||||
import {deriveArray, deriveEventsByIdForUrl} from "@welshman/store"
|
||||
import {pubkey, repository, tracker} from "@welshman/app"
|
||||
@@ -53,6 +53,18 @@ export type RoomMember = {
|
||||
roles: string[]
|
||||
}
|
||||
|
||||
export type MemberRoleInfo = {
|
||||
pubkey: string
|
||||
roles: RoleDefinition[]
|
||||
primaryRole?: RoleDefinition
|
||||
}
|
||||
|
||||
export type MemberRoleGroup = {
|
||||
key: string
|
||||
role?: RoleDefinition
|
||||
members: MemberRoleInfo[]
|
||||
}
|
||||
|
||||
type ParsedRoleState = {
|
||||
roles: Map<string, RoleDefinition>
|
||||
hasPermissionTags: boolean
|
||||
@@ -65,11 +77,7 @@ type RoomSnapshot = {
|
||||
admins: RoomMember[]
|
||||
}
|
||||
|
||||
export type SpaceMemberRoleInfo = {
|
||||
roles: RoleDefinition[]
|
||||
primaryRole?: RoleDefinition
|
||||
sortKey: number
|
||||
}
|
||||
export type SpaceMemberRoleInfo = MemberRoleInfo
|
||||
|
||||
type SpaceRoleState = {
|
||||
hasPermissionTags: boolean
|
||||
@@ -275,8 +283,62 @@ const getResolvedRoles = (rolesByName: Map<string, RoleDefinition>, roleNames: s
|
||||
export const sortRolesDesc = <T extends {order?: number}>(items: T[]) =>
|
||||
sortBy(item => -(item.order ?? -Infinity), items)
|
||||
|
||||
export const getRoleLabel = (role: RoleDefinition) => role.label || role.name
|
||||
|
||||
export const getRolePermissionsLabel = (role: RoleDefinition) => role.permissions.join(", ")
|
||||
|
||||
export const getRoleAccessLabel = (role: RoleDefinition) => Array.from(role.access).join(", ")
|
||||
|
||||
const getPrimaryRole = (roles: RoleDefinition[]) => first(sortRolesDesc(roles))
|
||||
|
||||
const toMemberRoleInfo = (pubkey: string, roles: RoleDefinition[]): MemberRoleInfo => {
|
||||
const sortedRoles = sortRolesDesc(roles)
|
||||
|
||||
return {
|
||||
pubkey,
|
||||
roles: sortedRoles,
|
||||
primaryRole: first(sortedRoles),
|
||||
}
|
||||
}
|
||||
|
||||
const sortMemberRoleInfos = (members: MemberRoleInfo[]) =>
|
||||
sortBy(member => -(member.primaryRole?.order ?? -Infinity), members)
|
||||
|
||||
export const groupMemberRoleInfos = (members: MemberRoleInfo[]) => {
|
||||
const byRole = new Map<string, MemberRoleGroup>()
|
||||
const ungrouped: MemberRoleGroup = {
|
||||
key: "members",
|
||||
members: [],
|
||||
}
|
||||
|
||||
for (const member of sortMemberRoleInfos(members)) {
|
||||
if (!member.primaryRole) {
|
||||
ungrouped.members.push(member)
|
||||
continue
|
||||
}
|
||||
|
||||
const key = member.primaryRole.name
|
||||
|
||||
if (!byRole.has(key)) {
|
||||
byRole.set(key, {
|
||||
key,
|
||||
role: member.primaryRole,
|
||||
members: [],
|
||||
})
|
||||
}
|
||||
|
||||
byRole.get(key)!.members.push(member)
|
||||
}
|
||||
|
||||
const groups = sortBy(group => -(group.role?.order ?? -Infinity), Array.from(byRole.values()))
|
||||
|
||||
if (ungrouped.members.length > 0) {
|
||||
groups.push(ungrouped)
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
|
||||
const deriveRoomRoleAssignments = simpleCache(([url, h]: [string, string]) =>
|
||||
derived(
|
||||
[deriveRoomRoleState(url, h), deriveRoomMembers(url, h), deriveRoomAdmins(url, h)],
|
||||
@@ -523,6 +585,23 @@ export const deriveUserRoleColor = (url: string, h: string, targetPubkey: string
|
||||
getPrimaryRole(getResolvedRoles($roomRoles.roles, $roleNames))?.color,
|
||||
)
|
||||
|
||||
export const deriveRoomRoleDefinitions = (url: string, h: string) =>
|
||||
derived(deriveRoomRoles(url, h), $roomRoles =>
|
||||
sortRolesDesc(Array.from($roomRoles.roles.values())),
|
||||
)
|
||||
|
||||
export const deriveRoomMemberRoleInfo = (url: string, h: string) =>
|
||||
derived([deriveRoomMembers(url, h), deriveRoomRoles(url, h)], ([$members, $roomRoles]) =>
|
||||
sortMemberRoleInfos(
|
||||
$members.map(member =>
|
||||
toMemberRoleInfo(member.pubkey, getResolvedRoles($roomRoles.roles, member.roles)),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
export const deriveGroupedRoomMembers = (url: string, h: string) =>
|
||||
derived(deriveRoomMemberRoleInfo(url, h), $members => groupMemberRoleInfos($members))
|
||||
|
||||
export const getRoleSortKey = (url: string, h: string, targetPubkey: string) =>
|
||||
derived(
|
||||
[deriveUserRoles(url, h, targetPubkey), deriveRoomRoles(url, h)],
|
||||
@@ -535,17 +614,23 @@ export const deriveSpaceMemberRoleInfo = (url: string) =>
|
||||
const roleInfoByPubkey = new Map<string, SpaceMemberRoleInfo>()
|
||||
|
||||
for (const [pubkey, roles] of $spaceRoleState.memberRoles.entries()) {
|
||||
const sortedRoles = sortRolesDesc(roles)
|
||||
const primaryRole = first(sortedRoles)
|
||||
|
||||
roleInfoByPubkey.set(pubkey, {
|
||||
roles: sortedRoles,
|
||||
primaryRole,
|
||||
sortKey: primaryRole?.order ?? -Infinity,
|
||||
})
|
||||
roleInfoByPubkey.set(pubkey, toMemberRoleInfo(pubkey, roles))
|
||||
}
|
||||
|
||||
return roleInfoByPubkey
|
||||
})
|
||||
|
||||
export const deriveSpaceMemberRoles = (url: string, targetPubkey: string) =>
|
||||
derived(
|
||||
deriveSpaceMemberRoleInfo(url),
|
||||
$spaceMemberRoles => $spaceMemberRoles.get(targetPubkey)?.roles || [],
|
||||
)
|
||||
|
||||
export const deriveGroupedSpaceMembers = (url: string, members: Readable<string[]>) =>
|
||||
derived([members, deriveSpaceMemberRoleInfo(url)], ([$members, $spaceMemberRoles]) =>
|
||||
groupMemberRoleInfos(
|
||||
$members.map(pubkey => $spaceMemberRoles.get(pubkey) || toMemberRoleInfo(pubkey, [])),
|
||||
),
|
||||
)
|
||||
|
||||
export const roleColorToCSS = (hue: number) => `oklch(0.75 0.15 ${(hue * 360) / 255})`
|
||||
|
||||
Reference in New Issue
Block a user