diff --git a/components/comment.js b/components/comment.js index 56b7b15f4a..2e3424fd39 100644 --- a/components/comment.js +++ b/components/comment.js @@ -136,6 +136,7 @@ export default function Comment ({ ? 'OPG' : null const bountyPaid = root.bountyPaidTo?.includes(Number(item.id)) + const replyRef = useRef() return (
{op}} + onQuoteReply={() => { + replyRef.current?.quoteReply?.() + }} extraInfo={ <> {includeParent && } @@ -211,7 +215,7 @@ export default function Comment ({ : (
{!noReply && - + {root.bounty && !bountyPaid && } } {children} diff --git a/components/form.js b/components/form.js index c94bbf0c96..27278baac3 100644 --- a/components/form.js +++ b/components/form.js @@ -483,7 +483,7 @@ export function Checkbox ({ children, label, groupClassName, hiddenLabel, extra, const StorageKeyPrefixContext = createContext() export function Form ({ - initial, schema, onSubmit, children, initialError, validateImmediately, storageKeyPrefix, validateOnChange = true, invoiceable, ...props + initial, schema, onSubmit, children, initialError, validateImmediately, storageKeyPrefix, validateOnChange = true, invoiceable, innerRef, ...props }) { const toaster = useToast() const initialErrorToasted = useRef(false) @@ -538,6 +538,7 @@ export function Form ({ toaster.danger(err.message || err.toString?.()) } }} + innerRef={innerRef} > diff --git a/components/item-full.js b/components/item-full.js index d93493a396..29902ebe0f 100644 --- a/components/item-full.js +++ b/components/item-full.js @@ -12,7 +12,7 @@ import Button from 'react-bootstrap/Button' import { TwitterTweetEmbed } from 'react-twitter-embed' import YouTube from 'react-youtube' import useDarkMode from './dark-mode' -import { useEffect, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import Poll from './poll' import { commentsViewed } from '../lib/new-comments' import Related from './related' @@ -121,10 +121,12 @@ function FwdUsers ({ forwards }) { function TopLevelItem ({ item, noReply, ...props }) { const ItemComponent = item.isJob ? ItemJob : Item + const replyRef = useRef() return ( {!noReply && <> - + {!item.position && !item.isJob && !item.parentId && !item.bounty > 0 && } {item.bounty > 0 && } } diff --git a/components/item-info.js b/components/item-info.js index b65f18f86e..37967222cb 100644 --- a/components/item-info.js +++ b/components/item-info.js @@ -2,6 +2,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' import { useEffect, useState } from 'react' import Badge from 'react-bootstrap/Badge' +import Dropdown from 'react-bootstrap/Dropdown' import Countdown from './countdown' import { abbrNum, numWithUnits } from '../lib/format' import { newComments, commentsViewedAt } from '../lib/new-comments' @@ -19,7 +20,8 @@ import ActionDropdown from './action-dropdown' export default function ItemInfo ({ item, pendingSats, full, commentsText = 'comments', - commentTextSingular = 'comment', className, embellishUser, extraInfo, onEdit, editText + commentTextSingular = 'comment', className, embellishUser, extraInfo, onEdit, editText, + onQuoteReply }) { const editThreshold = new Date(item.createdAt).getTime() + 10 * 60000 const me = useMe() @@ -140,6 +142,8 @@ export default function ItemInfo ({ !item.mine && !item.deletedAt && } {item.mine && !item.position && !item.deletedAt && } + {(item.parentId || item.text) && onQuoteReply && + quote reply} {extraInfo}
diff --git a/components/item.js b/components/item.js index 02fd0c8abc..03aa7ca642 100644 --- a/components/item.js +++ b/components/item.js @@ -24,7 +24,7 @@ export function SearchTitle ({ title }) { }) } -export default function Item ({ item, rank, belowTitle, right, full, children, siblingComments }) { +export default function Item ({ item, rank, belowTitle, right, full, children, siblingComments, replyRef }) { const titleRef = useRef() const router = useRouter() const [pendingSats, setPendingSats] = useState(0) @@ -85,6 +85,9 @@ export default function Item ({ item, rank, belowTitle, right, full, children, s
{ + replyRef?.current?.quoteReply?.() + }} embellishUser={Number(item?.user?.id) === AD_USER_ID && AD} /> {belowTitle} diff --git a/components/reply.js b/components/reply.js index 859a695eed..91c1cead23 100644 --- a/components/reply.js +++ b/components/reply.js @@ -9,6 +9,7 @@ import FeeButton from './fee-button' import { commentsViewedAfterComment } from '../lib/new-comments' import { commentSchema } from '../lib/validate' import Info from './info' +import { quote } from '../lib/md' export function ReplyOnAnotherPage ({ parentId }) { return ( @@ -33,10 +34,32 @@ function FreebieDialog () { ) } -export default function Reply ({ item, onSuccess, replyOpen, children, placeholder }) { +export default function Reply ({ item, onSuccess, replyOpen, children, placeholder, innerRef }) { const [reply, setReply] = useState(replyOpen) const me = useMe() const parentId = item.id + const replyInput = useRef(null) + const formInnerRef = useRef() + const quoteReply = useCallback(() => { + if (!reply) { + setReply(true) + } + let updatedValue + if (formInnerRef.current && formInnerRef.current.values && !formInnerRef.current.values.text) { + updatedValue = quote(item.text) + } else if (formInnerRef.current?.values?.text) { + // append quote reply text if the input already has content + updatedValue = `${replyInput.current.value}\n${quote(item.text)}` + } + if (updatedValue) { + replyInput.current.value = updatedValue + formInnerRef.current.setValues({ text: updatedValue }) + window.localStorage.setItem(`reply-${parentId}-text`, updatedValue) + } + }, [reply, item]) + if (innerRef) { + innerRef.current = { quoteReply } + } useEffect(() => { setReply(replyOpen || !!window.localStorage.getItem('reply-' + parentId + '-' + 'text')) @@ -96,7 +119,6 @@ export default function Reply ({ item, onSuccess, replyOpen, children, placehold setReply(replyOpen || false) }, [upsertComment, setReply, parentId]) - const replyInput = useRef(null) useEffect(() => { if (replyInput.current && reply && !replyOpen) replyInput.current.focus() }, [reply]) @@ -115,35 +137,35 @@ export default function Reply ({ item, onSuccess, replyOpen, children, placehold {/* HACK if we need more items, we should probably do a comment toolbar */} {children} )} - {reply && -
-
- } - innerRef={replyInput} - /> - {reply && -
- -
} - -
} +
+
+ } + innerRef={replyInput} + /> + {reply && +
+ +
} + +
) } diff --git a/lib/md.js b/lib/md.js index 1084e274e4..b15e69b588 100644 --- a/lib/md.js +++ b/lib/md.js @@ -35,3 +35,8 @@ export function extractUrls (md) { return Array.from(urls) } + +export const quote = (orig) => + orig.split('\n') + .map(line => `> ${line}`) + .join('\n') \ No newline at end of file