Skip to content

Commit 4d9695a

Browse files
committed
chat: move cursor to end of line
- also fix possible loss of data when editing a reply to a chat and scrolling around
1 parent e793d72 commit 4d9695a

File tree

3 files changed

+73
-50
lines changed

3 files changed

+73
-50
lines changed

src/packages/frontend/chat/input.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export default function ChatInput({
6666
() => redux.getStore("account").get_account_id(),
6767
[],
6868
);
69-
69+
const controlRef = useRef<any>(null);
7070
const [input, setInput] = useState<string>("");
7171
useEffect(() => {
7272
const dbInput = syncdb
@@ -80,12 +80,20 @@ export default function ChatInput({
8080
// the db version is used when you refresh your browser while editing, or scroll up and down
8181
// thus unmounting and remounting the currently editing message (due to virtualization).
8282
// See https://github.com/sagemathinc/cocalc/issues/6415
83-
setInput(dbInput ?? propsInput);
83+
const input = dbInput ?? propsInput;
84+
setInput(input);
85+
if (input?.trim()) {
86+
// have to wait until it's all rendered -- i hate code like this...
87+
for (const n of [1, 10, 50]) {
88+
setTimeout(() => {
89+
controlRef.current?.moveCursorToEndOfLine();
90+
}, n);
91+
}
92+
}
8493
}, [date, sender_id, propsInput]);
8594

8695
const currentInputRef = useRef<string>(input);
8796
const saveOnUnmountRef = useRef<boolean>(true);
88-
8997
const isMountedRef = useIsMountedRef();
9098
const lastSavedRef = useRef<string>(input);
9199
const saveChat = useDebouncedCallback(
@@ -142,7 +150,7 @@ export default function ChatInput({
142150
// Note: it is still slightly annoying, due to loss of focus... however, data
143151
// loss is NOT ok, whereas loss of focus is.
144152
const input = currentInputRef.current;
145-
if (input == null || syncdb == null) {
153+
if (!input || syncdb == null) {
146154
return;
147155
}
148156
if (
@@ -212,6 +220,7 @@ export default function ChatInput({
212220
onBlur={onBlur}
213221
cacheId={cacheId}
214222
value={input}
223+
controlRef={controlRef}
215224
enableUpload={true}
216225
enableMentions={true}
217226
submitMentionsRef={submitMentionsRef}

src/packages/frontend/editors/markdown-input/multimode.tsx

Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -134,54 +134,56 @@ interface Props {
134134
overflowEllipsis?: boolean; // if true, show "..." button popping up all menu entries
135135

136136
dirtyRef?: MutableRefObject<boolean>; // a boolean react ref that gets set to true whenever document changes for any reason (client should explicitly set this back to false).
137+
138+
controlRef?: MutableRefObject<any>;
137139
}
138140

139-
export default function MultiMarkdownInput(props: Props) {
140-
const {
141-
autoFocus,
142-
cacheId,
143-
cmOptions,
144-
compact,
145-
cursors,
146-
defaultMode,
147-
dirtyRef,
148-
editBarStyle,
149-
editorDivRef,
150-
enableMentions,
151-
enableUpload = true,
152-
extraHelp,
153-
fixedMode,
154-
fontSize,
155-
getValueRef,
156-
height = "auto",
157-
hideHelp,
158-
isFocused,
159-
minimal,
160-
modeSwitchStyle,
161-
noVfill,
162-
onBlur,
163-
onChange,
164-
onCursorBottom,
165-
onCursors,
166-
onCursorTop,
167-
onFocus,
168-
onModeChange,
169-
onRedo,
170-
onSave,
171-
onShiftEnter,
172-
onUndo,
173-
onUploadEnd,
174-
onUploadStart,
175-
overflowEllipsis = false,
176-
placeholder,
177-
refresh,
178-
registerEditor,
179-
saveDebounceMs = SAVE_DEBOUNCE_MS,
180-
style,
181-
submitMentionsRef,
182-
unregisterEditor,
183-
value,
184-
} = props;
141+
export default function MultiMarkdownInput({
142+
autoFocus,
143+
cacheId,
144+
cmOptions,
145+
compact,
146+
cursors,
147+
defaultMode,
148+
dirtyRef,
149+
editBarStyle,
150+
editorDivRef,
151+
enableMentions,
152+
enableUpload = true,
153+
extraHelp,
154+
fixedMode,
155+
fontSize,
156+
getValueRef,
157+
height = "auto",
158+
hideHelp,
159+
isFocused,
160+
minimal,
161+
modeSwitchStyle,
162+
noVfill,
163+
onBlur,
164+
onChange,
165+
onCursorBottom,
166+
onCursors,
167+
onCursorTop,
168+
onFocus,
169+
onModeChange,
170+
onRedo,
171+
onSave,
172+
onShiftEnter,
173+
onUndo,
174+
onUploadEnd,
175+
onUploadStart,
176+
overflowEllipsis = false,
177+
placeholder,
178+
refresh,
179+
registerEditor,
180+
saveDebounceMs = SAVE_DEBOUNCE_MS,
181+
style,
182+
submitMentionsRef,
183+
unregisterEditor,
184+
value,
185+
controlRef,
186+
}: Props) {
185187
const {
186188
isFocused: isFocusedFrame,
187189
isVisible,
@@ -529,6 +531,7 @@ export default function MultiMarkdownInput(props: Props) {
529531
submitMentionsRef={submitMentionsRef}
530532
editBar2={editBar2}
531533
dirtyRef={dirtyRef}
534+
controlRef={controlRef}
532535
/>
533536
</div>
534537
) : undefined}

src/packages/frontend/editors/slate/editable-markdown.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { markdown_to_html } from "@cocalc/frontend/markdown";
3333
import Fragment, { FragmentId } from "@cocalc/frontend/misc/fragment-id";
3434
import { Descendant, Editor, Range, Transforms, createEditor } from "slate";
3535
import { resetSelection } from "./control";
36+
import * as control from "./control";
3637
import { useBroadcastCursors, useCursorDecorate } from "./cursors";
3738
import { EditBar, useLinkURL, useListProperties, useMarks } from "./edit-bar";
3839
import { Element } from "./element";
@@ -121,6 +122,9 @@ interface Props {
121122
editBar2?: MutableRefObject<JSX.Element | undefined>;
122123
dirtyRef?: MutableRefObject<boolean>;
123124
minimal?: boolean;
125+
controlRef?: MutableRefObject<{
126+
moveCursorToEndOfLine: () => void;
127+
} | null>;
124128
}
125129

126130
export const EditableMarkdown: React.FC<Props> = React.memo((props: Props) => {
@@ -159,6 +163,7 @@ export const EditableMarkdown: React.FC<Props> = React.memo((props: Props) => {
159163
submitMentionsRef,
160164
unregisterEditor,
161165
value,
166+
controlRef,
162167
} = props;
163168
const { project_id, path, desc, isVisible } = useFrameContext();
164169
const isMountedRef = useIsMountedRef();
@@ -246,6 +251,12 @@ export const EditableMarkdown: React.FC<Props> = React.memo((props: Props) => {
246251
};
247252
}
248253

254+
if (controlRef != null) {
255+
controlRef.current = {
256+
moveCursorToEndOfLine: () => control.moveCursorToEndOfLine(ed),
257+
};
258+
}
259+
249260
ed.onCursorBottom = onCursorBottom;
250261
ed.onCursorTop = onCursorTop;
251262

0 commit comments

Comments
 (0)