Make rich text fields autosave to localstorage
This commit is contained in:
		
							parent
							
								
									b81c0b5e0e
								
							
						
					
					
						commit
						881e480819
					
				|  | @ -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}> | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ export function CreatePost(props: { group?: Group }) { | |||
|   const { group } = props | ||||
| 
 | ||||
|   const { editor, upload } = useTextEditor({ | ||||
|     key: `post ${group?.id || ''}`, | ||||
|     disabled: isSubmitting, | ||||
|   }) | ||||
| 
 | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user