a tool for shared writing and social publishing

keep focus properly for ios on block type change

+34 -24
+34 -24
components/Toolbar/TextBlockTypeToolbar.tsx
··· 26 26 let { rep } = useReplicache(); 27 27 28 28 let setLevel = useCallback( 29 - (level: number) => { 29 + async (level: number) => { 30 30 if (!focusedBlock) return; 31 31 if ( 32 32 blockType?.data.value !== "text" && ··· 35 35 return; 36 36 } 37 37 if (blockType.data.value === "text") { 38 - keepFocus(focusedBlock.entityID); 39 - rep?.mutate.assertFact({ 38 + let existingEditor = 39 + useEditorStates.getState().editorStates[focusedBlock.entityID]; 40 + let selection = existingEditor?.editor.selection; 41 + await rep?.mutate.assertFact({ 40 42 entity: focusedBlock.entityID, 41 43 attribute: "block/type", 42 44 data: { type: "block-type-union", value: "heading" }, 43 45 }); 46 + 47 + let newEditor = 48 + useEditorStates.getState().editorStates[focusedBlock.entityID]; 49 + if (!newEditor || !selection) return; 50 + newEditor.view?.dispatch( 51 + newEditor.editor.tr.setSelection( 52 + TextSelection.create(newEditor.editor.doc, selection.anchor), 53 + ), 54 + ); 55 + 56 + newEditor.view?.focus(); 44 57 } 45 58 rep?.mutate.assertFact({ 46 59 entity: focusedBlock.entityID, ··· 58 71 className={props.className} 59 72 onClick={() => { 60 73 setLevel(1); 61 - focusedBlock && keepFocus(focusedBlock.entityID); 62 74 }} 63 75 active={ 64 76 blockType?.data.value === "heading" && ··· 80 92 className={props.className} 81 93 onClick={() => { 82 94 setLevel(2); 83 - focusedBlock && keepFocus(focusedBlock.entityID); 84 95 }} 85 96 active={ 86 97 blockType?.data.value === "heading" && ··· 102 113 className={props.className} 103 114 onClick={() => { 104 115 setLevel(3); 105 - focusedBlock && keepFocus(focusedBlock.entityID); 106 116 }} 107 117 active={ 108 118 blockType?.data.value === "heading" && ··· 122 132 </ToolbarButton> 123 133 <ToolbarButton 124 134 className={`px-[6px] ${props.className}`} 125 - onClick={() => { 135 + onClick={async () => { 126 136 if (headingLevel) 127 - rep?.mutate.retractFact({ factID: headingLevel.id }); 137 + await rep?.mutate.retractFact({ factID: headingLevel.id }); 128 138 if (!focusedBlock || !blockType) return; 129 139 if (blockType.data.value !== "text") { 130 - keepFocus(focusedBlock.entityID); 131 - rep?.mutate.assertFact({ 140 + let existingEditor = 141 + useEditorStates.getState().editorStates[focusedBlock.entityID]; 142 + let selection = existingEditor?.editor.selection; 143 + await rep?.mutate.assertFact({ 132 144 entity: focusedBlock?.entityID, 133 145 attribute: "block/type", 134 146 data: { type: "block-type-union", value: "text" }, 135 147 }); 148 + 149 + let newEditor = 150 + useEditorStates.getState().editorStates[focusedBlock.entityID]; 151 + if (!newEditor || !selection) return; 152 + newEditor.view?.dispatch( 153 + newEditor.editor.tr.setSelection( 154 + TextSelection.create(newEditor.editor.doc, selection.anchor), 155 + ), 156 + ); 157 + 158 + newEditor.view?.focus(); 136 159 } 137 160 }} 138 161 active={blockType?.data.value === "text"} ··· 146 169 }; 147 170 148 171 export function keepFocus(entityID: string) { 149 - let existingEditor = useEditorStates.getState().editorStates[entityID]; 150 - let selection = existingEditor?.editor.selection; 151 - setTimeout(() => { 152 - let existingEditor = useEditorStates.getState().editorStates[entityID]; 153 - if (!selection || !existingEditor) return; 154 - existingEditor.view?.focus(); 155 - setEditorState(entityID, { 156 - editor: existingEditor.editor.apply( 157 - existingEditor.editor.tr.setSelection( 158 - TextSelection.create(existingEditor.editor.doc, selection.anchor), 159 - ), 160 - ), 161 - }); 162 - }, 20); 172 + setTimeout(() => {}, 1000); 163 173 } 164 174 165 175 export function TextBlockTypeButton(props: {