From e1e2ba8888ea290a76cdb573f50b9f7a0f25da58 Mon Sep 17 00:00:00 2001 From: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com> Date: Wed, 8 Jan 2025 04:30:16 -0800 Subject: [PATCH] Desktop: Fix keyboard can't add text after certain error/info dialogs are shown (#11603) --- packages/app-desktop/bridge.ts | 9 +++++++-- packages/app-desktop/commands/copyDevCommand.ts | 3 ++- packages/app-desktop/commands/restoreNoteRevision.ts | 5 +++-- packages/app-desktop/gui/ClipperConfigScreen.tsx | 3 ++- .../app-desktop/gui/ConfigScreen/ConfigScreen.tsx | 3 ++- .../EncryptionConfigScreen/EncryptionConfigScreen.tsx | 4 ++-- .../app-desktop/gui/MasterPasswordDialog/Dialog.tsx | 3 ++- packages/app-desktop/gui/NoteRevisionViewer.tsx | 3 ++- .../gui/ShareFolderDialog/ShareFolderDialog.tsx | 11 +++++++---- packages/app-desktop/gui/ShareNoteDialog.tsx | 3 ++- .../app-desktop/gui/Sidebar/hooks/useOnRenderItem.tsx | 3 ++- .../commands/leaveSharedFolder.ts | 5 +++-- 12 files changed, 36 insertions(+), 19 deletions(-) diff --git a/packages/app-desktop/bridge.ts b/packages/app-desktop/bridge.ts index f6f31e0361a..12941a4071b 100644 --- a/packages/app-desktop/bridge.ts +++ b/packages/app-desktop/bridge.ts @@ -1,5 +1,5 @@ import ElectronAppWrapper from './ElectronAppWrapper'; -import shim from '@joplin/lib/shim'; +import shim, { MessageBoxType } from '@joplin/lib/shim'; import { _, setLocale } from '@joplin/lib/locale'; import { BrowserWindow, nativeTheme, nativeImage, shell, dialog, MessageBoxSyncOptions, safeStorage } from 'electron'; import { dirname, toSystemSlashes } from '@joplin/lib/path-utils'; @@ -384,9 +384,14 @@ export class Bridge { /* returns the index of the clicked button */ public showMessageBox(message: string, options: MessageDialogOptions = {}) { + const defaultButtons = [_('OK')]; + if (options.type !== MessageBoxType.Error && options.type !== MessageBoxType.Info) { + defaultButtons.push(_('Cancel')); + } + const result = this.showMessageBox_(this.activeWindow(), { type: 'question', message: message, - buttons: [_('OK'), _('Cancel')], ...options }); + buttons: defaultButtons, ...options }); return result; } diff --git a/packages/app-desktop/commands/copyDevCommand.ts b/packages/app-desktop/commands/copyDevCommand.ts index e103ed61f26..d7bcb998b8f 100644 --- a/packages/app-desktop/commands/copyDevCommand.ts +++ b/packages/app-desktop/commands/copyDevCommand.ts @@ -1,5 +1,6 @@ import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService'; import { _ } from '@joplin/lib/locale'; +import shim, { MessageBoxType } from '@joplin/lib/shim'; const app = require('@electron/remote').app; const { clipboard } = require('electron'); @@ -14,7 +15,7 @@ export const runtime = (): CommandRuntime => { const appPath = app.getPath('exe'); const cmd = `${appPath} --env dev`; clipboard.writeText(cmd); - alert(`The dev mode command has been copied to clipboard:\n\n${cmd}`); + await shim.showMessageBox(`The dev mode command has been copied to clipboard:\n\n${cmd}`, { type: MessageBoxType.Info }); }, }; }; diff --git a/packages/app-desktop/commands/restoreNoteRevision.ts b/packages/app-desktop/commands/restoreNoteRevision.ts index ea7cc86d7c0..c80870643a1 100644 --- a/packages/app-desktop/commands/restoreNoteRevision.ts +++ b/packages/app-desktop/commands/restoreNoteRevision.ts @@ -1,5 +1,6 @@ import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService'; import RevisionService from '@joplin/lib/services/RevisionService'; +import shim, { MessageBoxType } from '@joplin/lib/shim'; export const declaration: CommandDeclaration = { name: 'restoreNoteRevision', @@ -11,9 +12,9 @@ export const runtime = (): CommandRuntime => { execute: async (_context: CommandContext, noteId: string, reverseRevIndex = 0) => { try { const note = await RevisionService.instance().restoreNoteById(noteId, reverseRevIndex); - alert(RevisionService.instance().restoreSuccessMessage(note)); + await shim.showMessageBox(RevisionService.instance().restoreSuccessMessage(note), { type: MessageBoxType.Info }); } catch (error) { - alert(error.message); + await shim.showErrorDialog(error.message); } }, }; diff --git a/packages/app-desktop/gui/ClipperConfigScreen.tsx b/packages/app-desktop/gui/ClipperConfigScreen.tsx index 6fd1dfa36dd..a6d0c6c4e5b 100644 --- a/packages/app-desktop/gui/ClipperConfigScreen.tsx +++ b/packages/app-desktop/gui/ClipperConfigScreen.tsx @@ -9,6 +9,7 @@ import ClipperServer from '@joplin/lib/ClipperServer'; import Setting from '@joplin/lib/models/Setting'; import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService'; import { AppState } from '../app.reducer'; +import shim, { MessageBoxType } from '@joplin/lib/shim'; class ClipperConfigScreenComponent extends React.Component { public constructor() { @@ -30,7 +31,7 @@ class ClipperConfigScreenComponent extends React.Component { private copyToken_click() { clipboard.writeText(this.props.apiToken); - alert(_('Token has been copied to the clipboard!')); + void shim.showMessageBox(_('Token has been copied to the clipboard!'), { type: MessageBoxType.Info }); } private renewToken_click() { diff --git a/packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx b/packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx index 25b00bc4b80..3a5f9783b4d 100644 --- a/packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx +++ b/packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx @@ -19,6 +19,7 @@ import shouldShowMissingPasswordWarning from '@joplin/lib/components/shared/conf import MacOSMissingPasswordHelpLink from './controls/MissingPasswordHelpLink'; const { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen'); import SettingComponent, { UpdateSettingValueEvent } from './controls/SettingComponent'; +import shim from '@joplin/lib/shim'; interface Font { @@ -144,7 +145,7 @@ class ConfigScreenComponent extends React.Component { screenName = section.name; if (this.hasChanges()) { - const ok = confirm(_('This will open a new screen. Save your current changes?')); + const ok = await shim.showConfirmationDialog(_('This will open a new screen. Save your current changes?')); if (ok) { await shared.saveSettings(this); } diff --git a/packages/app-desktop/gui/EncryptionConfigScreen/EncryptionConfigScreen.tsx b/packages/app-desktop/gui/EncryptionConfigScreen/EncryptionConfigScreen.tsx index f045c7e1549..03b2c729ee5 100644 --- a/packages/app-desktop/gui/EncryptionConfigScreen/EncryptionConfigScreen.tsx +++ b/packages/app-desktop/gui/EncryptionConfigScreen/EncryptionConfigScreen.tsx @@ -3,7 +3,7 @@ import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService'; import { themeStyle } from '@joplin/lib/theme'; import { _ } from '@joplin/lib/locale'; import time from '@joplin/lib/time'; -import shim from '@joplin/lib/shim'; +import shim, { MessageBoxType } from '@joplin/lib/shim'; import dialogs from '../dialogs'; import { decryptedStatText, determineKeyPassword, dontReencryptData, enableEncryptionConfirmationMessages, onSavePasswordClick, onToggleEnabledClick, reencryptData, upgradeMasterKey, useInputPasswords, useNeedMasterPassword, usePasswordChecker, useStats, useToggleShowDisabledMasterKeys } from '@joplin/lib/components/EncryptionConfigScreen/utils'; import { MasterKeyEntity } from '@joplin/lib/services/e2ee/types'; @@ -47,7 +47,7 @@ const EncryptionConfigScreen = (props: Props) => { const onUpgradeMasterKey = useCallback(async (mk: MasterKeyEntity) => { const password = determineKeyPassword(mk.id, masterPasswordKeys, props.masterPassword, props.passwords); const result = await upgradeMasterKey(mk, password); - alert(result); + await shim.showMessageBox(result, { type: MessageBoxType.Info }); }, [props.passwords, masterPasswordKeys, props.masterPassword]); const renderNeedUpgradeSection = () => { diff --git a/packages/app-desktop/gui/MasterPasswordDialog/Dialog.tsx b/packages/app-desktop/gui/MasterPasswordDialog/Dialog.tsx index 95ac261840b..1573bbc2460 100644 --- a/packages/app-desktop/gui/MasterPasswordDialog/Dialog.tsx +++ b/packages/app-desktop/gui/MasterPasswordDialog/Dialog.tsx @@ -11,6 +11,7 @@ import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService'; import KvStore from '@joplin/lib/services/KvStore'; import ShareService from '@joplin/lib/services/share/ShareService'; import LabelledPasswordInput from '../PasswordInput/LabelledPasswordInput'; +import shim from '@joplin/lib/shim'; interface Props { themeId: number; @@ -80,7 +81,7 @@ export default function(props: Props) { void reg.waitForSyncFinishedThenSync(); onClose(); } catch (error) { - alert(error.message); + void shim.showErrorDialog(error.message); } finally { setUpdatingPassword(false); } diff --git a/packages/app-desktop/gui/NoteRevisionViewer.tsx b/packages/app-desktop/gui/NoteRevisionViewer.tsx index a58f8963bff..dbdde2c305e 100644 --- a/packages/app-desktop/gui/NoteRevisionViewer.tsx +++ b/packages/app-desktop/gui/NoteRevisionViewer.tsx @@ -17,6 +17,7 @@ const urlUtils = require('@joplin/lib/urlUtils'); const ReactTooltip = require('react-tooltip'); const { connect } = require('react-redux'); import shared from '@joplin/lib/components/shared/note-screen-shared'; +import shim, { MessageBoxType } from '@joplin/lib/shim'; interface Props { themeId: number; @@ -97,7 +98,7 @@ class NoteRevisionViewerComponent extends React.PureComponent { this.setState({ restoring: true }); await RevisionService.instance().importRevisionNote(this.state.note); this.setState({ restoring: false }); - alert(RevisionService.instance().restoreSuccessMessage(this.state.note)); + await shim.showMessageBox(RevisionService.instance().restoreSuccessMessage(this.state.note), { type: MessageBoxType.Info }); } private backButton_click() { diff --git a/packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.tsx b/packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.tsx index e3eb8b159d4..df76c33edce 100644 --- a/packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.tsx +++ b/packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.tsx @@ -18,6 +18,7 @@ import { connect } from 'react-redux'; import { reg } from '@joplin/lib/registry'; import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect'; import { ChangeEvent, Dropdown, DropdownOptions, DropdownVariant } from '../Dropdown/Dropdown'; +import shim from '@joplin/lib/shim'; const logger = Logger.create('ShareFolderDialog'); @@ -242,13 +243,13 @@ function ShareFolderDialog(props: Props) { } async function recipient_delete(event: RecipientDeleteEvent) { - if (!confirm(_('Delete this invitation? The recipient will no longer have access to this shared notebook.'))) return; + if (!await shim.showConfirmationDialog(_('Delete this invitation? The recipient will no longer have access to this shared notebook.'))) return; try { await ShareService.instance().deleteShareRecipient(event.shareUserId); } catch (error) { logger.error(error); - alert(_('The recipient could not be removed from the list. Please try again.\n\nThe error was: "%s"', error.message)); + await shim.showErrorDialog(_('The recipient could not be removed from the list. Please try again.\n\nThe error was: "%s"', error.message)); } await ShareService.instance().refreshShareUsers(share.id); @@ -290,7 +291,7 @@ function ShareFolderDialog(props: Props) { }); await ShareService.instance().setPermissions(share.id, shareUserId, permissionsFromString(value)); } catch (error) { - alert(`Could not set permissions: ${error.message}`); + void shim.showErrorDialog(`Could not set permissions: ${error.message}`); logger.error(error); } finally { setRecipientsBeingUpdated(prev => { @@ -383,7 +384,9 @@ function ShareFolderDialog(props: Props) { async function buttonRow_click(event: ClickEvent) { if (event.buttonName === 'unshare') { - if (!confirm(_('Unshare this notebook? The recipients will no longer have access to its content.'))) return; + if (!await shim.showConfirmationDialog(_('Unshare this notebook? The recipients will no longer have access to its content.'))) { + return; + } await ShareService.instance().unshareFolder(props.folderId); void synchronize(); } diff --git a/packages/app-desktop/gui/ShareNoteDialog.tsx b/packages/app-desktop/gui/ShareNoteDialog.tsx index e09427c5a19..7f9e4c7232e 100644 --- a/packages/app-desktop/gui/ShareNoteDialog.tsx +++ b/packages/app-desktop/gui/ShareNoteDialog.tsx @@ -16,6 +16,7 @@ import { connect } from 'react-redux'; import { AppState } from '../app.reducer'; import { getEncryptionEnabled } from '@joplin/lib/services/synchronizer/syncInfoUtils'; import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry'; +import shim from '@joplin/lib/shim'; const { clipboard } = require('electron'); interface Props { @@ -146,7 +147,7 @@ export function ShareNoteDialog(props: Props) { reg.logger().error('ShareNoteDialog: Cannot publish note:', error); setSharesState('idle'); - alert(JoplinServerApi.connectionErrorMessage(error)); + void shim.showErrorDialog(JoplinServerApi.connectionErrorMessage(error)); } break; diff --git a/packages/app-desktop/gui/Sidebar/hooks/useOnRenderItem.tsx b/packages/app-desktop/gui/Sidebar/hooks/useOnRenderItem.tsx index f9fac470e21..bbdb3b37650 100644 --- a/packages/app-desktop/gui/Sidebar/hooks/useOnRenderItem.tsx +++ b/packages/app-desktop/gui/Sidebar/hooks/useOnRenderItem.tsx @@ -31,6 +31,7 @@ import HeaderItem from '../listItemComponents/HeaderItem'; import AllNotesItem from '../listItemComponents/AllNotesItem'; import ListItemWrapper from '../listItemComponents/ListItemWrapper'; import { focus } from '@joplin/lib/utils/focusHandler'; +import shim from '@joplin/lib/shim'; const Menu = bridge().Menu; const MenuItem = bridge().MenuItem; @@ -309,7 +310,7 @@ const useOnRenderItem = (props: Props) => { } } catch (error) { logger.error(error); - alert(error.message); + await shim.showErrorDialog(error.message); } }, []); diff --git a/packages/app-desktop/gui/WindowCommandsAndDialogs/commands/leaveSharedFolder.ts b/packages/app-desktop/gui/WindowCommandsAndDialogs/commands/leaveSharedFolder.ts index 7b8ac1e2b5b..6d964b296cb 100644 --- a/packages/app-desktop/gui/WindowCommandsAndDialogs/commands/leaveSharedFolder.ts +++ b/packages/app-desktop/gui/WindowCommandsAndDialogs/commands/leaveSharedFolder.ts @@ -2,6 +2,7 @@ import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/ import { _ } from '@joplin/lib/locale'; import ShareService from '@joplin/lib/services/share/ShareService'; import Logger from '@joplin/utils/Logger'; +import shim from '@joplin/lib/shim'; const logger = Logger.create('leaveSharedFolder'); @@ -13,7 +14,7 @@ export const declaration: CommandDeclaration = { export const runtime = (): CommandRuntime => { return { execute: async (_context: CommandContext, folderId: string = null) => { - const answer = confirm(_('This will remove the notebook from your collection and you will no longer have access to its content. Do you wish to continue?')); + const answer = await shim.showConfirmationDialog(_('This will remove the notebook from your collection and you will no longer have access to its content. Do you wish to continue?')); if (!answer) return; try { @@ -28,7 +29,7 @@ export const runtime = (): CommandRuntime => { await ShareService.instance().leaveSharedFolder(folderId, share.user.id); } catch (error) { logger.error(error); - alert(_('Error: %s', error.message)); + await shim.showErrorDialog(_('Error: %s', error.message)); } }, enabledCondition: 'joplinServerConnected && folderIsShareRootAndNotOwnedByUser',