Add sidebar toggle to external content pages and post 3-dots menu
ExternalContentPage (country, URL, ISBN pages) gains a 3-dots menu on the right side of the action bar with Add/Remove from sidebar. NoteMoreMenu gets an Add/Remove from sidebar item after 'Add to list', so any post can be pinned to the sidebar from its options menu. Both locations toggle between add (PanelLeft icon) and remove (Trash2 icon) based on whether the item is already in the sidebar.
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
Trash2,
|
||||
StickyNote,
|
||||
ListPlus,
|
||||
PanelLeft,
|
||||
Copy,
|
||||
Check,
|
||||
} from 'lucide-react';
|
||||
@@ -48,6 +49,7 @@ import { useCurrentUser } from '@/hooks/useCurrentUser';
|
||||
import { useAuthor } from '@/hooks/useAuthor';
|
||||
import { useMuteList } from '@/hooks/useMuteList';
|
||||
import { useDeleteEvent } from '@/hooks/useDeleteEvent';
|
||||
import { useFeedSettings } from '@/hooks/useFeedSettings';
|
||||
import { genUserName } from '@/lib/genUserName';
|
||||
import { timeAgo } from '@/lib/timeAgo';
|
||||
import { toast } from '@/hooks/useToast';
|
||||
@@ -241,9 +243,12 @@ function NoteMoreMenuContent({ event, open, onOpenChange, onReport, onMention, o
|
||||
const { addMute, removeMute, isMuted } = useMuteList();
|
||||
const userMuted = isMuted('pubkey', event.pubkey);
|
||||
const { mutate: deleteEvent, isPending: isDeleting } = useDeleteEvent();
|
||||
const { addToSidebar, removeFromSidebar, orderedItems } = useFeedSettings();
|
||||
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
|
||||
|
||||
const nip19Id = encodeEventNip19(event);
|
||||
const nostrUri = `nostr:${nip19Id}`;
|
||||
const isInSidebar = orderedItems.includes(nostrUri);
|
||||
|
||||
const close = () => onOpenChange(false);
|
||||
|
||||
@@ -264,6 +269,17 @@ function NoteMoreMenuContent({ event, open, onOpenChange, onReport, onMention, o
|
||||
close();
|
||||
};
|
||||
|
||||
const handleToggleSidebar = () => {
|
||||
if (isInSidebar) {
|
||||
removeFromSidebar(nostrUri);
|
||||
toast({ title: 'Removed from sidebar' });
|
||||
} else {
|
||||
addToSidebar(nostrUri);
|
||||
toast({ title: 'Added to sidebar' });
|
||||
}
|
||||
close();
|
||||
};
|
||||
|
||||
const handleTogglePin = () => {
|
||||
togglePin.mutate(event.id, {
|
||||
onSuccess: () => {
|
||||
@@ -383,6 +399,11 @@ function NoteMoreMenuContent({ event, open, onOpenChange, onReport, onMention, o
|
||||
onClick={() => { onAddToList(); }}
|
||||
/>
|
||||
)}
|
||||
<MenuItem
|
||||
icon={isInSidebar ? <Trash2 className="size-5" /> : <PanelLeft className="size-5" />}
|
||||
label={isInSidebar ? 'Remove from sidebar' : 'Add to sidebar'}
|
||||
onClick={handleToggleSidebar}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { useSeoMeta } from '@unhead/react';
|
||||
import { ArrowLeft, Globe, Heart, MessageSquare, Repeat2, Star, AlertTriangle } from 'lucide-react';
|
||||
import { ArrowLeft, Globe, Heart, MessageSquare, MoreHorizontal, Repeat2, Star, AlertTriangle, PanelLeft, Trash2 } from 'lucide-react';
|
||||
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@@ -9,6 +9,9 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
|
||||
import { getAvatarShape } from '@/lib/avatarShape';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||
import {
|
||||
DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { ThreadedReplyList } from '@/components/ThreadedReplyList';
|
||||
import { ComposeBox } from '@/components/ComposeBox';
|
||||
import { ReplyComposeModal } from '@/components/ReplyComposeModal';
|
||||
@@ -36,6 +39,7 @@ import { useLinkPreview } from '@/hooks/useLinkPreview';
|
||||
import { useLayoutOptions } from '@/contexts/LayoutContext';
|
||||
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
||||
import { useNostrPublish } from '@/hooks/useNostrPublish';
|
||||
import { useFeedSettings } from '@/hooks/useFeedSettings';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { getDisplayName } from '@/lib/getDisplayName';
|
||||
@@ -73,6 +77,19 @@ function ExternalActionBar({ content }: { content: ExternalContent }) {
|
||||
const queryClient = useQueryClient();
|
||||
const { toast } = useToast();
|
||||
const identifier = content.value;
|
||||
const { addToSidebar, removeFromSidebar, orderedItems } = useFeedSettings();
|
||||
|
||||
const isInSidebar = orderedItems.includes(identifier);
|
||||
|
||||
const handleAddToSidebar = useCallback(() => {
|
||||
addToSidebar(identifier);
|
||||
toast({ title: 'Added to sidebar' });
|
||||
}, [identifier, addToSidebar, toast]);
|
||||
|
||||
const handleRemoveFromSidebar = useCallback(() => {
|
||||
removeFromSidebar(identifier);
|
||||
toast({ title: 'Removed from sidebar' });
|
||||
}, [identifier, removeFromSidebar, toast]);
|
||||
|
||||
const userReactionData = useExternalUserReaction(content);
|
||||
const reactionCount = useExternalReactionCount(content);
|
||||
@@ -224,6 +241,34 @@ function ExternalActionBar({ content }: { content: ExternalContent }) {
|
||||
</BookReviewFormDialog>
|
||||
)}
|
||||
|
||||
{/* Spacer pushes the 3-dots menu to the right */}
|
||||
<div className="flex-1" />
|
||||
|
||||
{/* 3-dots menu with sidebar action */}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button
|
||||
className="p-2 rounded-full transition-colors text-muted-foreground hover:text-primary hover:bg-primary/10"
|
||||
title="More"
|
||||
>
|
||||
<MoreHorizontal className="size-5" />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-52">
|
||||
{isInSidebar ? (
|
||||
<DropdownMenuItem onClick={handleRemoveFromSidebar} className="gap-3">
|
||||
<Trash2 className="size-4" />
|
||||
Remove from sidebar
|
||||
</DropdownMenuItem>
|
||||
) : (
|
||||
<DropdownMenuItem onClick={handleAddToSidebar} className="gap-3">
|
||||
<PanelLeft className="size-4" />
|
||||
Add to sidebar
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
{shareOpen && (
|
||||
<ReplyComposeModal
|
||||
open={shareOpen}
|
||||
|
||||
Reference in New Issue
Block a user