Browse Source

change new event button row to a drop-down list

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

316
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" />
</Button>
</Uploader>
</>
)}
{/* Note creation buttons - only show when not replying */}
{!parentEvent && (
<>
<Button
variant="ghost"
size="icon"
title={t('Create Highlight')}
className={isHighlight ? 'bg-accent' : ''}
onClick={handleHighlightToggle}
>
<Highlighter className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon"
title={t('Send Public Message')}
className={isPublicMessage ? 'bg-accent' : ''}
onClick={handlePublicMessageToggle}
>
<MessageCircle className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon"
title={t('Create Poll')}
className={isPoll ? 'bg-accent' : ''}
onClick={handlePollToggle}
>
<ListTodo className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon"
title={t('Create Thread')}
className={isDiscussionThread ? 'bg-accent' : ''}
onClick={() => checkLogin(() => handleDiscussionThreadToggle())}
>
<MessagesSquare className="h-4 w-4" />
</Button>
{/* Article dropdown - only show if has private relays for publication content */}
{(hasPrivateRelaysAvailable || !isPublicationContent) && (
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button <Button variant="outline" size="sm" className="gap-1.5 h-8 text-sm font-normal">
variant="ghost" <ActiveIcon className="h-3.5 w-3.5 shrink-0" />
size="icon" <span className="max-w-[120px] truncate">{activeLabel}</span>
title={t('Create Article')} <ChevronDown className="h-3.5 w-3.5 shrink-0 opacity-60" />
className={
isLongFormArticle || isWikiArticle || isWikiArticleMarkdown || isPublicationContent
? 'bg-accent'
: ''
}
>
<FileText className="h-4 w-4" />
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent align="end" className="w-64">
<DropdownMenuItem onClick={() => handleArticleToggle('longform')}> <DropdownMenuLabel className="text-xs font-medium text-muted-foreground px-2 py-1">
{t('Long-form Article')} {t('Note type')}
</DropdownMenuLabel>
<DropdownMenuItem onClick={handlePlainNoteMode} className="gap-3 py-2 cursor-pointer">
<StickyNote 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('Short Note')}</span>
<span className="text-xs text-muted-foreground mt-0.5">{t('Plain text note (kind 1)')}</span>
</div>
{isPlainShortNoteToolbar && <Check className="h-4 w-4 shrink-0 text-primary" />}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => handleArticleToggle('wiki')}> <DropdownMenuItem onClick={() => mediaUploaderBtnRef.current?.click()} className="gap-3 py-2 cursor-pointer">
{t('Wiki Article (AsciiDoc)')} <Upload 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('Media Note')}</span>
<span className="text-xs text-muted-foreground mt-0.5">{t('Attach image, audio, or video')}</span>
</div>
{mediaNoteKind !== null && <Check className="h-4 w-4 shrink-0 text-primary" />}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleHighlightToggle} className="gap-3 py-2 cursor-pointer">
<Highlighter 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('Highlight')}</span>
<span className="text-xs text-muted-foreground mt-0.5">{t('Save a quote or passage')}</span>
</div>
{isHighlight && <Check className="h-4 w-4 shrink-0 text-primary" />}
</DropdownMenuItem>
<DropdownMenuItem onClick={handlePublicMessageToggle} className="gap-3 py-2 cursor-pointer">
<MessageCircle 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('Public Message')}</span>
<span className="text-xs text-muted-foreground mt-0.5">{t('Public direct message (kind 4)')}</span>
</div>
{isPublicMessage && <Check className="h-4 w-4 shrink-0 text-primary" />}
</DropdownMenuItem>
<DropdownMenuItem onClick={handlePollToggle} className="gap-3 py-2 cursor-pointer">
<ListTodo 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('Poll')}</span>
<span className="text-xs text-muted-foreground mt-0.5">{t('Create a voting poll')}</span>
</div>
{isPoll && <Check className="h-4 w-4 shrink-0 text-primary" />}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => checkLogin(() => handleDiscussionThreadToggle())} className="gap-3 py-2 cursor-pointer">
<MessagesSquare 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('Thread')}</span>
<span className="text-xs text-muted-foreground mt-0.5">{t('Start a discussion thread')}</span>
</div>
{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">
{t('Articles')}
</DropdownMenuLabel>
<DropdownMenuItem onClick={() => handleArticleToggle('longform')} 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('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>
<DropdownMenuItem onClick={() => handleArticleToggle('wiki-markdown')}> <DropdownMenuItem onClick={() => handleArticleToggle('wiki')} className="gap-3 py-2 cursor-pointer">
{t('Wiki Article (Markdown)')} <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> </DropdownMenuItem>
{hasPrivateRelaysAvailable && ( {hasPrivateRelaysAvailable && (
<DropdownMenuItem onClick={() => handleArticleToggle('publication')}> <DropdownMenuItem onClick={() => handleArticleToggle('publication')} className="gap-3 py-2 cursor-pointer">
{t('Take a note')} <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> </DropdownMenuItem>
)} )}
</DropdownMenuContent> <DropdownMenuSeparator />
</DropdownMenu> <DropdownMenuLabel className="text-xs font-medium text-muted-foreground px-2 py-1">
)} {t('Citations')}
{/* Citations (private relays) */} </DropdownMenuLabel>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
title={t('Create Citation')}
className={
isCitationInternal ||
isCitationExternal ||
isCitationHardcopy ||
isCitationPrompt
? 'bg-accent'
: ''
}
>
<Quote className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
{hasPrivateRelaysAvailable ? ( {hasPrivateRelaysAvailable ? (
<> <>
<DropdownMenuItem onClick={() => handleCitationToggle('internal')}> <DropdownMenuItem onClick={() => handleCitationToggle('internal')} className="gap-3 py-2 cursor-pointer">
{t('Internal Citation')} <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={() => handleCitationToggle('external')}> <DropdownMenuItem onClick={() => handleCitationToggle('external')} className="gap-3 py-2 cursor-pointer">
{t('External Citation')} <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={() => handleCitationToggle('hardcopy')}> <DropdownMenuItem onClick={() => handleCitationToggle('hardcopy')} className="gap-3 py-2 cursor-pointer">
{t('Hardcopy Citation')} <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>
<DropdownMenuItem onClick={() => handleCitationToggle('prompt')}> <DropdownMenuItem onClick={() => handleCitationToggle('prompt')} className="gap-3 py-2 cursor-pointer">
{t('Prompt Citation')} <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('Prompt Citation')}</span>
<span className="text-xs text-muted-foreground mt-0.5">{t('AI / LLM prompt citation')}</span>
</div>
{isCitationPrompt && <Check className="h-4 w-4 shrink-0 text-primary" />}
</DropdownMenuItem> </DropdownMenuItem>
</> </>
) : ( ) : (
<div className="px-2 py-1.5 text-xs text-muted-foreground max-w-[14rem]"> <div className="px-2 py-1.5 text-xs text-muted-foreground">
{t('Citations require private relays (NIP-65).')} {t('Citations require private relays (NIP-65).')}
</div> </div>
)} )}
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => checkLogin(() => setCreateCustomEventOpen(true))} className="gap-3 py-2 cursor-pointer">
<HelpCircle 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('Custom Event')}</span>
<span className="text-xs text-muted-foreground mt-0.5">{t('Create event with custom kind')}</span>
</div>
</DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
<Button )
type="button" })() : undefined
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