Files
eranos/src/components/MutedContentGuard.tsx
T
Chad Curtis d24f8c345a Add MutedContentGuard for post detail pages
Show a dismissible 'Muted Content' overlay instead of rendering muted
posts directly when navigating to their detail page. Follows the same
pattern as ContentWarningGuard — children are not mounted until the
user clicks 'Show Anyway', so media and nested queries are deferred.
2026-02-21 04:10:50 -06:00

59 lines
2.1 KiB
TypeScript

import { useState } from 'react';
import { VolumeX, Eye } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { useMuteList } from '@/hooks/useMuteList';
import { isEventMuted } from '@/lib/muteHelpers';
import { cn } from '@/lib/utils';
import type { NostrEvent } from '@nostrify/nostrify';
interface MutedContentGuardProps {
/** The Nostr event to check against the mute list. */
event: NostrEvent;
/** Content that should only render when the user dismisses the guard. */
children: React.ReactNode;
/** Optional class name for the guard container. */
className?: string;
}
/**
* Guards children behind a muted-content overlay when the event matches
* the user's mute list. Used on detail pages where the user navigated
* directly to a muted post — feeds hide muted content entirely instead.
*
* Children are **not mounted** until the user explicitly reveals, so
* media and nested queries are not fetched for muted content.
*/
export function MutedContentGuard({ event, children, className }: MutedContentGuardProps) {
const { muteItems } = useMuteList();
const [revealed, setRevealed] = useState(false);
if (revealed || muteItems.length === 0 || !isEventMuted(event, muteItems)) {
return <>{children}</>;
}
return (
<div className={cn('px-4 py-12', className)}>
<div className="max-w-sm mx-auto flex flex-col items-center text-center gap-3">
<div className="flex items-center justify-center size-10 rounded-full bg-muted">
<VolumeX className="size-5 text-muted-foreground" />
</div>
<div className="space-y-1">
<p className="text-sm font-medium text-foreground">Muted Content</p>
<p className="text-xs text-muted-foreground leading-relaxed">
This post is from a user or conversation you've muted.
</p>
</div>
<Button
variant="outline"
size="sm"
className="gap-1.5 mt-1 rounded-full px-5"
onClick={() => setRevealed(true)}
>
<Eye className="size-3.5" />
Show Anyway
</Button>
</div>
</div>
);
}