Browse Source

change new event button row to a drop-down list

imwald
Silberengel 3 weeks ago
parent
commit
6231fb7601
  1. 354
      src/components/PostEditor/PostContent.tsx

354
src/components/PostEditor/PostContent.tsx

@ -10,6 +10,8 @@ import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuItem, DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger DropdownMenuTrigger
} from '@/components/ui/dropdown-menu' } from '@/components/ui/dropdown-menu'
import { import {
@ -196,6 +198,7 @@ export default function PostContent({
) )
const [text, setText] = useState('') const [text, setText] = useState('')
const textareaRef = useRef<TPostTextareaHandle>(null) const textareaRef = useRef<TPostTextareaHandle>(null)
const mediaUploaderBtnRef = useRef<HTMLButtonElement>(null)
const [posting, setPosting] = useState(false) const [posting, setPosting] = useState(false)
const [uploadProgresses, setUploadProgresses] = useState< const [uploadProgresses, setUploadProgresses] = useState<
{ file: File; progress: number; cancel: () => void }[] { file: File; progress: number; cancel: () => void }[]
@ -2673,168 +2676,190 @@ export default function PostContent({
mediaImetaTags={mediaImetaTags} mediaImetaTags={mediaImetaTags}
mediaUrl={mediaUrl} mediaUrl={mediaUrl}
headerActions={ headerActions={
<> !parentEvent ? (() => {
{/* Media button - show for new posts only (replies have audio button at bottom) */} const ActiveIcon =
{!parentEvent && ( isLongFormArticle ? FileText :
<> isWikiArticle ? FileText :
<Button isWikiArticleMarkdown ? FileText :
type="button" isPublicationContent ? Book :
variant="ghost" isCitationInternal || isCitationExternal || isCitationHardcopy || isCitationPrompt ? Quote :
size="icon" isHighlight ? Highlighter :
title={t('composeModeKind1')} isPublicMessage ? MessageCircle :
className={isPlainShortNoteToolbar ? 'bg-accent' : ''} isPoll ? ListTodo :
aria-pressed={isPlainShortNoteToolbar} isDiscussionThread ? MessagesSquare :
onClick={handlePlainNoteMode} mediaNoteKind !== null ? Upload :
> StickyNote
<StickyNote className="h-4 w-4" /> const activeLabel =
</Button> isLongFormArticle ? t('Long-form Article') :
<Uploader isWikiArticle ? t('Wiki Article (AsciiDoc)') :
onUploadSuccess={handleMediaUploadSuccess} isWikiArticleMarkdown ? t('Wiki Article (Markdown)') :
onUploadStart={handleUploadStart} isPublicationContent ? t('Publication Note') :
onUploadEnd={handleUploadEnd} isCitationInternal ? t('Internal Citation') :
onProgress={handleUploadProgress} isCitationExternal ? t('External Citation') :
accept="image/*,audio/*,video/*" isCitationHardcopy ? t('Hardcopy Citation') :
> isCitationPrompt ? t('Prompt Citation') :
<Button isHighlight ? t('Highlight') :
type="button" isPublicMessage ? t('Public Message') :
variant="ghost" isPoll ? t('Poll') :
size="icon" isDiscussionThread ? t('Thread') :
title={t('Upload Media')} mediaNoteKind !== null ? t('Media Note') :
className={mediaNoteKind !== null ? 'bg-accent' : ''} t('Short Note')
> return (
<Upload className="h-4 w-4" /> <DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className="gap-1.5 h-8 text-sm font-normal">
<ActiveIcon className="h-3.5 w-3.5 shrink-0" />
<span className="max-w-[120px] truncate">{activeLabel}</span>
<ChevronDown className="h-3.5 w-3.5 shrink-0 opacity-60" />
</Button> </Button>
</Uploader> </DropdownMenuTrigger>
</> <DropdownMenuContent align="end" className="w-64">
)} <DropdownMenuLabel className="text-xs font-medium text-muted-foreground px-2 py-1">
{/* Note creation buttons - only show when not replying */} {t('Note type')}
{!parentEvent && ( </DropdownMenuLabel>
<> <DropdownMenuItem onClick={handlePlainNoteMode} className="gap-3 py-2 cursor-pointer">
<Button <StickyNote className="h-4 w-4 shrink-0 text-muted-foreground" />
variant="ghost" <div className="flex flex-col flex-1 min-w-0">
size="icon" <span className="font-medium leading-none">{t('Short Note')}</span>
title={t('Create Highlight')} <span className="text-xs text-muted-foreground mt-0.5">{t('Plain text note (kind 1)')}</span>
className={isHighlight ? 'bg-accent' : ''} </div>
onClick={handleHighlightToggle} {isPlainShortNoteToolbar && <Check className="h-4 w-4 shrink-0 text-primary" />}
> </DropdownMenuItem>
<Highlighter className="h-4 w-4" /> <DropdownMenuItem onClick={() => mediaUploaderBtnRef.current?.click()} className="gap-3 py-2 cursor-pointer">
</Button> <Upload className="h-4 w-4 shrink-0 text-muted-foreground" />
<Button <div className="flex flex-col flex-1 min-w-0">
variant="ghost" <span className="font-medium leading-none">{t('Media Note')}</span>
size="icon" <span className="text-xs text-muted-foreground mt-0.5">{t('Attach image, audio, or video')}</span>
title={t('Send Public Message')} </div>
className={isPublicMessage ? 'bg-accent' : ''} {mediaNoteKind !== null && <Check className="h-4 w-4 shrink-0 text-primary" />}
onClick={handlePublicMessageToggle} </DropdownMenuItem>
> <DropdownMenuSeparator />
<MessageCircle className="h-4 w-4" /> <DropdownMenuItem onClick={handleHighlightToggle} className="gap-3 py-2 cursor-pointer">
</Button> <Highlighter className="h-4 w-4 shrink-0 text-muted-foreground" />
<Button <div className="flex flex-col flex-1 min-w-0">
variant="ghost" <span className="font-medium leading-none">{t('Highlight')}</span>
size="icon" <span className="text-xs text-muted-foreground mt-0.5">{t('Save a quote or passage')}</span>
title={t('Create Poll')} </div>
className={isPoll ? 'bg-accent' : ''} {isHighlight && <Check className="h-4 w-4 shrink-0 text-primary" />}
onClick={handlePollToggle} </DropdownMenuItem>
> <DropdownMenuItem onClick={handlePublicMessageToggle} className="gap-3 py-2 cursor-pointer">
<ListTodo className="h-4 w-4" /> <MessageCircle className="h-4 w-4 shrink-0 text-muted-foreground" />
</Button> <div className="flex flex-col flex-1 min-w-0">
<Button <span className="font-medium leading-none">{t('Public Message')}</span>
variant="ghost" <span className="text-xs text-muted-foreground mt-0.5">{t('Public direct message (kind 4)')}</span>
size="icon" </div>
title={t('Create Thread')} {isPublicMessage && <Check className="h-4 w-4 shrink-0 text-primary" />}
className={isDiscussionThread ? 'bg-accent' : ''} </DropdownMenuItem>
onClick={() => checkLogin(() => handleDiscussionThreadToggle())} <DropdownMenuItem onClick={handlePollToggle} className="gap-3 py-2 cursor-pointer">
> <ListTodo className="h-4 w-4 shrink-0 text-muted-foreground" />
<MessagesSquare className="h-4 w-4" /> <div className="flex flex-col flex-1 min-w-0">
</Button> <span className="font-medium leading-none">{t('Poll')}</span>
{/* Article dropdown - only show if has private relays for publication content */} <span className="text-xs text-muted-foreground mt-0.5">{t('Create a voting poll')}</span>
{(hasPrivateRelaysAvailable || !isPublicationContent) && ( </div>
<DropdownMenu> {isPoll && <Check className="h-4 w-4 shrink-0 text-primary" />}
<DropdownMenuTrigger asChild> </DropdownMenuItem>
<Button <DropdownMenuItem onClick={() => checkLogin(() => handleDiscussionThreadToggle())} className="gap-3 py-2 cursor-pointer">
variant="ghost" <MessagesSquare className="h-4 w-4 shrink-0 text-muted-foreground" />
size="icon" <div className="flex flex-col flex-1 min-w-0">
title={t('Create Article')} <span className="font-medium leading-none">{t('Thread')}</span>
className={ <span className="text-xs text-muted-foreground mt-0.5">{t('Start a discussion thread')}</span>
isLongFormArticle || isWikiArticle || isWikiArticleMarkdown || isPublicationContent </div>
? 'bg-accent' {isDiscussionThread && <Check className="h-4 w-4 shrink-0 text-primary" />}
: '' </DropdownMenuItem>
} <DropdownMenuSeparator />
> <DropdownMenuLabel className="text-xs font-medium text-muted-foreground px-2 py-1">
<FileText className="h-4 w-4" /> {t('Articles')}
</Button> </DropdownMenuLabel>
</DropdownMenuTrigger> <DropdownMenuItem onClick={() => handleArticleToggle('longform')} className="gap-3 py-2 cursor-pointer">
<DropdownMenuContent> <FileText className="h-4 w-4 shrink-0 text-muted-foreground" />
<DropdownMenuItem onClick={() => handleArticleToggle('longform')}> <div className="flex flex-col flex-1 min-w-0">
{t('Long-form Article')} <span className="font-medium leading-none">{t('Long-form Article')}</span>
<span className="text-xs text-muted-foreground mt-0.5">{t('Markdown article (NIP-23)')}</span>
</div>
{isLongFormArticle && <Check className="h-4 w-4 shrink-0 text-primary" />}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleArticleToggle('wiki')} className="gap-3 py-2 cursor-pointer">
<FileText className="h-4 w-4 shrink-0 text-muted-foreground" />
<div className="flex flex-col flex-1 min-w-0">
<span className="font-medium leading-none">{t('Wiki Article (AsciiDoc)')}</span>
<span className="text-xs text-muted-foreground mt-0.5">{t('AsciiDoc wiki contribution')}</span>
</div>
{isWikiArticle && <Check className="h-4 w-4 shrink-0 text-primary" />}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleArticleToggle('wiki-markdown')} className="gap-3 py-2 cursor-pointer">
<FileText className="h-4 w-4 shrink-0 text-muted-foreground" />
<div className="flex flex-col flex-1 min-w-0">
<span className="font-medium leading-none">{t('Wiki Article (Markdown)')}</span>
<span className="text-xs text-muted-foreground mt-0.5">{t('Markdown wiki contribution')}</span>
</div>
{isWikiArticleMarkdown && <Check className="h-4 w-4 shrink-0 text-primary" />}
</DropdownMenuItem>
{hasPrivateRelaysAvailable && (
<DropdownMenuItem onClick={() => handleArticleToggle('publication')} className="gap-3 py-2 cursor-pointer">
<Book className="h-4 w-4 shrink-0 text-muted-foreground" />
<div className="flex flex-col flex-1 min-w-0">
<span className="font-medium leading-none">{t('Publication Note')}</span>
<span className="text-xs text-muted-foreground mt-0.5">{t('Private relay publication')}</span>
</div>
{isPublicationContent && <Check className="h-4 w-4 shrink-0 text-primary" />}
</DropdownMenuItem>
)}
<DropdownMenuSeparator />
<DropdownMenuLabel className="text-xs font-medium text-muted-foreground px-2 py-1">
{t('Citations')}
</DropdownMenuLabel>
{hasPrivateRelaysAvailable ? (
<>
<DropdownMenuItem onClick={() => handleCitationToggle('internal')} className="gap-3 py-2 cursor-pointer">
<Quote className="h-4 w-4 shrink-0 text-muted-foreground" />
<div className="flex flex-col flex-1 min-w-0">
<span className="font-medium leading-none">{t('Internal Citation')}</span>
<span className="text-xs text-muted-foreground mt-0.5">{t('Cite from private relay')}</span>
</div>
{isCitationInternal && <Check className="h-4 w-4 shrink-0 text-primary" />}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => handleArticleToggle('wiki')}> <DropdownMenuItem onClick={() => handleCitationToggle('external')} className="gap-3 py-2 cursor-pointer">
{t('Wiki Article (AsciiDoc)')} <Quote className="h-4 w-4 shrink-0 text-muted-foreground" />
<div className="flex flex-col flex-1 min-w-0">
<span className="font-medium leading-none">{t('External Citation')}</span>
<span className="text-xs text-muted-foreground mt-0.5">{t('Cite from external source')}</span>
</div>
{isCitationExternal && <Check className="h-4 w-4 shrink-0 text-primary" />}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => handleArticleToggle('wiki-markdown')}> <DropdownMenuItem onClick={() => handleCitationToggle('hardcopy')} className="gap-3 py-2 cursor-pointer">
{t('Wiki Article (Markdown)')} <Quote className="h-4 w-4 shrink-0 text-muted-foreground" />
<div className="flex flex-col flex-1 min-w-0">
<span className="font-medium leading-none">{t('Hardcopy Citation')}</span>
<span className="text-xs text-muted-foreground mt-0.5">{t('Physical source citation')}</span>
</div>
{isCitationHardcopy && <Check className="h-4 w-4 shrink-0 text-primary" />}
</DropdownMenuItem> </DropdownMenuItem>
{hasPrivateRelaysAvailable && ( <DropdownMenuItem onClick={() => handleCitationToggle('prompt')} className="gap-3 py-2 cursor-pointer">
<DropdownMenuItem onClick={() => handleArticleToggle('publication')}> <Quote className="h-4 w-4 shrink-0 text-muted-foreground" />
{t('Take a note')} <div className="flex flex-col flex-1 min-w-0">
</DropdownMenuItem> <span className="font-medium leading-none">{t('Prompt Citation')}</span>
)} <span className="text-xs text-muted-foreground mt-0.5">{t('AI / LLM prompt citation')}</span>
</DropdownMenuContent> </div>
</DropdownMenu> {isCitationPrompt && <Check className="h-4 w-4 shrink-0 text-primary" />}
)} </DropdownMenuItem>
{/* Citations (private relays) */} </>
<DropdownMenu> ) : (
<DropdownMenuTrigger asChild> <div className="px-2 py-1.5 text-xs text-muted-foreground">
<Button {t('Citations require private relays (NIP-65).')}
variant="ghost" </div>
size="icon" )}
title={t('Create Citation')} <DropdownMenuSeparator />
className={ <DropdownMenuItem onClick={() => checkLogin(() => setCreateCustomEventOpen(true))} className="gap-3 py-2 cursor-pointer">
isCitationInternal || <HelpCircle className="h-4 w-4 shrink-0 text-muted-foreground" />
isCitationExternal || <div className="flex flex-col flex-1 min-w-0">
isCitationHardcopy || <span className="font-medium leading-none">{t('Custom Event')}</span>
isCitationPrompt <span className="text-xs text-muted-foreground mt-0.5">{t('Create event with custom kind')}</span>
? 'bg-accent' </div>
: '' </DropdownMenuItem>
} </DropdownMenuContent>
> </DropdownMenu>
<Quote className="h-4 w-4" /> )
</Button> })() : undefined
</DropdownMenuTrigger>
<DropdownMenuContent>
{hasPrivateRelaysAvailable ? (
<>
<DropdownMenuItem onClick={() => handleCitationToggle('internal')}>
{t('Internal Citation')}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleCitationToggle('external')}>
{t('External Citation')}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleCitationToggle('hardcopy')}>
{t('Hardcopy Citation')}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleCitationToggle('prompt')}>
{t('Prompt Citation')}
</DropdownMenuItem>
</>
) : (
<div className="px-2 py-1.5 text-xs text-muted-foreground max-w-[14rem]">
{t('Citations require private relays (NIP-65).')}
</div>
)}
</DropdownMenuContent>
</DropdownMenu>
<Button
type="button"
variant="ghost"
size="icon"
title={t('Create event with custom kind')}
onClick={() => checkLogin(() => setCreateCustomEventOpen(true))}
>
<HelpCircle className="h-4 w-4" />
</Button>
</>
)}
</>
} }
/> />
{isDiscussionThread && !parentEvent && ( {isDiscussionThread && !parentEvent && (
@ -2930,6 +2955,19 @@ export default function PostContent({
)} )}
</div> </div>
)} )}
{/* Hidden uploader for the "Media Note" dropdown item */}
{!parentEvent && (
<Uploader
onUploadSuccess={handleMediaUploadSuccess}
onUploadStart={handleUploadStart}
onUploadEnd={handleUploadEnd}
onProgress={handleUploadProgress}
accept="image/*,audio/*,video/*"
className="sr-only"
>
<button ref={mediaUploaderBtnRef} type="button" aria-hidden="true" tabIndex={-1} />
</Uploader>
)}
<div className="flex flex-wrap items-center justify-between gap-2 min-w-0"> <div className="flex flex-wrap items-center justify-between gap-2 min-w-0">
<div className="flex gap-2 items-center min-w-0 shrink-0"> <div className="flex gap-2 items-center min-w-0 shrink-0">
{/* Audio button for replies and new PMs - placed before image button */} {/* Audio button for replies and new PMs - placed before image button */}

Loading…
Cancel
Save