👁️
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

better editor integration for pronouns, save state

+38 -16
+14 -10
src/components/profile/ProfileHeader.tsx
··· 51 51 [onUpdate, editedPronouns, profile?.createdAt], 52 52 ); 53 53 54 - const { doc, onChange, isDirty } = useProseMirror({ 54 + const { doc, onChange, isDirty, trigger, save } = useProseMirror({ 55 55 initialDoc: initialPMDoc, 56 56 onSave: handleSaveBio, 57 57 saveDebounceMs: 1500, 58 58 }); 59 59 60 + const handlePronounsChange = (value: string) => { 61 + setEditedPronouns(value); 62 + trigger(); // Schedule debounced save (callback has current pronouns via closure) 63 + }; 64 + 60 65 const handleDone = () => { 61 - // Save pronouns if changed 62 - if (editedPronouns.trim() !== (profile?.pronouns ?? "")) { 63 - onUpdate({ 64 - bio: profile?.bio, 65 - pronouns: editedPronouns.trim() || undefined, 66 - createdAt: profile?.createdAt ?? new Date().toISOString(), 67 - }); 68 - } 66 + save(); // Flush any pending save 69 67 setIsEditing(false); 70 68 }; 71 69 ··· 119 117 id={pronounsId} 120 118 type="text" 121 119 value={editedPronouns} 122 - onChange={(e) => setEditedPronouns(e.target.value)} 120 + onChange={(e) => handlePronounsChange(e.target.value)} 121 + onKeyDown={(e) => { 122 + if (e.key === "Enter") { 123 + e.preventDefault(); 124 + handleDone(); 125 + } 126 + }} 123 127 placeholder="e.g. she/her, they/them" 124 128 maxLength={64} 125 129 className="px-3 py-2 w-full max-w-xs bg-gray-100 dark:bg-slate-800 border border-gray-300 dark:border-slate-700 rounded-lg text-gray-900 dark:text-white focus:outline-none focus:border-cyan-500"
+5 -5
src/lib/lexicons/types/com/deckbelcher/actor/profile.ts
··· 18 18 */ 19 19 createdAt: /*#__PURE__*/ v.datetimeString(), 20 20 /** 21 - * Free-form pronouns text. 22 - * @maxLength 200 23 - * @maxGraphemes 20 21 + * Free-form pronouns text, can include brief explanation. 22 + * @maxLength 256 23 + * @maxGraphemes 64 24 24 */ 25 25 pronouns: /*#__PURE__*/ v.optional( 26 26 /*#__PURE__*/ v.constrain(/*#__PURE__*/ v.string(), [ 27 - /*#__PURE__*/ v.stringLength(0, 200), 28 - /*#__PURE__*/ v.stringGraphemes(0, 20), 27 + /*#__PURE__*/ v.stringLength(0, 256), 28 + /*#__PURE__*/ v.stringGraphemes(0, 64), 29 29 ]), 30 30 ), 31 31 }),
+19 -1
src/lib/useProseMirror.ts
··· 35 35 docJSON: PMDocJSON; 36 36 onChange: (newDoc: ProseMirrorNode) => void; 37 37 isDirty: boolean; 38 + /** Flush debounce and save immediately */ 38 39 save: () => void; 40 + /** Schedule a debounced save (use when external deps change) */ 41 + trigger: () => void; 39 42 } 40 43 41 44 function createEmptyDoc(): ProseMirrorNode { ··· 71 74 const onSaveRef = useRef(onSave); 72 75 onSaveRef.current = onSave; 73 76 77 + // When trigger() is called, we should save even if doc hasn't changed 78 + const forceSaveRef = useRef(false); 79 + 74 80 const [doc, setDoc] = useState<ProseMirrorNode>(() => 75 81 docFromJSON(initialDoc), 76 82 ); ··· 82 88 doc, 83 89 saveDebounceMs, 84 90 (value: ProseMirrorNode) => { 85 - if (!value.eq(savedNodeRef.current) && onSaveRef.current) { 91 + const shouldSave = 92 + forceSaveRef.current || !value.eq(savedNodeRef.current); 93 + if (shouldSave && onSaveRef.current) { 86 94 onSaveRef.current(docToJSON(value)); 87 95 savedNodeRef.current = value; 88 96 } 97 + forceSaveRef.current = false; 89 98 setSaveState("saved"); 90 99 }, 91 100 ); ··· 105 114 debounce.flush(); 106 115 }, [debounce]); 107 116 117 + // Schedule a debounced save without changing the doc 118 + // Useful when external data (e.g. pronouns) changes but doc hasn't 119 + const trigger = useCallback(() => { 120 + setSaveState("dirty"); 121 + forceSaveRef.current = true; 122 + debounce.update(doc); 123 + }, [debounce, doc]); 124 + 108 125 useEffect(() => { 109 126 const newNode = docFromJSON(initialDoc); 110 127 setDoc(newNode); ··· 118 135 onChange, 119 136 isDirty: saveState === "dirty", 120 137 save, 138 + trigger, 121 139 }; 122 140 }