refactor replies and full items

This commit is contained in:
keyan 2021-09-23 15:09:07 -05:00
parent e7787e3e67
commit 02c44dca63
9 changed files with 117 additions and 128 deletions

View File

@ -3,7 +3,6 @@ import * as Yup from 'yup'
import { gql, useMutation } from '@apollo/client' import { gql, useMutation } from '@apollo/client'
import styles from './reply.module.css' import styles from './reply.module.css'
import TextareaAutosize from 'react-textarea-autosize' import TextareaAutosize from 'react-textarea-autosize'
import Countdown from '../components/countdown'
export const CommentSchema = Yup.object({ export const CommentSchema = Yup.object({
text: Yup.string().required('required').trim() text: Yup.string().required('required').trim()
@ -53,19 +52,8 @@ export default function CommentEdit ({ comment, editThreshold, onSuccess, onCanc
minRows={4} minRows={4}
autoFocus autoFocus
required required
groupClassName='mb-0'
hint={<Countdown date={editThreshold} />}
/> />
<div className='d-flex align-items-center justify-content-between'> <SubmitButton variant='secondary' className='mt-1'>save</SubmitButton>
<SubmitButton variant='secondary' className='mt-1'>save</SubmitButton>
<div
className='font-weight-bold text-muted mr-3'
style={{ fontSize: '80%', cursor: 'pointer' }}
onClick={onCancel}
>
cancel
</div>
</div>
</Form> </Form>
</div> </div>
) )

View File

@ -41,9 +41,8 @@ function Parent ({ item, rootText }) {
export default function Comment ({ export default function Comment ({
item, children, replyOpen, includeParent, item, children, replyOpen, includeParent,
rootText, noComments, noReply rootText, noComments
}) { }) {
const [reply, setReply] = useState(replyOpen)
const [edit, setEdit] = useState() const [edit, setEdit] = useState()
const [collapse, setCollapse] = useState(false) const [collapse, setCollapse] = useState(false)
const ref = useRef(null) const ref = useRef(null)
@ -92,28 +91,36 @@ export default function Comment ({
<span> </span> <span> </span>
<span>{timeSince(new Date(item.createdAt))}</span> <span>{timeSince(new Date(item.createdAt))}</span>
{includeParent && <Parent item={item} rootText={rootText} />} {includeParent && <Parent item={item} rootText={rootText} />}
{canEdit &&
<>
<span> \ </span>
<div
className={styles.edit}
onClick={() => setEdit(!edit)}
>
{edit ? 'cancel' : 'edit'}
<Countdown
date={editThreshold}
onComplete={() => {
setCanEdit(false)
}}
/>
</div>
</>}
</div> </div>
{!includeParent && (collapse {!includeParent && (collapse
? <Eye className={styles.collapser} height={10} width={10} onClick={() => setCollapse(false)} /> ? <Eye className={styles.collapser} height={10} width={10} onClick={() => setCollapse(false)} />
: <EyeClose className={styles.collapser} height={10} width={10} onClick={() => setCollapse(true)} />)} : <EyeClose className={styles.collapser} height={10} width={10} onClick={() => setCollapse(true)} />)}
</div> </div>
{edit {edit
? ( ? (
<div className={styles.replyWrapper}> <CommentEdit
<CommentEdit comment={item}
comment={item} onSuccess={() => {
onSuccess={() => { setEdit(!edit)
setEdit(!edit) setCanEdit(mine && (Date.now() < editThreshold))
setCanEdit(mine && (Date.now() < editThreshold)) }}
}} />
onCancel={() => {
setEdit(!edit)
setCanEdit(mine && (Date.now() < editThreshold))
}}
editThreshold={editThreshold}
/>
</div>
) )
: ( : (
<div className={styles.text}> <div className={styles.text}>
@ -123,40 +130,9 @@ export default function Comment ({
</div> </div>
</div> </div>
<div className={`${styles.children}`}> <div className={`${styles.children}`}>
{!noReply && !edit && ( <Reply
<div className={`${itemStyles.other} ${styles.reply}`}> parentId={item.id} replyOpen={replyOpen}
<div />
className='d-inline-block'
onClick={() => setReply(!reply)}
>
{reply ? 'cancel' : 'reply'}
</div>
{canEdit && !reply && !edit &&
<>
<span> \ </span>
<div
className='d-inline-block'
onClick={() => setEdit(!edit)}
>
edit
<Countdown
date={editThreshold}
className=' '
onComplete={() => {
setCanEdit(false)
}}
/>
</div>
</>}
</div>
)}
<div className={reply ? styles.replyWrapper : 'd-none'}>
<Reply
parentId={item.id} autoFocus={!replyOpen}
onSuccess={() => setReply(replyOpen || false)}
/>
</div>
{children} {children}
<div className={`${styles.comments} ml-sm-1 ml-md-3`}> <div className={`${styles.comments} ml-sm-1 ml-md-3`}>
{item.comments && !noComments {item.comments && !noComments

View File

@ -11,6 +11,11 @@
padding-right: 15px; padding-right: 15px;
} }
.edit {
display: inline-block;
cursor: pointer;
}
.collapsed .hunk { .collapsed .hunk {
margin-bottom: .5rem; margin-bottom: .5rem;
} }
@ -33,20 +38,8 @@
user-select: none; user-select: none;
} }
.reply {
font-weight: bold;
cursor: pointer;
padding-bottom: .5rem;
}
.replyWrapper {
padding-right: 15px;
padding-bottom: .5rem;
}
.children { .children {
margin-top: 0; margin-top: 0;
padding-top: .25rem;
margin-left: 24px; margin-left: 24px;
} }

View File

@ -2,7 +2,7 @@ import Countdown from 'react-countdown'
export default function SimpleCountdown ({ className, onComplete, date }) { export default function SimpleCountdown ({ className, onComplete, date }) {
return ( return (
<span className={className || 'text-muted font-weight-bold'}> <span className={className}>
<Countdown <Countdown
date={date} date={date}
renderer={props => <span> {props.formatted.minutes}:{props.formatted.seconds}</span>} renderer={props => <span> {props.formatted.minutes}:{props.formatted.seconds}</span>}

View File

@ -10,7 +10,33 @@ import styles from '../styles/item.module.css'
import { NOFOLLOW_LIMIT } from '../lib/constants' import { NOFOLLOW_LIMIT } from '../lib/constants'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
export default function ItemFull ({ item: qItem, minimal }) { function BioItem ({ item }) {
if (!item.text) {
return null
}
return (
<>
<ItemText item={item} />
<Reply parentId={item.id} />
</>
)
}
function TopLevelItem ({ item }) {
return (
<Item item={item}>
{item.text && <ItemText item={item} />}
<Reply parentId={item.id} replyOpen />
</Item>
)
}
function ItemText ({ item }) {
return <Text nofollow={item.sats + item.boost < NOFOLLOW_LIMIT}>{item.text}</Text>
}
export default function ItemFull ({ item: qItem, bio }) {
const query = gql` const query = gql`
${ITEM_FIELDS} ${ITEM_FIELDS}
${COMMENTS} ${COMMENTS}
@ -49,24 +75,9 @@ export default function ItemFull ({ item: qItem, minimal }) {
<> <>
{item.parentId {item.parentId
? <Comment item={item} replyOpen includeParent noComments /> ? <Comment item={item} replyOpen includeParent noComments />
: (minimal : (bio
? ( ? <BioItem item={item} />
<> : <TopLevelItem item={item} />
{item.text &&
<div className='mb-3'>
<Text nofollow={item.sats + item.boost < NOFOLLOW_LIMIT}>{item.text}</Text>
</div>}
</>)
: (
<>
<Item item={item}>
{item.text &&
<div className='mb-3'>
<Text nofollow={item.sats + item.boost < NOFOLLOW_LIMIT}>{item.text}</Text>
</div>}
<Reply parentId={item.id} />
</Item>
</>)
)} )}
<div className={styles.comments}> <div className={styles.comments}>
<Comments comments={item.comments} /> <Comments comments={item.comments} />

View File

@ -6,12 +6,14 @@ import { COMMENTS } from '../fragments/comments'
import { useMe } from './me' import { useMe } from './me'
import ActionTooltip from './action-tooltip' import ActionTooltip from './action-tooltip'
import TextareaAutosize from 'react-textarea-autosize' import TextareaAutosize from 'react-textarea-autosize'
import { useState } from 'react'
export const CommentSchema = Yup.object({ export const CommentSchema = Yup.object({
text: Yup.string().required('required').trim() text: Yup.string().required('required').trim()
}) })
export default function Reply ({ parentId, onSuccess, autoFocus }) { export default function Reply ({ parentId, onSuccess, replyOpen }) {
const [reply, setReply] = useState(replyOpen)
const me = useMe() const me = useMe()
const [createComment] = useMutation( const [createComment] = useMutation(
@ -47,35 +49,41 @@ export default function Reply ({ parentId, onSuccess, autoFocus }) {
) )
return ( return (
<div className={`${styles.reply} mb-1`}> <div>
<Form <div
initial={{ className={styles.replyButtons}
text: '' onClick={() => setReply(!reply)}
}}
schema={CommentSchema}
onSubmit={async (values, { resetForm }) => {
const { error } = await createComment({ variables: { ...values, parentId } })
if (error) {
throw new Error({ message: error.toString() })
}
resetForm({ text: '' })
if (onSuccess) {
onSuccess()
}
}}
> >
<MarkdownInput {reply ? 'cancel' : 'reply'}
name='text' </div>
as={TextareaAutosize} <div className={reply ? `${styles.reply}` : 'd-none'}>
minRows={4} <Form
autoFocus={autoFocus} initial={{
required text: ''
hint={me?.freeComments ? <span className='text-success'>{me.freeComments} free comments left</span> : null} }}
/> schema={CommentSchema}
<ActionTooltip> onSubmit={async (values, { resetForm }) => {
<SubmitButton variant='secondary' className='mt-1'>reply</SubmitButton> const { error } = await createComment({ variables: { ...values, parentId } })
</ActionTooltip> if (error) {
</Form> throw new Error({ message: error.toString() })
}
resetForm({ text: '' })
setReply(replyOpen || false)
}}
>
<MarkdownInput
name='text'
as={TextareaAutosize}
minRows={4}
autoFocus={!replyOpen}
required
hint={me?.freeComments ? <span className='text-success'>{me.freeComments} free comments left</span> : null}
/>
<ActionTooltip>
<SubmitButton variant='secondary' className='mt-1'>reply</SubmitButton>
</ActionTooltip>
</Form>
</div>
</div> </div>
) )
} }

View File

@ -1,5 +1,16 @@
.reply { .reply {
max-width: 600px; max-width: 600px;
padding-right: 15px;
padding-bottom: .5rem;
}
.replyButtons {
font-size: 70%;
color: grey;
font-weight: bold;
display: inline-block;
cursor: pointer;
padding-bottom: .5rem;
} }
.skeleton .input { .skeleton .input {

View File

@ -85,12 +85,14 @@ export default function User ({ user }) {
const [create, setCreate] = useState(false) const [create, setCreate] = useState(false)
const [session] = useSession() const [session] = useSession()
// need to check if this is the user's page
return ( return (
<Layout noSeo containClassName={styles.contain}> <Layout noSeo containClassName={styles.contain}>
<Seo user={user} /> <Seo user={user} />
<UserHeader user={user} /> <UserHeader user={user} />
{user.bio {user.bio
? <ItemFull item={user.bio} minimal /> ? <ItemFull item={user.bio} bio />
: ( : (
<div className={styles.create}> <div className={styles.create}>
{create {create

View File

@ -1,10 +1,10 @@
.comments { .comments {
margin-top: 3rem; margin-top: 1rem;
} }
@media only screen and (max-width: 600px) { @media only screen and (max-width: 600px) {
.comments { .comments {
margin-top: 3rem; margin-top: 1rem;
margin-left: -15px; margin-left: -15px;
margin-right: -15px; margin-right: -15px;
} }