mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
fix(desktop): show the text context menu on password fields (#3285)
This commit is contained in:
1 parent
e7859fefb1
commit
510cc1b916
4 files changed
+4
-58
No files matched your search
@@ -217,6 +217,7 @@ export const TextareaContextMenu = observer(
|
|||||||
);
|
);
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
const isPasswordTarget = targetElement instanceof HTMLInputElement && targetElement.type === 'password';
|
||||||
const spellcheckEnabled = Spellcheck.enabled;
|
const spellcheckEnabled = Spellcheck.enabled;
|
||||||
const hasMisspelling = spellcheckEnabled && misspelledWord && suggestions.length > 0;
|
const hasMisspelling = spellcheckEnabled && misspelledWord && suggestions.length > 0;
|
||||||
return (
|
return (
|
||||||
@@ -307,6 +308,7 @@ export const TextareaContextMenu = observer(
|
|||||||
{i18n._(SELECT_ALL_DESCRIPTOR)}
|
{i18n._(SELECT_ALL_DESCRIPTOR)}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuGroup>
|
</MenuGroup>
|
||||||
|
{!isPasswordTarget && (
|
||||||
<MenuGroup data-flx="channel.textarea.textarea-context-menu.menu-group--5">
|
<MenuGroup data-flx="channel.textarea.textarea-context-menu.menu-group--5">
|
||||||
<MenuItemSubmenu
|
<MenuItemSubmenu
|
||||||
label={i18n._(SPELLCHECK_DESCRIPTOR)}
|
label={i18n._(SPELLCHECK_DESCRIPTOR)}
|
||||||
@@ -320,6 +322,7 @@ export const TextareaContextMenu = observer(
|
|||||||
data-flx="channel.textarea.textarea-context-menu.menu-item-submenu"
|
data-flx="channel.textarea.textarea-context-menu.menu-item-submenu"
|
||||||
/>
|
/>
|
||||||
</MenuGroup>
|
</MenuGroup>
|
||||||
|
)}
|
||||||
{showSendButtonToggle && (
|
{showSendButtonToggle && (
|
||||||
<MenuGroup data-flx="channel.textarea.textarea-context-menu.menu-group--6">
|
<MenuGroup data-flx="channel.textarea.textarea-context-menu.menu-group--6">
|
||||||
<CheckboxItem
|
<CheckboxItem
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ const DISALLOWED_INPUT_TYPES = new Set([
|
|||||||
'submit',
|
'submit',
|
||||||
'time',
|
'time',
|
||||||
'week',
|
'week',
|
||||||
'password',
|
|
||||||
]);
|
]);
|
||||||
const CONTEXT_TARGET_MAX_AGE_MS = 5000;
|
const CONTEXT_TARGET_MAX_AGE_MS = 5000;
|
||||||
const createSyntheticEvent = (
|
const createSyntheticEvent = (
|
||||||
|
|||||||
@@ -140,13 +140,6 @@ let factoryPromise: Promise<HunspellFactory> | null = null;
|
|||||||
let ipcRegistered = false;
|
let ipcRegistered = false;
|
||||||
let launchModeLogged = false;
|
let launchModeLogged = false;
|
||||||
|
|
||||||
const contextSourceByWebContents = new WeakMap<
|
|
||||||
WebContents,
|
|
||||||
{
|
|
||||||
isTextarea: boolean;
|
|
||||||
ts: number;
|
|
||||||
}
|
|
||||||
>();
|
|
||||||
const norm = (code: string): string => code.toLowerCase();
|
const norm = (code: string): string => code.toLowerCase();
|
||||||
const beginSessionApply = (session: Session): number => {
|
const beginSessionApply = (session: Session): number => {
|
||||||
const generation = (sessionApplyGeneration.get(session) ?? 0) + 1;
|
const generation = (sessionApplyGeneration.get(session) ?? 0) + 1;
|
||||||
@@ -971,20 +964,6 @@ const suggestWord = (session: Session, word: string): Array<string> => {
|
|||||||
const ensureSharedIpc = () => {
|
const ensureSharedIpc = () => {
|
||||||
if (ipcRegistered) return;
|
if (ipcRegistered) return;
|
||||||
ipcRegistered = true;
|
ipcRegistered = true;
|
||||||
ipcMain.on(
|
|
||||||
'spellcheck-context-target',
|
|
||||||
(
|
|
||||||
event,
|
|
||||||
payload: {
|
|
||||||
isTextarea?: boolean;
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
contextSourceByWebContents.set(event.sender, {
|
|
||||||
isTextarea: Boolean(payload?.isTextarea),
|
|
||||||
ts: Date.now(),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
ipcMain.on(
|
ipcMain.on(
|
||||||
'spellcheck:update-autodetect-text',
|
'spellcheck:update-autodetect-text',
|
||||||
(
|
(
|
||||||
@@ -1016,31 +995,6 @@ const ensureSharedIpc = () => {
|
|||||||
return suggestWord(event.sender.session, word);
|
return suggestWord(event.sender.session, word);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const shouldHandleContextMenu = (webContents: WebContents, params: Electron.ContextMenuParams): boolean => {
|
|
||||||
if (!params['isEditable']) return false;
|
|
||||||
const inputFieldType = (
|
|
||||||
params as {
|
|
||||||
inputFieldType?: string;
|
|
||||||
}
|
|
||||||
).inputFieldType;
|
|
||||||
const isPassword =
|
|
||||||
(
|
|
||||||
params as {
|
|
||||||
isPassword?: boolean;
|
|
||||||
}
|
|
||||||
).isPassword === true ||
|
|
||||||
inputFieldType === 'password' ||
|
|
||||||
(
|
|
||||||
params as {
|
|
||||||
formControlType?: string;
|
|
||||||
}
|
|
||||||
).formControlType === 'password';
|
|
||||||
if (isPassword) return false;
|
|
||||||
const target = contextSourceByWebContents.get(webContents);
|
|
||||||
const targetRecent = target && Date.now() - target.ts < 5000;
|
|
||||||
const isTextLike = inputFieldType === 'plainText' || inputFieldType === 'textarea' || inputFieldType === undefined;
|
|
||||||
return Boolean((targetRecent && target.isTextarea) || isTextLike);
|
|
||||||
};
|
|
||||||
let rendererSpellcheckHandlersRegistered = false;
|
let rendererSpellcheckHandlersRegistered = false;
|
||||||
|
|
||||||
export const registerSpellcheck = (webContents: WebContents): void => {
|
export const registerSpellcheck = (webContents: WebContents): void => {
|
||||||
@@ -1117,7 +1071,7 @@ export const registerSpellcheck = (webContents: WebContents): void => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
webContents.on('context-menu', (event, params) => {
|
webContents.on('context-menu', (event, params) => {
|
||||||
if (!shouldHandleContextMenu(webContents, params)) return;
|
if (!params.isEditable) return;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const cur = sessionState.get(session) ?? {...defaultState};
|
const cur = sessionState.get(session) ?? {...defaultState};
|
||||||
const resolved = sessionResolvedEngine.get(session) ?? resolveEngine(cur, session);
|
const resolved = sessionResolvedEngine.get(session) ?? resolveEngine(cur, session);
|
||||||
|
|||||||
@@ -870,16 +870,6 @@ const api: ElectronAPI = {
|
|||||||
} satisfies VoiceEngineV2BridgeHardwareEncoderApi,
|
} satisfies VoiceEngineV2BridgeHardwareEncoderApi,
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener(
|
|
||||||
'contextmenu',
|
|
||||||
(event) => {
|
|
||||||
const target = event.target as HTMLElement | null;
|
|
||||||
const isTextarea = Boolean(target?.closest?.('textarea'));
|
|
||||||
ipcRenderer.send('spellcheck-context-target', {isTextarea});
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
let spellcheckAutodetectTimer: NodeJS.Timeout | null = null;
|
let spellcheckAutodetectTimer: NodeJS.Timeout | null = null;
|
||||||
let spellcheckAutodetectContextSequence = 0;
|
let spellcheckAutodetectContextSequence = 0;
|
||||||
const SPELLCHECK_AUTODETECT_DEBOUNCE_MS = 750;
|
const SPELLCHECK_AUTODETECT_DEBOUNCE_MS = 750;
|
||||||
|
|||||||
Reference in new issue
Block a user