Skip to content

Commit 19d61b0

Browse files
committed
chat: fix a few cases where a few keystrokes of a reply could get lost
- if you quickly scrolled up and down and virtualization caused recreating the reply component you may have lost a few keystrokes - somebody else replies to the same thread you're replying to - it's still possible to have focus loss, probably, which is annoying but not as bad as data loss.
1 parent 60d6fd9 commit 19d61b0

File tree

1 file changed

+38
-2
lines changed

1 file changed

+38
-2
lines changed

src/packages/frontend/chat/input.tsx

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export default function ChatInput({
7070
() => redux.getStore("account").get_account_id(),
7171
[],
7272
);
73+
7374
const [input, setInput] = useState<string>(() => {
7475
const dbInput = syncdb
7576
?.get_one({
@@ -85,6 +86,7 @@ export default function ChatInput({
8586
const input = dbInput ?? propsInput;
8687
return input;
8788
});
89+
const currentInputRef = useRef<string>(input);
8890

8991
const isMountedRef = useIsMountedRef();
9092
const lastSavedRef = useRef<string | null>(null);
@@ -97,7 +99,6 @@ export default function ChatInput({
9799
// but definitely don't save (thus updating active) if
98100
// the input didn't really change, since we use active for
99101
// showing that a user is writing to other users.
100-
console.log("saveChat", { date });
101102
const input0 = syncdb
102103
.get_one({
103104
event: "draft",
@@ -123,9 +124,42 @@ export default function ChatInput({
123124
}
124125
},
125126
SAVE_DEBOUNCE_MS,
126-
{ leading: true },
127+
{
128+
leading: true,
129+
},
127130
);
128131

132+
useEffect(() => {
133+
return () => {
134+
// save before unmounting. This is very important since if a new reply comes in,
135+
// then the input component gets unmounted, then remounted BELOW the reply.
136+
// Note: it is still slightly annoying, due to loss of focus... however, data
137+
// loss is NOT ok, whereas loss of focus is.
138+
const input = currentInputRef.current;
139+
if (input == null || syncdb == null) {
140+
return;
141+
}
142+
if (
143+
syncdb.get_one({
144+
event: "draft",
145+
sender_id,
146+
date,
147+
}) == null
148+
) {
149+
return;
150+
}
151+
152+
syncdb.set({
153+
event: "draft",
154+
sender_id,
155+
input,
156+
date, // it's a primary key so can't use this to represent when user last edited this; use other date for editing past chats.
157+
active: Date.now(),
158+
});
159+
syncdb.commit();
160+
};
161+
}, []);
162+
129163
useEffect(() => {
130164
if (syncdb == null) return;
131165
const onSyncdbChange = () => {
@@ -138,6 +172,7 @@ export default function ChatInput({
138172
const input = x?.get("input");
139173
if (input != null && input !== lastSavedRef.current) {
140174
setInput(input);
175+
currentInputRef.current = input;
141176
lastSavedRef.current = null;
142177
}
143178
};
@@ -161,6 +196,7 @@ export default function ChatInput({
161196
enableMentions={true}
162197
submitMentionsRef={submitMentionsRef}
163198
onChange={(input) => {
199+
currentInputRef.current = input;
164200
/* BUG: in Markdown mode this stops getting
165201
called after you paste in an image. It works
166202
fine in Slate/Text mode. See

0 commit comments

Comments
 (0)