Skip to content

Commit d2acebc

Browse files
committed
fix: auth checks in reaction selector
1 parent ef8e3cf commit d2acebc

File tree

6 files changed

+24
-73
lines changed

6 files changed

+24
-73
lines changed

web/src/components/MemoView/MemoView.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
11
import { memo, useMemo, useRef, useState } from "react";
2+
import useCurrentUser from "@/hooks/useCurrentUser";
23
import { useUser } from "@/hooks/useUserQueries";
34
import { cn } from "@/lib/utils";
5+
import { State } from "@/types/proto/api/v1/common_pb";
6+
import { isSuperUser } from "@/utils/user";
47
import MemoEditor from "../MemoEditor";
58
import PreviewImageDialog from "../PreviewImageDialog";
69
import { MemoBody, MemoHeader } from "./components";
710
import { MEMO_CARD_BASE_CLASSES } from "./constants";
8-
import { useImagePreview, useMemoActions, useMemoHandlers, useMemoViewDerivedState, useNsfwContent } from "./hooks";
11+
import { useImagePreview, useMemoActions, useMemoHandlers, useNsfwContent } from "./hooks";
912
import { MemoViewContext } from "./MemoViewContext";
1013
import type { MemoViewProps } from "./types";
1114

1215
const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
13-
const { memo: memoData, className } = props;
16+
const { memo: memoData, className, parentPage: parentPageProp } = props;
1417
const cardRef = useRef<HTMLDivElement>(null);
15-
const [reactionSelectorOpen, setReactionSelectorOpen] = useState(false);
1618
const [showEditor, setShowEditor] = useState(false);
1719

20+
const currentUser = useCurrentUser();
1821
const creator = useUser(memoData.creator).data;
19-
const { isArchived, readonly, parentPage } = useMemoViewDerivedState(memoData, props.parentPage);
22+
const isArchived = memoData.state === State.ARCHIVED;
23+
const readonly = memoData.creator !== currentUser?.name && !isSuperUser(currentUser);
24+
const parentPage = parentPageProp || "/";
25+
2026
const { nsfw, showNSFWContent, toggleNsfwVisibility } = useNsfwContent(memoData, props.showNsfwContent);
2127
const { previewState, openPreview, setPreviewOpen } = useImagePreview();
2228
const { unpinMemo } = useMemoActions(memoData, isArchived);
@@ -37,11 +43,14 @@ const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
3743
() => ({
3844
memo: memoData,
3945
creator,
46+
currentUser,
4047
parentPage,
48+
isArchived,
49+
readonly,
4150
showNSFWContent,
4251
nsfw,
4352
}),
44-
[memoData, creator, parentPage, showNSFWContent, nsfw],
53+
[memoData, creator, currentUser, parentPage, isArchived, readonly, showNSFWContent, nsfw],
4554
);
4655

