import { useTheme } from '../../hooks/useTheme';
import { useRoomCreatorsTag } from '../../hooks/useRoomCreatorsTag';
import { usePowerLevelTags } from '../../hooks/usePowerLevelTags';
+import { useComposingCheck } from '../../hooks/useComposingCheck';
interface RoomInputProps {
editor: Editor;
const dropZoneVisible = useFileDropZone(fileDropContainerRef, handleFiles);
const [hideStickerBtn, setHideStickerBtn] = useState(document.body.clientWidth < 500);
+ const isComposing = useComposingCheck();
+
useElementSizeObserver(
useCallback(() => document.body, []),
useCallback((width) => setHideStickerBtn(width < 500), [])
(evt) => {
if (
(isKeyHotkey('mod+enter', evt) || (!enterForNewline && isKeyHotkey('enter', evt))) &&
- !evt.nativeEvent.isComposing
+ !isComposing(evt)
) {
evt.preventDefault();
submit();
setReplyDraft(undefined);
}
},
- [submit, setReplyDraft, enterForNewline, autocompleteQuery]
+ [submit, setReplyDraft, enterForNewline, autocompleteQuery, isComposing]
);
const handleKeyUp: KeyboardEventHandler = useCallback(
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { getEditedEvent, getMentionContent, trimReplyFromFormattedBody } from '../../../utils/room';
import { mobileOrTablet } from '../../../utils/user-agent';
+import { useComposingCheck } from '../../../hooks/useComposingCheck';
type MessageEditorProps = {
roomId: string;
const [globalToolbar] = useSetting(settingsAtom, 'editorToolbar');
const [isMarkdown] = useSetting(settingsAtom, 'isMarkdown');
const [toolbar, setToolbar] = useState(globalToolbar);
+ const isComposing = useComposingCheck();
const [autocompleteQuery, setAutocompleteQuery] =
useState<AutocompleteQuery<AutocompletePrefix>>();
const handleKeyDown: KeyboardEventHandler = useCallback(
(evt) => {
- if ((isKeyHotkey('mod+enter', evt) || (!enterForNewline && isKeyHotkey('enter', evt))) && !evt.nativeEvent.isComposing) {
+ if (
+ (isKeyHotkey('mod+enter', evt) || (!enterForNewline && isKeyHotkey('enter', evt))) &&
+ !isComposing(evt)
+ ) {
evt.preventDefault();
handleSave();
}
onCancel();
}
},
- [onCancel, handleSave, enterForNewline]
+ [onCancel, handleSave, enterForNewline, isComposing]
);
const handleKeyUp: KeyboardEventHandler = useCallback(
--- /dev/null
+import { useCallback, useEffect } from 'react';
+import { useAtomValue, useSetAtom } from 'jotai';
+import { lastCompositionEndAtom } from '../state/lastCompositionEnd';
+
+interface TimeStamped {
+ readonly timeStamp: number;
+}
+
+export function useCompositionEndTracking(): void {
+ const setLastCompositionEnd = useSetAtom(lastCompositionEndAtom);
+
+ const recordCompositionEnd = useCallback(
+ (evt: TimeStamped) => {
+ setLastCompositionEnd(evt.timeStamp);
+ },
+ [setLastCompositionEnd]
+ );
+
+ useEffect(() => {
+ window.addEventListener('compositionend', recordCompositionEnd, { capture: true });
+ return () => {
+ window.removeEventListener('compositionend', recordCompositionEnd, { capture: true });
+ };
+ });
+}
+
+interface IsComposingLike {
+ readonly timeStamp: number;
+ readonly keyCode: number;
+ readonly nativeEvent: {
+ readonly isComposing?: boolean;
+ };
+}
+
+export function useComposingCheck({
+ compositionEndThreshold = 500,
+}: { compositionEndThreshold?: number } = {}): (evt: IsComposingLike) => boolean {
+ const compositionEnd = useAtomValue(lastCompositionEndAtom);
+ return useCallback(
+ (evt: IsComposingLike): boolean =>
+ evt.nativeEvent.isComposing ||
+ (evt.keyCode === 229 &&
+ typeof compositionEnd !== 'undefined' &&
+ evt.timeStamp - compositionEnd < compositionEndThreshold),
+ [compositionEndThreshold, compositionEnd]
+ );
+}
import { FeatureCheck } from './FeatureCheck';
import { createRouter } from './Router';
import { ScreenSizeProvider, useScreenSize } from '../hooks/useScreenSize';
+import { useCompositionEndTracking } from '../hooks/useComposingCheck';
const queryClient = new QueryClient();
function App() {
const screenSize = useScreenSize();
+ useCompositionEndTracking();
const portalContainer = document.getElementById('portalContainer') ?? undefined;
--- /dev/null
+import { atom } from 'jotai';
+
+export const lastCompositionEndAtom = atom<number | undefined>(undefined);