@@ -76,25 +58,15 @@
{/snippet}
diff --git a/src/app/members.ts b/src/app/members.ts
index d5e9b0c3..155fa042 100644
--- a/src/app/members.ts
+++ b/src/app/members.ts
@@ -22,6 +22,7 @@ import {
import type {Filter, PublishedRoomMeta, TrustedEvent} from "@welshman/util"
import {first, memoize, sortBy, spec, uniq} from "@welshman/lib"
import {addRoomMember, manageRelay, pubkey, waitForThunkError} from "@welshman/app"
+import {load} from "@welshman/net"
import {get} from "svelte/store"
import {deriveEventsForUrl, deriveRelaySignedEvents} from "@app/repository"
@@ -32,60 +33,32 @@ export const deriveSpaceMembers = (url: string) =>
export const RELAY_ROLE = 33534
-// An hsl color tuple. Any component may be an empty string, in which case the
-// client supplies its own default. Usually only `hue` is set per the spec.
-export type SpaceRoleColor = {
- hue: string // 0 to 360
- saturation: string // 0 to 1
- lightness: string // 0 to 1
-}
-
export type SpaceRole = {
id: string
label: string
description: string
- color: SpaceRoleColor
+ color: number // hue, 0 to 360
order: number
}
-// Defaults filled in for empty color components, chosen to read on both themes.
+// Defaults for the components the client supplies, chosen to read on both themes.
const DEFAULT_SATURATION = 0.7
const DEFAULT_LIGHTNESS = 0.5
-const roleColorValue = (value: string, fallback: number) => {
- const parsed = parseFloat(value)
+// Parse the hue from a ["color", hue] tag, falling back to 0.
+export const parseRoleColor = (tags: string[][]): number => {
+ const hue = parseFloat(first(getTags("color", tags))?.[1] ?? "")
- return isNaN(parsed) ? fallback : parsed
+ return isNaN(hue) ? 0 : hue
}
-// Parse the ["color", hue, saturation, lightness] tag, preserving empty values.
-export const parseRoleColor = (tags: string[][]): SpaceRoleColor => {
- const tag = first(getTags("color", tags)) ?? []
-
- return {
- hue: tag[1] ?? "",
- saturation: tag[2] ?? "",
- lightness: tag[3] ?? "",
- }
-}
-
-// Build an hsl() string from a role color, using the role's values or our defaults.
-export const roleColor = (color: SpaceRoleColor) => {
- const h = roleColorValue(color.hue, 0)
- const s = roleColorValue(color.saturation, DEFAULT_SATURATION)
- const l = roleColorValue(color.lightness, DEFAULT_LIGHTNESS)
-
- return `hsl(${h}, ${s * 100}%, ${l * 100}%)`
-}
+// Build an hsl() string from a role's hue, using our default saturation and lightness.
+export const roleColor = (hue: number) =>
+ `hsl(${hue}, ${DEFAULT_SATURATION * 100}%, ${DEFAULT_LIGHTNESS * 100}%)`
// A translucent tint of the role color for use as a background fill.
-export const roleColorSoft = (color: SpaceRoleColor) => {
- const h = roleColorValue(color.hue, 0)
- const s = roleColorValue(color.saturation, DEFAULT_SATURATION)
- const l = roleColorValue(color.lightness, DEFAULT_LIGHTNESS)
-
- return `hsl(${h}, ${s * 100}%, ${l * 100}%, 0.15)`
-}
+export const roleColorSoft = (hue: number) =>
+ `hsl(${hue}, ${DEFAULT_SATURATION * 100}%, ${DEFAULT_LIGHTNESS * 100}%, 0.15)`
export const deriveSpaceRoles = (url: string) =>
derived(deriveRelaySignedEvents(url, [{kinds: [RELAY_ROLE]}]), $events => {
@@ -100,7 +73,7 @@ export const deriveSpaceRoles = (url: string) =>
label: getTagValue("label", event.tags) ?? "",
description: getTagValue("description", event.tags) ?? "",
color: parseRoleColor(event.tags),
- order: parseInt(getTagValue("order", event.tags) ?? "0", 10) || 0,
+ order: Math.max(1, parseInt(getTagValue("order", event.tags) ?? "1", 10) || 1),
})
}
}
@@ -127,23 +100,29 @@ export const deriveSpaceMemberRoles = (url: string) =>
return memberRoles
})
-// Flatten a color tuple into management params, mirroring the color tag's value order.
-const roleColorParams = (color: SpaceRoleColor) =>
- [color.hue, color.saturation, color.lightness] as unknown as string
+// Pull the relay's freshly-signed role event so the UI reflects the change immediately,
+// rather than waiting for the live subscription to catch up.
+const reloadRole = (url: string, id: string) =>
+ load({relays: [url], filters: [{kinds: [RELAY_ROLE], "#d": [id]}]})
export const createRole = async (
url: string,
id: string,
label: string,
description: string,
- color: SpaceRoleColor,
+ color: number,
order: number,
): Promise
=> {
const {error} = await manageRelay(url, {
method: "createrole" as ManagementMethod,
- params: [id, label, description, roleColorParams(color), order.toString()],
+ // hue and order go over the wire as JSON numbers, not strings, per the relay's NIP-86 impl.
+ params: [id, label, description, color, order] as unknown as string[],
})
+ if (!error) {
+ await reloadRole(url, id)
+ }
+
return error
}
@@ -152,14 +131,19 @@ export const editRole = async (
id: string,
label: string,
description: string,
- color: SpaceRoleColor,
+ color: number,
order: number,
): Promise => {
const {error} = await manageRelay(url, {
method: "editrole" as ManagementMethod,
- params: [id, label, description, roleColorParams(color), order.toString()],
+ // hue and order go over the wire as JSON numbers, not strings, per the relay's NIP-86 impl.
+ params: [id, label, description, color, order] as unknown as string[],
})
+ if (!error) {
+ await reloadRole(url, id)
+ }
+
return error
}