index.tsx raw

   1  import {
   2    Dialog,
   3    DialogContent,
   4    DialogDescription,
   5    DialogHeader,
   6    DialogTitle
   7  } from '@/components/ui/dialog'
   8  import {
   9    Sheet,
  10    SheetContent,
  11    SheetDescription,
  12    SheetHeader,
  13    SheetTitle
  14  } from '@/components/ui/sheet'
  15  import { useVisualViewportHeight } from '@/hooks/useVisualViewportHeight'
  16  import { usePrimaryPage } from '@/PageManager'
  17  import { useScreenSize } from '@/providers/ScreenSizeProvider'
  18  import postEditor from '@/services/post-editor.service'
  19  import { ArrowLeft, X } from 'lucide-react'
  20  import { Event } from 'nostr-tools'
  21  import { Dispatch, useMemo, useRef } from 'react'
  22  import { useTranslation } from 'react-i18next'
  23  import PostContent, { TPostContentHandle } from './PostContent'
  24  import Title from './Title'
  25  
  26  export default function PostEditor({
  27    defaultContent = '',
  28    parentStuff,
  29    open,
  30    setOpen,
  31    highlightedText,
  32    inline = false
  33  }: {
  34    defaultContent?: string
  35    parentStuff?: Event | string
  36    open: boolean
  37    setOpen: Dispatch<boolean>
  38    highlightedText?: string
  39    inline?: boolean
  40  }) {
  41    const { t } = useTranslation()
  42    const { isSmallScreen } = useScreenSize()
  43    const { navigate } = usePrimaryPage()
  44    const viewportHeight = useVisualViewportHeight()
  45    const contentRef = useRef<TPostContentHandle>(null)
  46  
  47    const handleReset = () => {
  48      contentRef.current?.reset()
  49    }
  50  
  51    const content = useMemo(() => {
  52      return (
  53        <PostContent
  54          ref={contentRef}
  55          defaultContent={defaultContent}
  56          parentStuff={parentStuff}
  57          close={() => {
  58            setOpen(false)
  59            navigate('home')
  60          }}
  61          highlightedText={highlightedText}
  62        />
  63      )
  64    }, [highlightedText, navigate])
  65  
  66    if (inline) {
  67      if (!open) return null
  68      return (
  69        <div className="border-t border-b bg-card animate-in slide-in-from-top-2 fade-in duration-200">
  70          <div className="flex items-center h-10 px-3 border-b shrink-0">
  71            <div className="flex-1 text-sm font-medium truncate">
  72              {highlightedText ? t('Create Highlight') : <Title parentStuff={parentStuff} />}
  73            </div>
  74            <div className="flex items-center gap-1">
  75              <button
  76                onClick={handleReset}
  77                className="flex items-center justify-center w-8 h-8 rounded hover:bg-accent transition-colors text-sm"
  78                aria-label="Reset"
  79              >
  80                🧹
  81              </button>
  82              <button
  83                onClick={() => setOpen(false)}
  84                className="flex items-center justify-center w-8 h-8 rounded hover:bg-accent transition-colors"
  85                aria-label="Close"
  86              >
  87                <X className="w-4 h-4" />
  88              </button>
  89            </div>
  90          </div>
  91          <div className="px-4 py-3">{content}</div>
  92        </div>
  93      )
  94    }
  95  
  96    if (isSmallScreen) {
  97      return (
  98        <Sheet open={open} onOpenChange={setOpen}>
  99          <SheetContent
 100            className="w-full p-0 border-none flex flex-col"
 101            style={{ height: `${viewportHeight}px` }}
 102            side="bottom"
 103            hideClose
 104            onEscapeKeyDown={(e) => {
 105              if (postEditor.isSuggestionPopupOpen) {
 106                e.preventDefault()
 107                postEditor.closeSuggestionPopup()
 108              }
 109            }}
 110          >
 111            <div className="flex items-center h-12 border-b shrink-0">
 112              <button
 113                onClick={() => setOpen(false)}
 114                className="flex items-center justify-center w-10 h-full hover:bg-accent transition-colors"
 115                aria-label="Close"
 116              >
 117                <ArrowLeft className="w-5 h-5" />
 118              </button>
 119              <SheetHeader className="flex-1">
 120                <SheetTitle className="text-start text-base font-medium">
 121                  {highlightedText ? t('Create Highlight') : <Title parentStuff={parentStuff} />}
 122                </SheetTitle>
 123                <SheetDescription className="hidden" />
 124              </SheetHeader>
 125              <button
 126                onClick={handleReset}
 127                className="flex items-center justify-center w-10 h-full hover:bg-accent transition-colors"
 128                aria-label="Reset"
 129              >
 130                🧹
 131              </button>
 132            </div>
 133            <div className="flex flex-col flex-1 min-h-0 px-4 py-4">{content}</div>
 134          </SheetContent>
 135        </Sheet>
 136      )
 137    }
 138  
 139    return (
 140      <Dialog open={open} onOpenChange={setOpen}>
 141        <DialogContent
 142          className="p-0 max-w-2xl flex flex-col overflow-hidden"
 143          style={{ maxHeight: `${Math.min(viewportHeight * 0.9, viewportHeight)}px` }}
 144          withoutClose
 145          onEscapeKeyDown={(e) => {
 146            if (postEditor.isSuggestionPopupOpen) {
 147              e.preventDefault()
 148              postEditor.closeSuggestionPopup()
 149            }
 150          }}
 151        >
 152          <div className="px-6 pt-6 pb-2 shrink-0">
 153            <DialogHeader className="flex flex-row items-center justify-between">
 154              <DialogTitle>
 155                {highlightedText ? t('Create Highlight') : <Title parentStuff={parentStuff} />}
 156              </DialogTitle>
 157              <button
 158                onClick={handleReset}
 159                className="flex items-center justify-center w-8 h-8 rounded hover:bg-accent transition-colors"
 160                aria-label="Reset"
 161              >
 162                🧹
 163              </button>
 164              <DialogDescription className="hidden" />
 165            </DialogHeader>
 166          </div>
 167          <div className="flex-1 min-h-0 overflow-y-auto px-6 pb-6">
 168            {content}
 169          </div>
 170        </DialogContent>
 171      </Dialog>
 172    )
 173  }
 174