ac3cdf34b2
User-visible copy now matches the Organization rebrand. Internal
symbols, file names, query keys, routes, and storage keys are
intentionally left alone for this pass — they're still pinned to
"community" / "communities" until a dedicated rename commit.
Touched strings:
- `MobileBottomNav` and `sidebarItems` labels: "Communities" →
"Organize", matching the existing TopNav copy.
- `CommunityDetailPage` hero fallback ("Unnamed Community" →
"Unnamed Organization") and "About this organization" aria-label.
- `CommunityContent` thumbnail fallback name.
- `ExternalContentHeader.CommunityPreview` row label and fallback name.
- `NoteCard` kind-34550 noun "community" → "organization" (used in
feed-card action lines like "created an organization") and the
article switches from "a" to "an".
- `NoteMoreMenu` overflow-menu labels: "Report post to community" →
"Report post to organization", "Remove from community" → "Remove
from organization".
- `BanConfirmDialog` title, description, and success/failure toasts.
- `CommunityContentWarning` reporter pluralization and the
fallback report-type label ("community guidelines" →
"organization guidelines"); reporters are now scoped to founder /
moderators per the commit 4 cleanup, so the wording reflects that.
- `CommunityReportDialog` description copy.
- `CreateGoalDialog` placeholder example.
- `CreateActionDialog` org-scoped description string.
- `CreateCommunityEventDialog` NIP-31 `alt` tag prefix.
- `CommentContext` kind-34550 entries in the action-noun and
rendered-noun maps ("a community" / "community" → "an
organization" / "organization").
- `extraKinds` kind-34550 entry: label, description, and blurb.
- `kindLabels` kinds 4550, 10004, 34550.
- `DiscoverHero` ticker stat copy.
- `GetFeedTool` error message drops "communities" since the
Following feed no longer includes organization activity (removed
in the badge-runtime commit).
120 lines
3.8 KiB
TypeScript
120 lines
3.8 KiB
TypeScript
import { useState } from 'react';
|
|
import { useQueryClient } from '@tanstack/react-query';
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogTitle,
|
|
DialogDescription,
|
|
} from '@/components/ui/dialog';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Textarea } from '@/components/ui/textarea';
|
|
import { Label } from '@/components/ui/label';
|
|
import { useNostrPublish } from '@/hooks/useNostrPublish';
|
|
import { toast } from '@/hooks/useToast';
|
|
import {
|
|
MODERATION_BAN_LABEL,
|
|
MODERATION_LABEL_NAMESPACE,
|
|
REPORT_KIND,
|
|
} from '@/lib/communityUtils';
|
|
|
|
// ── Props ─────────────────────────────────────────────────────────────────────
|
|
//
|
|
// Only content-level bans remain. Agora's organization trust model has no
|
|
// "member" tier any more, so banning a user wholesale is no longer
|
|
// modeled — hide each unwanted post individually instead.
|
|
|
|
interface BanConfirmDialogProps {
|
|
/** The event ID to ban. */
|
|
eventId: string;
|
|
/** The event author's pubkey. */
|
|
targetPubkey: string;
|
|
/** The community `A` tag coordinate. */
|
|
communityATag: string;
|
|
/** Display name for the dialog description. */
|
|
displayName?: string;
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
}
|
|
|
|
export function BanConfirmDialog({
|
|
eventId,
|
|
targetPubkey,
|
|
communityATag,
|
|
open,
|
|
onOpenChange,
|
|
}: BanConfirmDialogProps) {
|
|
const queryClient = useQueryClient();
|
|
const { mutateAsync: publishEvent, isPending } = useNostrPublish();
|
|
const [reason, setReason] = useState('');
|
|
|
|
const handleSubmit = async () => {
|
|
try {
|
|
const tags: string[][] = [
|
|
['e', eventId, 'other'],
|
|
['p', targetPubkey, 'other'],
|
|
['A', communityATag],
|
|
['L', MODERATION_LABEL_NAMESPACE],
|
|
['l', MODERATION_BAN_LABEL, MODERATION_LABEL_NAMESPACE],
|
|
];
|
|
|
|
await publishEvent({
|
|
kind: REPORT_KIND,
|
|
content: reason.trim(),
|
|
tags,
|
|
});
|
|
|
|
// Invalidate community queries so the moderation overlay updates
|
|
// immediately (removes banned content without a page refresh).
|
|
await queryClient.invalidateQueries({ queryKey: ['community-members', communityATag] });
|
|
|
|
toast({ title: 'Post removed from organization' });
|
|
setReason('');
|
|
onOpenChange(false);
|
|
} catch {
|
|
toast({ title: 'Failed to remove post from organization', variant: 'destructive' });
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className="max-w-md rounded-2xl flex flex-col overflow-hidden">
|
|
<DialogTitle>Remove from organization</DialogTitle>
|
|
<DialogDescription className="text-sm text-muted-foreground">
|
|
This will hide the post from canonical organization views.
|
|
</DialogDescription>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="ban-reason" className="text-sm font-medium">
|
|
Reason <span className="text-muted-foreground font-normal">(optional)</span>
|
|
</Label>
|
|
<Textarea
|
|
id="ban-reason"
|
|
value={reason}
|
|
onChange={(e) => setReason(e.target.value)}
|
|
placeholder="Reason for this action..."
|
|
className="resize-none"
|
|
rows={2}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex gap-2 justify-end pt-2">
|
|
<Button
|
|
variant="ghost"
|
|
onClick={() => onOpenChange(false)}
|
|
disabled={isPending}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
onClick={handleSubmit}
|
|
disabled={isPending}
|
|
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
>
|
|
{isPending ? 'Submitting...' : 'Remove'}
|
|
</Button>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|