mirror of
https://github.com/block/buzz.git
synced 2026-11-08 02:11:36 +00:00
fix: expose community icon control on open relays (#2640)
Signed-off-by: npub1cl47vfhsqpqy9pwndphpm36vcp7vvz5h2js4qpqm5yewzj7nutkq7xyw8c <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Co-authored-by: npub1cl47vfhsqpqy9pwndphpm36vcp7vvz5h2js4qpqm5yewzj7nutkq7xyw8c <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
This commit is contained in:
@@ -12,12 +12,13 @@ import { nip19 } from "nostr-tools";
|
||||
import * as React from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { canEditCommunityProfile } from "@/shared/api/relayMembers";
|
||||
import { useUsersBatchQuery } from "@/features/profile/hooks";
|
||||
import { ProfileAvatar } from "@/features/profile/ui/ProfileAvatar";
|
||||
import {
|
||||
useAddRelayMemberMutation,
|
||||
useChangeRelayMemberRoleMutation,
|
||||
useMyRelayMembershipQuery,
|
||||
useMyRelayMembershipLookupQuery,
|
||||
useRelayMembersQuery,
|
||||
useRemoveRelayMemberMutation,
|
||||
} from "@/features/community-members/hooks";
|
||||
@@ -275,9 +276,13 @@ export function CommunityMembersSettingsCard({
|
||||
}: {
|
||||
currentPubkey?: string;
|
||||
}) {
|
||||
const myMembershipQuery = useMyRelayMembershipQuery();
|
||||
const currentRole = myMembershipQuery.data?.role ?? null;
|
||||
const myMembershipQuery = useMyRelayMembershipLookupQuery();
|
||||
const currentRole = myMembershipQuery.data?.membership?.role ?? null;
|
||||
const canManageRelay = currentRole === "owner" || currentRole === "admin";
|
||||
// Open relays do not advertise NIP-43, so they cannot expose an admin role
|
||||
// snapshot. Keep the icon editor available there and let the relay enforce
|
||||
// the authoritative owner/admin check on kind 9033.
|
||||
const canEditIcon = canEditCommunityProfile(myMembershipQuery.data);
|
||||
const membersQuery = useRelayMembersQuery(canManageRelay);
|
||||
const members = React.useMemo(
|
||||
() => membersQuery.data ?? [],
|
||||
@@ -323,10 +328,22 @@ export function CommunityMembersSettingsCard({
|
||||
);
|
||||
}
|
||||
|
||||
if (!canManageRelay || !currentRole) {
|
||||
if (!canEditIcon) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!canManageRelay || !currentRole) {
|
||||
return (
|
||||
<section className="min-w-0" data-testid="settings-community-members">
|
||||
<SettingsSectionHeader
|
||||
title="Community access"
|
||||
description="Manage how this community appears to members."
|
||||
/>
|
||||
<CommunityIconSettingsCard />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
const normalizedInput = normalizeRelayPubkeyInput(pubkeyInput);
|
||||
const canGrantAdmin = currentRole === "owner";
|
||||
const canAdd =
|
||||
|
||||
@@ -3,7 +3,10 @@ import { getVersion } from "@tauri-apps/api/app";
|
||||
import { AlertCircle, ArrowLeft, LoaderCircle, RefreshCw } from "lucide-react";
|
||||
|
||||
import { useMyRelayMembershipLookupQuery } from "@/features/community-members/hooks";
|
||||
import { shouldWarnMissingMembershipSnapshot } from "@/shared/api/relayMembers";
|
||||
import {
|
||||
canEditCommunityProfile,
|
||||
shouldWarnMissingMembershipSnapshot,
|
||||
} from "@/shared/api/relayMembers";
|
||||
import { getFeature } from "@/shared/features/manifest";
|
||||
import {
|
||||
resolveEnabled,
|
||||
@@ -126,8 +129,6 @@ export function SettingsView({
|
||||
const myMembershipQuery = useMyRelayMembershipLookupQuery();
|
||||
const featureState = useFeatureSnapshot();
|
||||
const visibleSections = React.useMemo(() => {
|
||||
const membership = myMembershipQuery.data?.membership;
|
||||
|
||||
return settingsSections.filter((s) => {
|
||||
// Feature gate check. Manifest is preview-only — if the gate id is in
|
||||
// the manifest, it's preview and needs an opt-in; if it's not, it's
|
||||
@@ -138,12 +139,10 @@ export function SettingsView({
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Community members requires admin/owner role
|
||||
// Closed relays require a discovered admin/owner role. Open relays have
|
||||
// no NIP-43 snapshot, so expose only the relay-authorized profile editor.
|
||||
if (s.value === "community-members") {
|
||||
return (
|
||||
membership != null &&
|
||||
(membership.role === "owner" || membership.role === "admin")
|
||||
);
|
||||
return canEditCommunityProfile(myMembershipQuery.data);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
canEditCommunityProfile,
|
||||
loadRelayMembershipLookup,
|
||||
shouldWarnMissingMembershipSnapshot,
|
||||
} from "./relayMembers.ts";
|
||||
@@ -35,6 +36,50 @@ test("membership relays request their membership snapshot", async () => {
|
||||
assert.equal(lookup.membership?.role, "admin");
|
||||
});
|
||||
|
||||
test("community profile editing is visible on open relays", () => {
|
||||
assert.equal(
|
||||
canEditCommunityProfile({
|
||||
snapshotFound: false,
|
||||
membershipRequired: false,
|
||||
membership: null,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test("community profile editing is visible to closed-relay admins and owners", () => {
|
||||
for (const role of ["admin", "owner"]) {
|
||||
assert.equal(
|
||||
canEditCommunityProfile({
|
||||
snapshotFound: true,
|
||||
membershipRequired: true,
|
||||
membership: { pubkey: "a".repeat(64), role },
|
||||
}),
|
||||
true,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("community profile editing stays hidden while loading and from closed-relay non-admins", () => {
|
||||
assert.equal(canEditCommunityProfile(undefined), false);
|
||||
assert.equal(
|
||||
canEditCommunityProfile({
|
||||
snapshotFound: true,
|
||||
membershipRequired: true,
|
||||
membership: { pubkey: "a".repeat(64), role: "member" },
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
canEditCommunityProfile({
|
||||
snapshotFound: true,
|
||||
membershipRequired: true,
|
||||
membership: null,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test("missing snapshot warns when the relay requires membership", () => {
|
||||
assert.equal(
|
||||
shouldWarnMissingMembershipSnapshot({
|
||||
|
||||
@@ -38,6 +38,15 @@ export type RelayMembershipLookup = {
|
||||
membership: RelayMember | null;
|
||||
};
|
||||
|
||||
export function canEditCommunityProfile(
|
||||
lookup: RelayMembershipLookup | undefined,
|
||||
): boolean {
|
||||
const role = lookup?.membership?.role;
|
||||
return (
|
||||
lookup?.membershipRequired === false || role === "owner" || role === "admin"
|
||||
);
|
||||
}
|
||||
|
||||
export function shouldWarnMissingMembershipSnapshot(
|
||||
lookup: RelayMembershipLookup | undefined,
|
||||
): boolean {
|
||||
|
||||
Reference in New Issue
Block a user