refactor: simplify NSFW implementation by inlining logic

- Remove unused showNsfwContent prop (was only used in MemoDetail to pre-reveal NSFW, which defeats the purpose)
- Inline useNsfwContent hook logic directly into MemoView.tsx (only 3 lines, no reusability benefit)
- Delete web/src/components/MemoView/hooks/useNsfwContent.ts
- NSFW content now consistently starts hidden across all pages
- Maintains same user experience: click to reveal, no toggle back

This removes unnecessary indirection and prop threading while preserving functionality.
This commit is contained in:
Steven 2026-02-02 22:06:04 +08:00
parent 6db58fae11
commit 29412f4690
6 changed files with 9 additions and 45 deletions

View File

@ -8,7 +8,7 @@ import MemoEditor from "../MemoEditor";
import PreviewImageDialog from "../PreviewImageDialog";
import { MemoBody, MemoHeader } from "./components";
import { MEMO_CARD_BASE_CLASSES } from "./constants";
import { useImagePreview, useMemoActions, useMemoHandlers, useNsfwContent } from "./hooks";
import { useImagePreview, useMemoActions, useMemoHandlers } from "./hooks";
import { MemoViewContext } from "./MemoViewContext";
import type { MemoViewProps } from "./types";
@ -23,7 +23,11 @@ const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
const readonly = memoData.creator !== currentUser?.name && !isSuperUser(currentUser);
const parentPage = parentPageProp || "/";
const { nsfw, showNSFWContent, toggleNsfwVisibility } = useNsfwContent(memoData, props.showNsfwContent);
// NSFW content management: always blur content tagged with NSFW (case-insensitive)
const [showNSFWContent, setShowNSFWContent] = useState(false);
const nsfw = memoData.tags?.some((tag) => tag.toUpperCase() === "NSFW") ?? false;
const toggleNsfwVisibility = () => setShowNSFWContent((prev) => !prev);
const { previewState, openPreview, setPreviewOpen } = useImagePreview();
const { unpinMemo } = useMemoActions(memoData, isArchived);
@ -76,7 +80,6 @@ const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
onEdit={openEditor}
onGotoDetail={handleGotoMemoDetailPage}
onUnpin={unpinMemo}
onToggleNsfwVisibility={toggleNsfwVisibility}
/>
<MemoBody

View File

@ -1,5 +1,5 @@
import { timestampDate } from "@bufbuild/protobuf/wkt";
import { BookmarkIcon, EyeOffIcon, MessageCircleMoreIcon } from "lucide-react";
import { BookmarkIcon, MessageCircleMoreIcon } from "lucide-react";
import { useState } from "react";
import { Link } from "react-router-dom";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
@ -16,19 +16,11 @@ import VisibilityIcon from "../../VisibilityIcon";
import { useMemoViewContext, useMemoViewDerived } from "../MemoViewContext";
import type { MemoHeaderProps } from "../types";
const MemoHeader: React.FC<MemoHeaderProps> = ({
showCreator,
showVisibility,
showPinned,
onEdit,
onGotoDetail,
onUnpin,
onToggleNsfwVisibility,
}) => {
const MemoHeader: React.FC<MemoHeaderProps> = ({ showCreator, showVisibility, showPinned, onEdit, onGotoDetail, onUnpin }) => {
const t = useTranslate();
const [reactionSelectorOpen, setReactionSelectorOpen] = useState(false);
const { memo, creator, currentUser, parentPage, isArchived, readonly, showNSFWContent, nsfw } = useMemoViewContext();
const { memo, creator, currentUser, parentPage, isArchived, readonly } = useMemoViewContext();
const { isInMemoDetailPage, commentAmount, relativeTimeFormat } = useMemoViewDerived();
const displayTime = isArchived ? (
@ -100,12 +92,6 @@ const MemoHeader: React.FC<MemoHeaderProps> = ({
</TooltipProvider>
)}
{nsfw && showNSFWContent && onToggleNsfwVisibility && (
<span className="cursor-pointer">
<EyeOffIcon className="w-4 h-auto text-primary" onClick={onToggleNsfwVisibility} />
</span>
)}
<MemoActionMenu memo={memo} readonly={readonly} onEdit={onEdit} />
</div>
</div>

View File

@ -1,4 +1,3 @@
export { useImagePreview } from "./useImagePreview";
export { useMemoActions } from "./useMemoActions";
export { useMemoHandlers } from "./useMemoHandlers";
export { useNsfwContent } from "./useNsfwContent";

View File

@ -1,21 +0,0 @@
import { useState } from "react";
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
export interface UseNsfwContentReturn {
nsfw: boolean;
showNSFWContent: boolean;
toggleNsfwVisibility: () => void;
}
export const useNsfwContent = (memo: Memo, initialShowNsfw?: boolean): UseNsfwContentReturn => {
const [showNSFWContent, setShowNSFWContent] = useState(initialShowNsfw ?? false);
// Always blur content tagged with NSFW
const nsfw = memo.tags?.includes("NSFW") ?? false;
return {
nsfw,
showNSFWContent,
toggleNsfwVisibility: () => setShowNSFWContent((prev) => !prev),
};
};

View File

@ -6,7 +6,6 @@ export interface MemoViewProps {
showCreator?: boolean;
showVisibility?: boolean;
showPinned?: boolean;
showNsfwContent?: boolean;
className?: string;
parentPage?: string;
}
@ -18,7 +17,6 @@ export interface MemoHeaderProps {
onEdit: () => void;
onGotoDetail: () => void;
onUnpin: () => void;
onToggleNsfwVisibility?: () => void;
}
export interface MemoBodyProps {

View File

@ -93,7 +93,6 @@ const MemoDetail = () => {
showCreator
showVisibility
showPinned
showNsfwContent
/>
<div className="pt-8 pb-16 w-full">
<h2 id="comments" className="sr-only">