Browse Source

fix janky reply form

imwald
Silberengel 2 weeks ago
parent
commit
55ce27d272
  1. 30
      src/components/PostEditor/PostContent.tsx
  2. 16
      src/components/PostEditor/PostTextarea/index.tsx
  3. 18
      src/components/PostEditor/index.tsx

30
src/components/PostEditor/PostContent.tsx

@ -47,6 +47,7 @@ import {
} from '@/constants' } from '@/constants'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { useNostr } from '@/providers/NostrProvider' import { useNostr } from '@/providers/NostrProvider'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { useReplyIngress } from '@/hooks/useReplyIngress' import { useReplyIngress } from '@/hooks/useReplyIngress'
import { canonicalizeRssArticleUrl, getArticleUrlFromCommentITags } from '@/lib/rss-article' import { canonicalizeRssArticleUrl, getArticleUrlFromCommentITags } from '@/lib/rss-article'
import { cleanUrl, isBlossomBudBlobUrl, rewritePlainTextHttpUrls } from '@/lib/url' import { cleanUrl, isBlossomBudBlobUrl, rewritePlainTextHttpUrls } from '@/lib/url'
@ -200,6 +201,7 @@ export default function PostContent({
}) { }) {
const { t, i18n } = useTranslation() const { t, i18n } = useTranslation()
const { pubkey, publish, checkLogin, canSignEvents } = useNostr() const { pubkey, publish, checkLogin, canSignEvents } = useNostr()
const { isSmallScreen } = useScreenSize()
const { addReplies } = useReplyIngress() const { addReplies } = useReplyIngress()
const mergePublishedReplyIntoThread = useCallback( const mergePublishedReplyIntoThread = useCallback(
@ -2519,7 +2521,19 @@ export default function PostContent({
} }
return ( return (
<div className="space-y-2 min-w-0"> <div
className={cn(
'min-w-0',
isSmallScreen ? 'flex min-h-0 flex-1 flex-col' : 'space-y-2'
)}
>
<NeventPickerProvider>
<div
className={cn(
'space-y-2 min-w-0',
isSmallScreen && 'min-h-0 flex-1 overflow-y-auto overscroll-y-contain'
)}
>
{/* Dynamic Title based on mode */} {/* Dynamic Title based on mode */}
<div className="text-lg font-semibold"> <div className="text-lg font-semibold">
{(() => { {(() => {
@ -2569,7 +2583,7 @@ export default function PostContent({
</div> </div>
{parentEvent && ( {parentEvent && (
<ScrollArea className="flex max-h-48 flex-col overflow-y-auto rounded-lg border bg-muted/40"> <ScrollArea className="flex max-h-32 sm:max-h-48 flex-col overflow-y-auto rounded-lg border bg-muted/40">
<div className="p-2 sm:p-3 pointer-events-none"> <div className="p-2 sm:p-3 pointer-events-none">
<Note size="small" event={parentEvent} hideParentNotePreview /> <Note size="small" event={parentEvent} hideParentNotePreview />
</div> </div>
@ -3355,7 +3369,6 @@ export default function PostContent({
</div> </div>
)} )}
<NeventPickerProvider>
<PostTextarea <PostTextarea
ref={textareaRef} ref={textareaRef}
text={text} text={text}
@ -3364,7 +3377,7 @@ export default function PostContent({
parentEvent={isDiscussionThread && !parentEvent ? THREAD_POST_EDITOR_PARENT : parentEvent} parentEvent={isDiscussionThread && !parentEvent ? THREAD_POST_EDITOR_PARENT : parentEvent}
onSubmit={() => post()} onSubmit={() => post()}
className={cn( className={cn(
isPoll ? 'min-h-20' : 'min-h-52', isPoll ? 'min-h-20' : isSmallScreen ? 'min-h-36' : 'min-h-52',
isDiscussionThread && threadErrors.content && 'border-destructive' isDiscussionThread && threadErrors.content && 'border-destructive'
)} )}
onUploadStart={handleUploadStart} onUploadStart={handleUploadStart}
@ -3718,6 +3731,14 @@ export default function PostContent({
<button ref={mediaUploaderBtnRef} type="button" aria-hidden="true" tabIndex={-1} /> <button ref={mediaUploaderBtnRef} type="button" aria-hidden="true" tabIndex={-1} />
</Uploader> </Uploader>
)} )}
</div>
<div
className={cn(
'space-y-2 min-w-0',
isSmallScreen &&
'sticky bottom-0 z-10 shrink-0 border-t border-border bg-background pt-2 pb-[max(0.5rem,env(safe-area-inset-bottom,0px))]'
)}
>
<div className="flex min-w-0 w-full items-center gap-1.5"> <div className="flex min-w-0 w-full items-center gap-1.5">
<div className="min-w-0 flex-1 overflow-x-auto overscroll-x-contain"> <div className="min-w-0 flex-1 overflow-x-auto overscroll-x-contain">
<PostEditorFormatToolbar <PostEditorFormatToolbar
@ -3873,6 +3894,7 @@ export default function PostContent({
inComposer inComposer
/> />
) : null} ) : null}
</div>
{/* Media Kind Selection Dialog */} {/* Media Kind Selection Dialog */}
<Dialog open={showMediaKindDialog} onOpenChange={setShowMediaKindDialog}> <Dialog open={showMediaKindDialog} onOpenChange={setShowMediaKindDialog}>

16
src/components/PostEditor/PostTextarea/index.tsx

@ -13,10 +13,12 @@ import Text from '@tiptap/extension-text'
import { TextSelection } from '@tiptap/pm/state' import { TextSelection } from '@tiptap/pm/state'
import { Editor, EditorContent, useEditor } from '@tiptap/react' import { Editor, EditorContent, useEditor } from '@tiptap/react'
import { Event } from 'nostr-tools' import { Event } from 'nostr-tools'
import { useScreenSizeOptional } from '@/providers/ScreenSizeProvider'
import { import {
Dispatch, Dispatch,
forwardRef, forwardRef,
SetStateAction, SetStateAction,
useEffect,
useImperativeHandle, useImperativeHandle,
useMemo, useMemo,
useRef, useRef,
@ -122,6 +124,7 @@ const PostTextarea = forwardRef<
ref ref
) => { ) => {
const { t } = useTranslation() const { t } = useTranslation()
const isSmallScreen = useScreenSizeOptional()?.isSmallScreen ?? false
const onUploadSuccessRef = useRef(onUploadSuccess) const onUploadSuccessRef = useRef(onUploadSuccess)
onUploadSuccessRef.current = onUploadSuccess onUploadSuccessRef.current = onUploadSuccess
const onUploadCompressPhaseRef = useRef(onUploadCompressPhase) const onUploadCompressPhaseRef = useRef(onUploadCompressPhase)
@ -197,6 +200,19 @@ const PostTextarea = forwardRef<
editorRef.current = editor editorRef.current = editor
useEffect(() => {
if (!editor || !isSmallScreen) return
const scrollEditorIntoView = () => {
requestAnimationFrame(() => {
editor.view.dom.scrollIntoView({ block: 'nearest', inline: 'nearest' })
})
}
editor.on('focus', scrollEditorIntoView)
return () => {
editor.off('focus', scrollEditorIntoView)
}
}, [editor, isSmallScreen])
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
appendText: (text: string, addNewline = false) => { appendText: (text: string, addNewline = false) => {
const ed = editorRef.current const ed = editorRef.current

18
src/components/PostEditor/index.tsx

@ -98,7 +98,7 @@ export default function PostEditor({
return ( return (
<Sheet open={open} onOpenChange={setOpen}> <Sheet open={open} onOpenChange={setOpen}>
<SheetContent <SheetContent
className="h-full w-full max-w-full p-0 border-none overflow-hidden" className="flex h-[var(--vh,100dvh)] max-h-[var(--vh,100dvh)] w-full max-w-full flex-col p-0 border-none overflow-hidden data-[state=open]:duration-200 data-[state=closed]:duration-200"
side="bottom" side="bottom"
hideClose hideClose
onInteractOutside={(e) => { onInteractOutside={(e) => {
@ -114,15 +114,13 @@ export default function PostEditor({
} }
}} }}
> >
<ScrollArea className="px-4 h-full max-h-screen min-w-0 overflow-x-auto" scrollBarClassName="opacity-100"> <div className="flex min-h-0 flex-1 flex-col px-4 pt-4 pb-2 min-w-0">
<div className="space-y-4 px-2 pr-4 py-6 min-w-0"> <SheetHeader className="sr-only">
<SheetHeader className="sr-only"> <SheetTitle>Post Editor</SheetTitle>
<SheetTitle>Post Editor</SheetTitle> <SheetDescription>Create a new post or reply</SheetDescription>
<SheetDescription>Create a new post or reply</SheetDescription> </SheetHeader>
</SheetHeader> {content}
{content} </div>
</div>
</ScrollArea>
</SheetContent> </SheetContent>
</Sheet> </Sheet>
) )

Loading…
Cancel
Save