From f393246e4faae3508a85e7aaf25e09e8a5ddf52d Mon Sep 17 00:00:00 2001 From: Austin Chen Date: Sun, 17 Jul 2022 22:22:44 -0700 Subject: [PATCH] Let users edit descriptions and questions (#654) * Use rich text editor on the description * Write a new line to description when the question is changed * Stop showing categories * Allow anyone to edit their own question --- .../contract/contract-description.tsx | 211 ++++++++++-------- web/components/editor.tsx | 6 +- 2 files changed, 119 insertions(+), 98 deletions(-) diff --git a/web/components/contract/contract-description.tsx b/web/components/contract/contract-description.tsx index b2f839e9..d9864186 100644 --- a/web/components/contract/contract-description.tsx +++ b/web/components/contract/contract-description.tsx @@ -2,16 +2,17 @@ import clsx from 'clsx' import dayjs from 'dayjs' import { useState } from 'react' import Textarea from 'react-expanding-textarea' -import { CATEGORY_LIST } from '../../../common/categories' -import { Contract } from 'common/contract' -import { parseTags, exhibitExts } from 'common/util/parse' +import { Contract, MAX_DESCRIPTION_LENGTH } from 'common/contract' +import { exhibitExts, parseTags } from 'common/util/parse' import { useAdmin } from 'web/hooks/use-admin' import { updateContract } from 'web/lib/firebase/contracts' import { Row } from '../layout/row' -import { TagsList } from '../tags-list' import { Content } from '../editor' -import { Editor } from '@tiptap/react' +import { TextEditor, useTextEditor } from 'web/components/editor' +import { Button } from '../button' +import { Spacer } from '../layout/spacer' +import { Editor, Content as ContentType } from '@tiptap/react' export function ContractDescription(props: { contract: Contract @@ -19,20 +20,39 @@ export function ContractDescription(props: { className?: string }) { const { contract, isCreator, className } = props - const descriptionTimestamp = () => `${dayjs().format('MMM D, h:mma')}: ` const isAdmin = useAdmin() + return ( +
+ {isCreator || isAdmin ? ( + + ) : ( + + )} + {isAdmin && !isCreator && ( +
(👆 admin powers)
+ )} +
+ ) +} - const desc = contract.description ?? '' +function editTimestamp() { + return `${dayjs().format('MMM D, h:mma')}: ` +} - // Append the new description (after a newline) - async function saveDescription(newText: string) { - const editor = new Editor({ content: desc, extensions: exhibitExts }) - editor - .chain() - .focus('end') - .insertContent('

') - .insertContent(newText.trim()) - .run() +function RichEditContract(props: { contract: Contract }) { + const { contract } = props + const [editing, setEditing] = useState(false) + const [editingQ, setEditingQ] = useState(false) + const [isSubmitting, setIsSubmitting] = useState(false) + + const { editor, upload } = useTextEditor({ + max: MAX_DESCRIPTION_LENGTH, + defaultValue: contract.description, + disabled: isSubmitting, + }) + + async function saveDescription() { + if (!editor) return const tags = parseTags( `${editor.getText()} ${contract.tags.map((tag) => `#${tag}`).join(' ')}` @@ -46,76 +66,92 @@ export function ContractDescription(props: { }) } - const { tags } = contract - const categories = tags.filter((tag) => - CATEGORY_LIST.includes(tag.toLowerCase()) - ) - - return ( -
- - - {categories.length > 0 && ( -
- -
- )} - -
- - {isCreator && ( - - )} - {isAdmin && ( - updateContract(contract.id, { question })} - buttonText="ADMIN: Edit question" - /> - )} - {/* {isAdmin && ( - - updateContract(contract.id, { createdTime: Number(time) }) - } - buttonText="ADMIN: Edit createdTime" - /> - )} */} -
+ return editing ? ( + <> + + + + + + + + ) : ( + <> + + + + + + + + ) } -function EditContract(props: { - text: string - onSave: (newText: string) => void - buttonText: string +function EditQuestion(props: { + contract: Contract + editing: boolean + setEditing: (editing: boolean) => void }) { - const [text, setText] = useState(props.text) - const [editing, setEditing] = useState(false) - const onSave = (newText: string) => { + const { contract, editing, setEditing } = props + const [text, setText] = useState(contract.question) + + function questionChanged(oldQ: string, newQ: string) { + return `

${editTimestamp()}${oldQ} → ${newQ}

` + } + + function joinContent(oldContent: ContentType, newContent: string) { + const editor = new Editor({ content: oldContent, extensions: exhibitExts }) + editor.chain().focus('end').insertContent(newContent).run() + return editor.getJSON() + } + + const onSave = async (newText: string) => { setEditing(false) - setText(props.text) // Reset to original text - props.onSave(newText) + await updateContract(contract.id, { + question: newText, + description: joinContent( + contract.description, + questionChanged(contract.question, newText) + ), + }) } return editing ? (