feat: add renderers for NIP-43 Add/Remove User events

- Change kind 13534 icon from Shield to Users for consistency
- Add feed and detail renderers for kind 8000 (Add User)
- Add feed and detail renderers for kind 8001 (Remove User)
- Both show the affected pubkey using PubkeyListFull component
This commit is contained in:
Claude
2026-01-15 11:16:56 +00:00
parent 5b83212915
commit 61f5543773
4 changed files with 151 additions and 4 deletions

View File

@@ -1,4 +1,4 @@
import { Shield, Users } from "lucide-react";
import { Users } from "lucide-react";
import { getTagValues } from "@/lib/nostr-utils";
import {
BaseEventProps,
@@ -35,7 +35,7 @@ export function RelayMembersRenderer({ event }: BaseEventProps) {
event={event}
className="flex items-center gap-1.5 text-sm font-medium"
>
<Shield className="size-4 text-muted-foreground" />
<Users className="size-4 text-muted-foreground" />
<span>Relay Members</span>
</ClickableEventTitle>
@@ -61,7 +61,7 @@ export function RelayMembersDetailRenderer({ event }: { event: NostrEvent }) {
return (
<div className="flex flex-col gap-6 p-4">
<div className="flex items-center gap-2">
<Shield className="size-6 text-muted-foreground" />
<Users className="size-6 text-muted-foreground" />
<span className="text-lg font-semibold">Relay Members</span>
</div>

View File

@@ -0,0 +1,137 @@
import { UserPlus, UserMinus } from "lucide-react";
import { getTagValue } from "applesauce-core/helpers";
import {
BaseEventProps,
BaseEventContainer,
ClickableEventTitle,
} from "./BaseEventRenderer";
import { PubkeyListFull } from "../lists";
import type { NostrEvent } from "@/types/nostr";
/**
* Kind 8000 Renderer - Add User (Feed View)
* NIP-43 event published when a member is added to a relay
*/
export function AddUserRenderer({ event }: BaseEventProps) {
const addedPubkey = getTagValue(event, "p");
if (!addedPubkey) {
return (
<BaseEventContainer event={event}>
<div className="text-xs text-muted-foreground italic">
Invalid add user event (missing pubkey)
</div>
</BaseEventContainer>
);
}
return (
<BaseEventContainer event={event}>
<div className="flex flex-col gap-2">
<ClickableEventTitle
event={event}
className="flex items-center gap-1.5 text-sm font-medium"
>
<UserPlus className="size-4 text-muted-foreground" />
<span>User Added</span>
</ClickableEventTitle>
<div className="text-xs text-muted-foreground">
Member added to relay
</div>
</div>
</BaseEventContainer>
);
}
/**
* Kind 8000 Detail View - Full add user details
*/
export function AddUserDetailRenderer({ event }: { event: NostrEvent }) {
const addedPubkey = getTagValue(event, "p");
return (
<div className="flex flex-col gap-6 p-4">
<div className="flex items-center gap-2">
<UserPlus className="size-6 text-muted-foreground" />
<span className="text-lg font-semibold">User Added</span>
</div>
{addedPubkey ? (
<PubkeyListFull
pubkeys={[addedPubkey]}
label="Added Member"
icon={<UserPlus className="size-5" />}
/>
) : (
<div className="text-sm text-muted-foreground italic">
Invalid event (missing pubkey)
</div>
)}
</div>
);
}
/**
* Kind 8001 Renderer - Remove User (Feed View)
* NIP-43 event published when a member is removed from a relay
*/
export function RemoveUserRenderer({ event }: BaseEventProps) {
const removedPubkey = getTagValue(event, "p");
if (!removedPubkey) {
return (
<BaseEventContainer event={event}>
<div className="text-xs text-muted-foreground italic">
Invalid remove user event (missing pubkey)
</div>
</BaseEventContainer>
);
}
return (
<BaseEventContainer event={event}>
<div className="flex flex-col gap-2">
<ClickableEventTitle
event={event}
className="flex items-center gap-1.5 text-sm font-medium"
>
<UserMinus className="size-4 text-muted-foreground" />
<span>User Removed</span>
</ClickableEventTitle>
<div className="text-xs text-muted-foreground">
Member removed from relay
</div>
</div>
</BaseEventContainer>
);
}
/**
* Kind 8001 Detail View - Full remove user details
*/
export function RemoveUserDetailRenderer({ event }: { event: NostrEvent }) {
const removedPubkey = getTagValue(event, "p");
return (
<div className="flex flex-col gap-6 p-4">
<div className="flex items-center gap-2">
<UserMinus className="size-6 text-muted-foreground" />
<span className="text-lg font-semibold">User Removed</span>
</div>
{removedPubkey ? (
<PubkeyListFull
pubkeys={[removedPubkey]}
label="Removed Member"
icon={<UserMinus className="size-5" />}
/>
) : (
<div className="text-sm text-muted-foreground italic">
Invalid event (missing pubkey)
</div>
)}
</div>
);
}

View File

@@ -71,6 +71,12 @@ import {
RelayMembersRenderer,
RelayMembersDetailRenderer,
} from "./RelayMembersRenderer";
import {
AddUserRenderer,
AddUserDetailRenderer,
RemoveUserRenderer,
RemoveUserDetailRenderer,
} from "./RelayUserChangeRenderer";
// NIP-51 List Renderers
import { MuteListRenderer, MuteListDetailRenderer } from "./MuteListRenderer";
import { PinListRenderer, PinListDetailRenderer } from "./PinListRenderer";
@@ -163,6 +169,8 @@ const kindRenderers: Record<number, React.ComponentType<BaseEventProps>> = {
1621: IssueRenderer, // Issue (NIP-34)
9735: Kind9735Renderer, // Zap Receipt
9802: Kind9802Renderer, // Highlight
8000: AddUserRenderer, // Add User (NIP-43)
8001: RemoveUserRenderer, // Remove User (NIP-43)
777: SpellRenderer, // Spell (Grimoire)
10000: MuteListRenderer, // Mute List (NIP-51)
10001: PinListRenderer, // Pin List (NIP-51)
@@ -260,6 +268,8 @@ const detailRenderers: Record<
1618: PullRequestDetailRenderer, // Pull Request Detail (NIP-34)
1621: IssueDetailRenderer, // Issue Detail (NIP-34)
9802: Kind9802DetailRenderer, // Highlight Detail
8000: AddUserDetailRenderer, // Add User Detail (NIP-43)
8001: RemoveUserDetailRenderer, // Remove User Detail (NIP-43)
10000: MuteListDetailRenderer, // Mute List Detail (NIP-51)
10001: PinListDetailRenderer, // Pin List Detail (NIP-51)
10002: Kind10002DetailRenderer, // Relay List Detail (NIP-65)

View File

@@ -892,7 +892,7 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
name: "Relay Members",
description: "Relay membership list",
nip: "43",
icon: Shield,
icon: Users,
},
// 17375: {
// kind: 17375,