Make rich text fields autosave to localstorage

This commit is contained in:
Sinclair Chen 2022-10-07 18:14:19 -07:00
parent b81c0b5e0e
commit 881e480819
5 changed files with 26 additions and 8 deletions

View File

@ -43,7 +43,7 @@ export function CreateChallengeModal(props: {
const { user, contract, isOpen, setOpen } = props
const [challengeSlug, setChallengeSlug] = useState('')
const [loading, setLoading] = useState(false)
const { editor } = useTextEditor({ placeholder: '' })
const { editor } = useTextEditor({ key: 'challenge'})
return (
<Modal open={isOpen} setOpen={setOpen}>

View File

@ -46,6 +46,7 @@ function RichEditContract(props: { contract: Contract; isAdmin?: boolean }) {
const [isSubmitting, setIsSubmitting] = useState(false)
const { editor, upload } = useTextEditor({
// key: `description ${contract.id}`,
max: MAX_DESCRIPTION_LENGTH,
defaultValue: contract.description,
disabled: isSubmitting,

View File

@ -21,6 +21,7 @@ export function CreatePost(props: { group?: Group }) {
const { group } = props
const { editor, upload } = useTextEditor({
key: `post ${group?.id || ''}`,
disabled: isSubmitting,
})

View File

@ -13,7 +13,7 @@ import StarterKit from '@tiptap/starter-kit'
import { Image } from '@tiptap/extension-image'
import { Link } from '@tiptap/extension-link'
import clsx from 'clsx'
import { useEffect, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { Linkify } from './linkify'
import { uploadImage } from 'web/lib/firebase/storage'
import { useMutation } from 'react-query'
@ -42,6 +42,12 @@ import ItalicIcon from 'web/lib/icons/italic-icon'
import LinkIcon from 'web/lib/icons/link-icon'
import { getUrl } from 'common/util/parse'
import { TiptapSpoiler } from 'common/util/tiptap-spoiler'
import {
storageStore,
usePersistentState,
} from 'web/hooks/use-persistent-state'
import { safeLocalStorage } from 'web/lib/util/local'
import { debounce } from 'lodash'
const DisplayImage = Image.configure({
HTMLAttributes: {
@ -75,8 +81,20 @@ export function useTextEditor(props: {
defaultValue?: Content
disabled?: boolean
simple?: boolean
key?: string // unique key for this text field for autosave
}) {
const { placeholder, max, defaultValue = '', disabled, simple } = props
const { placeholder, max, defaultValue, disabled, simple, key } = props
const [content, saveContent] = usePersistentState<JSONContent | undefined>(
undefined,
{
key: `text ${key}`,
store: storageStore(safeLocalStorage()),
}
)
// eslint-disable-next-line react-hooks/exhaustive-deps
const save = useCallback(debounce(saveContent, 500), [])
const editorClass = clsx(
proseClass,
@ -88,6 +106,7 @@ export function useTextEditor(props: {
const editor = useEditor({
editorProps: { attributes: { class: editorClass, spellcheck: 'false' } },
onUpdate: key ? ({ editor }) => save(editor.getJSON()) : undefined,
extensions: [
StarterKit.configure({
heading: simple ? false : { levels: [1, 2, 3] },
@ -113,7 +132,7 @@ export function useTextEditor(props: {
spoilerOpenClass: 'rounded-sm bg-greyscale-2',
}),
],
content: defaultValue,
content: defaultValue ?? (key && content ? content : ''),
})
const upload = useUploadMutation(editor)

View File

@ -23,7 +23,6 @@ import { ChoicesToggleGroup } from 'web/components/choices-toggle-group'
import { getGroup, groupPath } from 'web/lib/firebase/groups'
import { Group } from 'common/group'
import { useTracking } from 'web/hooks/use-tracking'
import { useWarnUnsavedChanges } from 'web/hooks/use-warn-unsaved-changes'
import { track } from 'web/lib/service/analytics'
import { GroupSelector } from 'web/components/groups/group-selector'
import { User } from 'common/user'
@ -228,6 +227,7 @@ export function NewContract(props: {
: `e.g. I will choose the answer according to...`
const { editor, upload } = useTextEditor({
key: 'create market',
max: MAX_DESCRIPTION_LENGTH,
placeholder: descriptionPlaceholder,
disabled: isSubmitting,
@ -236,9 +236,6 @@ export function NewContract(props: {
: undefined,
})
const isEditorFilled = editor != null && !editor.isEmpty
useWarnUnsavedChanges(!isSubmitting && (Boolean(question) || isEditorFilled))
function setCloseDateInDays(days: number) {
const newCloseDate = dayjs().add(days, 'day').format('YYYY-MM-DD')
setCloseDate(newCloseDate)