Add post preview to More menu, blur modal backdrops, polish compose modal
- NoteMoreMenu now shows a compact post preview at the top (avatar, author name, timestamp, and 3-line-clamped content) before menu items - Dialog overlay updated globally: bg-black/60 + backdrop-blur-sm for a frosted-glass effect behind all modals - FloatingComposeButton modal reworked to match the same presentation pattern: rounded-2xl, hidden default close button replaced with a centered header (X / "New post" title), separator, compose area Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
import { useState } from 'react';
|
||||
import { Pencil } from 'lucide-react';
|
||||
import { Pencil, X } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { ComposeBox } from '@/components/ComposeBox';
|
||||
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
||||
|
||||
@@ -22,10 +27,22 @@ export function FloatingComposeButton() {
|
||||
</Button>
|
||||
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent className="max-w-[480px] rounded-2xl p-0 gap-0 border-border overflow-hidden">
|
||||
<div className="flex items-center justify-between px-4 h-12 border-b border-border">
|
||||
<DialogContent className="max-w-[480px] rounded-2xl p-0 gap-0 border-border overflow-hidden [&>button]:hidden">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-4 h-12">
|
||||
<button
|
||||
onClick={() => setOpen(false)}
|
||||
className="p-1.5 -ml-1.5 rounded-full text-muted-foreground hover:text-foreground hover:bg-secondary/60 transition-colors"
|
||||
>
|
||||
<X className="size-5" />
|
||||
</button>
|
||||
<DialogTitle className="text-base font-semibold">New post</DialogTitle>
|
||||
<div className="w-8" />
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
{/* Compose area */}
|
||||
<ComposeBox onSuccess={() => setOpen(false)} placeholder="What's happening?" />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useState } from 'react';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import {
|
||||
ArrowUpDown,
|
||||
@@ -15,11 +14,14 @@ import {
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { NoteContent } from '@/components/NoteContent';
|
||||
import { useBookmarks } from '@/hooks/useBookmarks';
|
||||
import { useAuthor } from '@/hooks/useAuthor';
|
||||
import { genUserName } from '@/lib/genUserName';
|
||||
import { timeAgo } from '@/lib/timeAgo';
|
||||
import { toast } from '@/hooks/useToast';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { NostrEvent } from '@nostrify/nostrify';
|
||||
@@ -42,7 +44,7 @@ function MenuItem({ icon, label, onClick, destructive }: MenuItemProps) {
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={cn(
|
||||
'flex items-center gap-4 w-full px-5 py-3.5 text-[15px] transition-colors hover:bg-secondary/60',
|
||||
'flex items-center gap-4 w-full px-5 py-3 text-[15px] transition-colors hover:bg-secondary/60',
|
||||
destructive ? 'text-destructive' : 'text-muted-foreground',
|
||||
)}
|
||||
>
|
||||
@@ -56,9 +58,9 @@ export function NoteMoreMenu({ event, open, onOpenChange }: NoteMoreMenuProps) {
|
||||
const { isBookmarked, toggleBookmark } = useBookmarks();
|
||||
const bookmarked = isBookmarked(event.id);
|
||||
const author = useAuthor(event.pubkey);
|
||||
const displayName = author.data?.metadata?.name || genUserName(event.pubkey);
|
||||
const metadata = author.data?.metadata;
|
||||
const displayName = metadata?.name || genUserName(event.pubkey);
|
||||
|
||||
const noteId = nip19.noteEncode(event.id);
|
||||
const neventId = nip19.neventEncode({ id: event.id, author: event.pubkey });
|
||||
|
||||
const close = () => onOpenChange(false);
|
||||
@@ -111,7 +113,31 @@ export function NoteMoreMenu({ event, open, onOpenChange }: NoteMoreMenuProps) {
|
||||
<DialogContent className="max-w-md p-0 gap-0 rounded-2xl overflow-hidden [&>button]:hidden">
|
||||
<DialogTitle className="sr-only">Post options</DialogTitle>
|
||||
|
||||
<div className="py-2">
|
||||
{/* Post preview */}
|
||||
<div className="px-4 pt-4 pb-3">
|
||||
<div className="flex gap-3">
|
||||
<Avatar className="size-10 shrink-0">
|
||||
<AvatarImage src={metadata?.picture} alt={displayName} />
|
||||
<AvatarFallback className="bg-primary/20 text-primary text-sm">
|
||||
{displayName[0].toUpperCase()}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-1.5 text-sm">
|
||||
<span className="font-bold truncate">{displayName}</span>
|
||||
<span className="text-muted-foreground shrink-0">·</span>
|
||||
<span className="text-muted-foreground shrink-0 text-xs">{timeAgo(event.created_at)}</span>
|
||||
</div>
|
||||
<div className="mt-0.5 text-sm text-muted-foreground line-clamp-3">
|
||||
<NoteContent event={event} className="text-sm leading-relaxed" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="py-1">
|
||||
<MenuItem
|
||||
icon={<ArrowUpDown className="size-5" />}
|
||||
label="Show Post Details"
|
||||
@@ -136,7 +162,7 @@ export function NoteMoreMenu({ event, open, onOpenChange }: NoteMoreMenuProps) {
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="py-2">
|
||||
<div className="py-1">
|
||||
<MenuItem
|
||||
icon={<BellOff className="size-5" />}
|
||||
label="Mute Conversation"
|
||||
@@ -151,7 +177,7 @@ export function NoteMoreMenu({ event, open, onOpenChange }: NoteMoreMenuProps) {
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="py-2">
|
||||
<div className="py-1">
|
||||
<MenuItem
|
||||
icon={<VolumeX className="size-5" />}
|
||||
label={`Mute @${displayName}`}
|
||||
@@ -167,10 +193,10 @@ export function NoteMoreMenu({ event, open, onOpenChange }: NoteMoreMenuProps) {
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="py-2">
|
||||
<div className="py-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full h-auto py-3.5 text-[15px] font-medium text-muted-foreground hover:bg-secondary/60 rounded-none"
|
||||
className="w-full h-auto py-3 text-[15px] font-medium text-muted-foreground hover:bg-secondary/60 rounded-none"
|
||||
onClick={close}
|
||||
>
|
||||
Close
|
||||
|
||||
@@ -19,7 +19,7 @@ const DialogOverlay = React.forwardRef<
|
||||
<DialogPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
"fixed inset-0 z-50 bg-black/60 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
Reference in New Issue
Block a user