Skip to content

Fix Japanese IME input handling in message composer #30324

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 65 additions & 27 deletions src/components/views/rooms/BasicMessageComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@
onPaste?(event: Event | SyntheticEvent, data: DataTransfer, model: EditorModel): boolean;
}

interface IPlatformInfo {
isElectron: boolean;
isWindows: boolean;
isMac: boolean;
isSafari: boolean;
}

interface IState {
useMarkdown: boolean;
showPillAvatar: boolean;
Expand All @@ -122,7 +129,7 @@
private modifiedFlag = false;
private isIMEComposing = false;
private hasTextSelected = false;
private readonly isSafari: boolean;
private readonly platformInfo: IPlatformInfo;

private _isCaretAtEnd = false;
private lastCaret!: DocumentOffset;
Expand All @@ -143,11 +150,22 @@
showVisualBell: false,
};

const ua = navigator.userAgent.toLowerCase();
this.isSafari = ua.includes("safari/") && !ua.includes("chrome/");
this.platformInfo = this.detectPlatform();
this.configureEmoticonAutoReplace();
}

private detectPlatform(): IPlatformInfo {
const ua = navigator.userAgent.toLowerCase();
const platform = navigator.platform || "";

return {
isElectron: !!(window as any).electron || ua.includes('electron'),
isWindows: ua.includes('win') || platform.includes('Win'),
isMac: ua.includes('mac') || platform.includes('Mac'),
isSafari: ua.includes("safari/") && !ua.includes("chrome/"),
};
}

Comment on lines +157 to +168
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is mixing platform, browser etc
Why do we need to detect windows and macos?

public componentDidUpdate(prevProps: IProps): void {
// We need to re-check the placeholder when the enabled state changes because it causes the
// placeholder element to remount, which gets rid of the `::before` class. Re-evaluating the
Expand Down Expand Up @@ -275,32 +293,36 @@

private onCompositionEnd = (): void => {
this.isIMEComposing = false;
// some browsers (Chrome) don't fire an input event after ending a composition,
// so trigger a model update after the composition is done by calling the input handler.

// however, modifying the DOM (caused by the editor model update) from the compositionend handler seems
// to confuse the IME in Chrome, likely causing https://github.com/vector-im/element-web/issues/10913 ,
// so we do it async

// however, doing this async seems to break things in Safari for some reason, so browser sniff.

if (this.isSafari) {
// IME確定後の入力イベントをプラットフォームに応じて処理

Comment on lines +296 to +297
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep the previous comment. Also the comments should be in english

const triggerInput = (): void => {
this.onInput({ inputType: "insertCompositionText" });
};

if (this.platformInfo.isMac && this.platformInfo.isSafari) {
// Safariでは同期実行
triggerInput();
} else if (this.platformInfo.isElectron) {
// Electronでは確実に非同期実行
setTimeout(triggerInput, 0);
Comment on lines +305 to +307
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to do that?

} else {
Promise.resolve().then(() => {
this.onInput({ inputType: "insertCompositionText" });
});
// その他のブラウザはPromiseで非同期実行
Promise.resolve().then(triggerInput);
}
};

public isComposing(event: React.KeyboardEvent): boolean {
// checking the event.isComposing flag just in case any browser out there
// emits events related to the composition after compositionend
// has been fired

// From https://www.stum.de/2016/06/24/handling-ime-events-in-javascript/
// Safari emits an additional keyDown after compositionend
return !!(this.isIMEComposing || (event.nativeEvent && event.nativeEvent.isComposing));
// IME composition状態の統合的な判定
const nativeEvent = event.nativeEvent;
const isNativeComposing = nativeEvent && nativeEvent.isComposing;

// プラットフォーム固有の追加チェック
const isPlatformSpecificComposing = (
(this.platformInfo.isMac && this.platformInfo.isSafari && event.which === 229) ||
((this.platformInfo.isWindows || this.platformInfo.isElectron) && event.which === 229)
);

return !!(this.isIMEComposing || isNativeComposing || isPlatformSpecificComposing);
Comment on lines +315 to +325
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep the original comments and please explain why is it needed

}

private onCutCopy = (event: ClipboardEvent, type: string): void => {
Expand Down Expand Up @@ -378,10 +400,12 @@

private onInput = (event: Partial<InputEvent>): void => {
if (!this.editorRef.current) return;
// ignore any input while doing IME compositions
if (this.isIMEComposing) {

// IME composition中は確定イベント以外を無視
if (this.isIMEComposing && event.inputType !== "insertCompositionText") {
Comment on lines +404 to +405
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

return;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unneeded space change

this.modifiedFlag = true;
const sel = document.getSelection()!;
const { caret, text } = getCaretOffsetAndText(this.editorRef.current, sel);
Expand Down Expand Up @@ -479,11 +503,25 @@

private onKeyDown = (event: React.KeyboardEvent): void => {
if (!this.editorRef.current) return;
if (this.isSafari && event.which == 229) {
// Swallow the extra keyDown by Safari

// IME composition中は全てのキーイベントを無視
if (this.isComposing(event)) {
return;
}

// プラットフォーム固有のIME処理
if (this.platformInfo.isMac && this.platformInfo.isSafari && event.which === 229) {
// Safariの追加のkeyDownを無視
event.stopPropagation();
return;
}

// Windows/ElectronでのIMEキーコード229処理
if ((this.platformInfo.isWindows || this.platformInfo.isElectron) && event.which === 229) {
// Windows IMEのキーコード229を適切に処理
return;
}
Comment on lines +507 to +523
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto


const model = this.props.model;
let handled = false;

Expand Down
Loading