4756
if (showEditor) {
@@ -68,8 +77,6 @@ const MemoView: React.FC<MemoViewProps> = (props: MemoViewProps) => {
6877
onGotoDetail={handleGotoMemoDetailPage}
6978
onUnpin={unpinMemo}
7079
onToggleNsfwVisibility={toggleNsfwVisibility}
71-
reactionSelectorOpen={reactionSelectorOpen}
72-
onReactionSelectorOpenChange={setReactionSelectorOpen}
7380
/>
7481

7582
<MemoBody

web/src/components/MemoView/MemoViewContext.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import { timestampDate } from "@bufbuild/protobuf/wkt";
22
import { createContext, useContext } from "react";
33
import { useLocation } from "react-router-dom";
4-
import useCurrentUser from "@/hooks/useCurrentUser";
5-
import { State } from "@/types/proto/api/v1/common_pb";
64
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
75
import { MemoRelation_Type } from "@/types/proto/api/v1/memo_service_pb";
86
import type { User } from "@/types/proto/api/v1/user_service_pb";
9-
import { isSuperUser } from "@/utils/user";
107
import { RELATIVE_TIME_THRESHOLD_MS } from "./constants";
118

129
export interface MemoViewContextValue {
1310
memo: Memo;
1411
creator: User | undefined;
12+
currentUser: User | undefined;
1513
parentPage: string;
14+
isArchived: boolean;
15+
readonly: boolean;
1616
showNSFWContent: boolean;
1717
nsfw: boolean;
1818
}
@@ -28,12 +28,9 @@ export const useMemoViewContext = (): MemoViewContextValue => {
2828
};
2929

3030
export const useMemoViewDerived = () => {
31-
const { memo } = useMemoViewContext();
31+
const { memo, isArchived, readonly } = useMemoViewContext();
3232
const location = useLocation();
33-
const currentUser = useCurrentUser();
3433

35-
const isArchived = memo.state === State.ARCHIVED;
36-
const readonly = memo.creator !== currentUser?.name && !isSuperUser(currentUser);
3734
const isInMemoDetailPage = location.pathname.startsWith(`/${memo.name}`);
3835

3936
const commentAmount = memo.relations.filter(

web/src/components/MemoView/components/MemoHeader.tsx

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { timestampDate } from "@bufbuild/protobuf/wkt";
22
import { BookmarkIcon, EyeOffIcon, MessageCircleMoreIcon } from "lucide-react";
3+
import { useState } from "react";
34
import { Link } from "react-router-dom";
45
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
56
import i18n from "@/i18n";
@@ -23,13 +24,12 @@ const MemoHeader: React.FC<MemoHeaderProps> = ({
2324
onGotoDetail,
2425
onUnpin,
2526
onToggleNsfwVisibility,
26-
reactionSelectorOpen,
27-
onReactionSelectorOpenChange,
2827
}) => {
2928
const t = useTranslate();
29+
const [reactionSelectorOpen, setReactionSelectorOpen] = useState(false);
3030

31-
const { memo, creator, parentPage, showNSFWContent, nsfw } = useMemoViewContext();
32-
const { isArchived, readonly, isInMemoDetailPage, commentAmount, relativeTimeFormat } = useMemoViewDerived();
31+
const { memo, creator, currentUser, parentPage, isArchived, readonly, showNSFWContent, nsfw } = useMemoViewContext();
32+
const { isInMemoDetailPage, commentAmount, relativeTimeFormat } = useMemoViewDerived();
3333

3434
const displayTime = isArchived ? (
3535
(memo.displayTime ? timestampDate(memo.displayTime) : undefined)?.toLocaleString(i18n.language)
@@ -43,7 +43,6 @@ const MemoHeader: React.FC<MemoHeaderProps> = ({
4343

4444
return (
4545
<div className="w-full flex flex-row justify-between items-center gap-2">
46-
{/* Left section: Creator info or time */}
4746
<div className="w-auto max-w-[calc(100%-8rem)] grow flex flex-row justify-start items-center">
4847
{showCreator && creator ? (
4948
<CreatorDisplay creator={creator} displayTime={displayTime} onGotoDetail={onGotoDetail} />
@@ -52,18 +51,15 @@ const MemoHeader: React.FC<MemoHeaderProps> = ({
5251
)}
5352
</div>
5453

55-
{/* Right section: Actions */}
5654
<div className="flex flex-row justify-end items-center select-none shrink-0 gap-2">
57-
{/* Reaction selector */}
58-
{!isArchived && (
55+
{currentUser && !isArchived && (
5956
<ReactionSelector
6057
className={cn("border-none w-auto h-auto", reactionSelectorOpen && "block!", "hidden group-hover:block")}
6158
memo={memo}
62-
onOpenChange={onReactionSelectorOpenChange}
59+
onOpenChange={setReactionSelectorOpen}
6360
/>
6461
)}
6562

66-
{/* Comment count link */}
6763
{!isInMemoDetailPage && (
6864
<Link
6965
className={cn(
@@ -79,7 +75,6 @@ const MemoHeader: React.FC<MemoHeaderProps> = ({
7975
</Link>
8076
)}
8177

82-
{/* Visibility icon */}
8378
{showVisibility && memo.visibility !== Visibility.PRIVATE && (
8479
<Tooltip>
8580
<TooltipTrigger>
@@ -93,7 +88,6 @@ const MemoHeader: React.FC<MemoHeaderProps> = ({
9388
</Tooltip>
9489
)}
9590

96-
{/* Pinned indicator */}
9791
{showPinned && memo.pinned && (
9892
<TooltipProvider>
9993
<Tooltip>
@@ -109,14 +103,12 @@ const MemoHeader: React.FC<MemoHeaderProps> = ({
109103
</TooltipProvider>
110104
)}
111105

112-
{/* NSFW hide button */}
113106
{nsfw && showNSFWContent && onToggleNsfwVisibility && (
114107
<span className="cursor-pointer">
115108
<EyeOffIcon className="w-4 h-auto text-primary" onClick={onToggleNsfwVisibility} />
116109
</span>
117110
)}
118111

119-
{/* Action menu */}
120112
<MemoActionMenu memo={memo} readonly={readonly} onEdit={onEdit} />
121113
</div>
122114
</div>
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
export { useImagePreview } from "./useImagePreview";
22
export { useMemoActions } from "./useMemoActions";
33
export { useMemoHandlers } from "./useMemoHandlers";
4-
export { useMemoViewDerivedState } from "./useMemoViewDerivedState";
54
export { useNsfwContent } from "./useNsfwContent";

web/src/components/MemoView/hooks/useMemoViewDerivedState.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.

web/src/components/MemoView/types.ts

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,28 @@
11
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
22

3-
/**
4-
* Props for the MemoView component.
5-
* MemoView is the main component for displaying a memo card with all its metadata,
6-
* content, and interactive elements.
7-
*/
83
export interface MemoViewProps {
9-
/** The memo object to display */
104
memo: Memo;
11-
/** Whether to show compact view (hides some metadata) */
125
compact?: boolean;
13-
/** Whether to show the creator's profile information */
146
showCreator?: boolean;
15-
/** Whether to show the visibility indicator */
167
showVisibility?: boolean;
17-
/** Whether to show the pinned indicator */
188
showPinned?: boolean;
19-
/** Whether to show NSFW content by default */
209
showNsfwContent?: boolean;
21-
/** Additional CSS classes to apply to the root element */
2210
className?: string;
23-
/** The parent page URL for navigation context */
2411
parentPage?: string;
2512
}
2613

27-
/**
28-
* Props for the MemoHeader component.
29-
* Displays memo metadata like creator, timestamp, and action buttons.
30-
*/
3114
export interface MemoHeaderProps {
32-
// Display options
3315
showCreator?: boolean;
3416
showVisibility?: boolean;
3517
showPinned?: boolean;
36-
// Callbacks
3718
onEdit: () => void;
3819
onGotoDetail: () => void;
3920
onUnpin: () => void;
4021
onToggleNsfwVisibility?: () => void;
41-
// Reaction state
42-
reactionSelectorOpen: boolean;
43-
onReactionSelectorOpenChange: (open: boolean) => void;
4422
}
4523

46-
/**
47-
* Props for the MemoBody component.
48-
* Displays memo content, attachments, and relations.
49-
*/
5024
export interface MemoBodyProps {
51-
// Display options
5225
compact?: boolean;
53-
// Callbacks
5426
onContentClick: (e: React.MouseEvent) => void;
5527
onContentDoubleClick: (e: React.MouseEvent) => void;
5628
onToggleNsfwVisibility: () => void;

0 commit comments

Comments
 (0)